Android Security Part 1 - Reverse Engineering Android Apps

Google introduced automatic support for ProGuard, a code obfuscation tool, in ADT as early as December 2010 in version 8.0.0. It is integrated into the build system to make code obfuscation a pretty “straightforward” process. Since then, Google has encouraged—albeit not proactively—developers to enable ProGuard to prevent hackers from reverse engineering and hacking applications. Despite enabling ProGuard, however, a very persistent hacker may still be able to reverse engineer a ProGuard-ed application but the task becomes exponentially harder. There are other good practices developers should abide by to ensure a secure app, but code obfuscation should always be the first line of defense (more information on code obfuscation on Android here and here). Out of curiosity, I went ahead and did some reverse engineering on some popular apps (all have more than 1 million downloads on Google Play) that may contain user sensitive information and un/surprisingly, 10 out of 12 weren’t doing some sort of code obfuscation. I’m sharing in this post what and how I did that to point out 2 things: (1) how easy it is to reverse engineer an app, and (2) to increase awareness of the importance of security specifically code obfuscation.


Methodology


My methodology was simple, in fact, a quick Google Search on ”Android Hacking” should give anyone the tools to do this. Below are the steps:

  1. Obtain the .apk files of the applications of interest by using Astro File Manager. Simply navigate to “tools” in the options menu, “Application Backup”, and then select all the apps you’re interested in. Once backup is perform, an image is obtained of the apps and the corresponding .apk files will be stored under your SD card’s backups/ directory.
  2. Unpack .apk file using unzip.
  3. Disassemble compiled classes.dex file using baksmali. This will generate tons of .smali files.
  4. Perform static analysis on .smali files.

For step 4, if an application does not have code obfuscation, then all static constant declarations can be read in plain text (red flag!). You can still take things a step further, for instance, by writing smali code and retracing your steps to create your own custom hacked-out app.

Results

Understanding smali code is pretty straightforward event without prior knowledge of the language especially when it comes to finding static member fields. Declared String values are human-readable. Here are a few screen-shots of what I’ve found:
Fig. 1 Fig. 2 Fig. 3 Fig. 4

Conclusion

ProGuard creates drastic improvements in security. Here’s an example of how smali code would look like with ProGuard on vs. with ProGuard off. Say your Activity has a static member field HACK_THIS:
Fig. 5

When we disassemble the resulting .dex file without ProGuard enabled, we get this:

Fig. 6

Whereas code obfuscation using ProGuard results in:



From this comparison, you can observe the following: enabling ProGuard in this situation removes the human-readable static member field whereas disabling it leaves it in plain-text in smali assembly code. Although the content of the String gets copied wherever it is used in code as seen on line 39 in Figure 7, the context of what that String represents is virtually unknown. Again, a persistent hacker may deduce what that means through brute-force, but ProGuard increases the complexity of the task. ##### Any tips on how to create a secure Android app? Leave a comment below!

Dynamic Data with ListView + Loading Footer

A pretty common UI design pattern for  ListViews on Android is displaying a loading footer for the dynamic loading of a list. It’s a pattern used by several well known apps - Twitter, Gmail and Instagram to mention a few - to display data without having to click a button to paginate through information. Essentially, as a user scrolls down to the end of a list, which indicates the end of the current page, a loading indicator is displayed at the footer of the list to notify the user that the list is in the process of populating more data.


While playing around with this pattern, I was quite surprise how not-so-straightforward it was to implement this (then again, that’s programming for you). With that said, I’d like to share my implementation with you in case you’re trying to use this pattern in your application.

Trial 1

The intuitive way to go about this is to (1) simply attach an Adapter with the first page of items to a ListView, (2) attach an OnScrollListener to detect if the bottom of the list has been reached, if so, (3) add a loading footer view to the list while retrieving more data, and when the retrieval process is done, remove the loading footer and (4) update the adapter with the recently pulled data. Sounds pretty straightforward right? Turns out, it’s not. Here’s some code snippets of the above approach. (1) & (2)
ListView list = (ListView) findViewById(R.id.listview);
MyAdapter adapter = new MyAdapter(context, items);
list.setAdapter(adapter);

list.setOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, 
            int scrollState) {
         // Do nothing
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, 
            int visibleItemCount, int totalItemCount) {

        // threshold being indicator if bottom of list is hit
        if (firstVisibleItem = threshold) {
            pullMoreData();
        }
    }
});
(3) & (4)
private void pullMoreData() {
    doNetworkRequest(page); // Perform request for next page
    list.addFooterView(loadingFooter);
}

@Override
public void onNetworkRequestComplete(Request request) {
    list.removeFooterView(loadingFooter);
    adapter.addAll(request.getData());
    adapter.notifyDataSetChanged();
}
This implementation, however, does not result in the intended action - the footer never gets displayed. A work-around I did for this leads me to…

Trial 2

With my second trial, I did this: (1) attached the footer first before the adapter, and when the bottom of the list has been reached and new data has been retrieved, (2) reattach the footer and create a new adapter with the old+new data which is then reattached to the list. Finally, to bring the user back to the scroll position, I (3) keep track of the first visible item on the scroll view and set the list selection to be this item. Immediately, a few things must be popping up in your head such as: that must be slow! it’s a hack! there has to be a cleaner way! etc. I can’t agree with you more. (1), (2) & (3)
ListView list = (ListView) findViewById(R.id.listview);
list.addFooterView(loadingFooter);

MyAdapter adapter = new MyAdapter(context, items);
list.setAdapter(adapter);

list.setOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, 
            int scrollState) {
         // Do nothing
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, 
            int visibleItemCount, int totalItemCount) {

 // member variable for restoring selection
        mSelection = firstVisibleItem;

 // threshold being indicator if bottom of list is hit
        if (firstVisibleItem = threshold) {
            pullMoreData();
        }
    }
});

@Override
public void onNetworkRequestComplete(Request request) {
    list.removeFooterView(loadingFooter);
    list.addFooterView(loadingFooter);

    MyAdapter newAdapter = new MyAdapter(this);
    newAdapter.addOldData(adapter);
    newAdapter.addAll(request.getData());

    list.setAdapter(newAdapter);
    adapter = newAdapter;

 // Set table to last selection
    list.setSelection(mSelection)
}
Albeit some performance issues and jerkiness because of (3), this implementation actually works. Can we do better than this?

Trial 3

The trick, it turns out, is to attach the footer view before setting the adapter, this way, any combination of adding and removing of the footer view/s just works. Why does it have to be in order!? If you have the answer, please leave a comment. I’d love to know
ListView list = (ListView) findViewById(R.id.listview);
list.addFooterView(loadingFooter);

MyAdapter adapter = new MyAdapter(context, items);
list.setAdapter(adapter);

// this step is important to not display the footer view // right of the bat.
list.removeFooterView(loadingFooter);

list.setOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, 
            int scrollState) {
 // Do nothing
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, 
            int visibleItemCount, int totalItemCount) {
 // threshold being indicator if bottom of list is hit
        if (firstVisibleItem = threshold) {
            pullMoreData();
        }
    }
});
I’ll share more Android quirks as they come up, I hope this helped.