Android debug mode and apps. A cautionary tale
In my downtime I’ve been looking at a tablet designed for kids. No names just yet, as I’ve yet to finish my work on it and don’t want to get carried away.
This is a post about a built-in app I found on the device, which had a flag that I’ve never actually seen in the wild before: the debug flag. I’ll also qualify at this point that the version of the app on Google Play doesn’t have the debug app set.
The debug flag is designed to help developers debug their apps, by connecting to the Java debugger, which allows them to retrieve data, dump variables etc. So what can we do if an app is in debug mode and how do we find this out.
How to find out whether debug mode is active
The easiest way to find this out is from the Android Debug Bridge (adb) shell. This can be enabled in developer options on the device (as USB Debugging) and can be accessed using the adb program in the Android SDK.
Never do this on devices that are being used for personal data as even the Android shell can leak information.
Now we have adb enabled, we can enter a shell with the “adb shell” command and we can see that Android just runs a Unixlike environment under the cover (in fact it’s a highly customised version of Linux).
There are two ways that debug mode can be set: on the device, or on an app. Seeing whether a device is set to debug mode is easy, we can look to see whether it has the ro.debuggable property set, using the getprop command:
shell@android:/ $ getprop ro.debuggable
The 0 there means that the OS isn’t debuggable.
Next we can see if the apps are debuggable, this is stored in the package manager’s simple text list file, /data/system/packages.list and is signified by the third field being set to a “1” (this file is more restricted on later versions of Android):
com.halfbrick.fruitninjahd 10055 0
com.toongoggles.b 10047 1 /data/data/com.toongoggles.b
com.android.providers.applications 10002 0
com.android.providers.drm 10018 0
As we can see, com.toongoggles.b; which is a simple app to allow the user to download and watch cartoons online; is marked as being debuggable.
So What Does This Do?
Before I go any further I should note that I have not rooted the device yet: all actions are being performed by a normal user as if they have access to the device. This means that I don’t have access to the application’s sandbox.
Basically debug mode means that the app makes a connection to the the JDWP service – the Java Debug Wire Protocol. This allows a set of commands for debugging the app; allowing such lovely functions as performing commands as the app’s user and dumping its heap memory.
So let’s look at the first: performing commands as the app’s user. We can only do this from the ADB shell. We can use the built-in command run-as to run a shell command as if we were the app’s user. The main advantage to this is that it will allow us to view files in the app’s sandbox and even files in the app’s keystore.
By default we can browse to an app’s keystore (as the shell user), but cannot see any files:
shell@android:/data/data/com.toongoggles.b $ ls
opendir failed, Permission denied
So let’s try this with the run-as command:
com.toongoggles.b ls /data/data/com.toongoggles.b
Neat, so most configuration files are stored in shared_prefs; is there anything juicy in there?
com.toongoggles.b ls /data/data/com.toongoggles.b/shared_prefs
shell@android:/data/data/com.toongoggles.b $ run-as
<?xml version=’1.0′ encoding=’utf-8′ standalone=’yes’ ?>
Ouch! I think that’s successful!
Looking at the app
So, we’ve broken the sandbox, what else can we do. If you remember from above, the debug flag connects the app to the Java debugger, so what happens if we talk to the other end?
Fortunately there’s a program in the SDK that will allow us to talk to the Java debugger, called monitor, and here it is:
I’ve set this up to heap monitoring mode – what this does is allow us to monitor the app’s heap – the memory that is allocated to the app to store its objects and variables in. The view there shows us how much is being used and of what types, but we can go further…
If we press this button it will dump the heap into a hprof (heap profile) file, which is a raw dump of the heap memory with some meta data to allow us to see what memory is allocated to which variable. In simple terms it is a dump of all the app’s variables.
So, we press the button and save the hprof file. Unfortunately the file doesn’t follow exactly the same format as the standard Java hprof file, so before we can analyse it we have to convert it to a standard Java hprof file, using the hprof-conv tool that is conveniently provided in the SDK:
(The –z flag is to tell it to skip anything from the zygote process – basically system stuff that we don’t need.)
Now we have the converted hprof we can load this into the standard memory analysis tools, for example the Eclipse based Memory Analyzer [sic] Tool or MAT:
A bit of hunting around and we can dump the memory of the object that handles the userdetails, com.tg.controllres.account.AccountLoginViewController:
This is a very quick summary of some of the things that can be found if an application has been left in “debug” mode. There is much more, but hopefully this is a taster of why it’s bad.
So in essence:
- Ensure that your app is not in debug mode once it goes into production.
- Be wary of default apps that exist on devices, especially on the cheaper end of the market.