Reverse engineering an Android app
INTRODUCTION
In this post we’ll see how to reverse engineer Android app’s to inspect and document their communication with a remote server via HTTP calls. Most of the security on mobile devices is targeted towards protecting the user from the app but not on the other direction, which makes it great and easy for us.
The main objective here is to (very) briefly demonstrate the steps and tools to get “juicy info”, this info can be used to:
- Make an app more secure/pentesting
- Break into an app
- Identify malware / malicious apps
- Create a private server for a game
- Identify ways to use mobile only API’s on bot’s
- Etc.
Keep in mind I am not trying to directly exploit an app so I will just stop at the moment where we start seeing the goodies, it’s up to the reader to keep pulling that thread.
OUR TOOLS
It will seem like we need a lot of tools but its just because we’ll try several methods:
- A rooted phone (search for magisk/mkt easy su/ twrp/“how to root android phone”) OR the android emulator (download android studio for this)
- ADB — This will allow us to pull and push applications to the device — https://dl.google.com/android/repository/platform-tools-latest-linux.zip
- APKTool — this will help us modify the APK to add a custom certificate autority — https://ibotpeaches.github.io/Apktool/install/
- HTTPToolkit — A simple tool to inspect traffic, with android it will take care of adding certificates and all the boring stuff for us— https://httptoolkit.com/
- BurpSuite — Another monitoring proxy like HTTPToolkit but with more features (this is a massive oversimplification of what burp does) — https://portswigger.net/
- Frida — A very complete tool for reverse engineering at a lower level — https://frida.re/ (get frida-tools for your computer and frida-server for the device)
- Cyberchef — Not really used in the guide but useful to figure out what the info we extracted is — https://gchq.github.io/CyberChef/
- Some random apps (CoinMaster, Amazon, Nike, Farfetch) to experiment with.
- An interest in breaking things apart and messing around with stuff.
THE APPROACHES
- The all-in-one: we will do this one with HTTPToolkit only.
- The App repackaging: we’ll decompile the app, change it up a bit then repack and reinstall, we’ll use Burp to monitor traffic and apktool to unpack/pack the app.
- The Java hooks: we’ll plug into the JVM and listen to function calls with Frida.
HTTPToolkit
This is gonna be the easiest method of them all, its simple to run and simple to set up; assuming you already installed HTTPToolkit on your pc and you have your device (physical or virtual) visible by ADB, open up HTTPToolkit and select “Android device via ADB”:
This will take a few seconds to work as its gonna install the counterpart on the device, it will ask you to set up a VPN connection, this is fine as its how it manages to route all traffic via a single point as well as giving it permission to intercept it, accept that prompt on the device.
If you get a warning that “system trust is disabled”, make sure you are running it as root.
Once that everything is running, on your PC you should see something like this:
I will now open a game called “CoinMaster” (randomly picked a game from the top chart), and as it starts it show up the domain is actually using as its API, some headers that say “unity” (the game engine) and some API keys:
Since the app will have a lot of tracking like newrelic, facebook, onesignal and others, i will use the bottom filter to limit the scope to only vik-game.moonactive.net as my host.
One of the requests (/store) reveals that the game is using libcurl to do the requests, the webserver is express, the user agent and a base64 encoded JWT token (https://github.com/ticarpi/jwt_tool/wiki), this could be used to try to impersonate another user and perform some actions on their behalf, knowing that libcurl is used is some juicy information that we could exploit later with Frida.
Another of the requests (/fullData), has as the response the amount of coins available, we could maybe modify the amount of coins to trick the game client into thinking we have more than we should, another thing we could do here is an inventory of all endpoint and the structure of the responses and write ourselves a private game server with all micro transactions removed :), you may experiment more here or continue with the next method.
App repackaging
This one involves unpacking the app, decompiling the java classes, doing any change that we need and then packing it up again and installing this modified version, we can also make a stop to see what the code/configuration files do before repackaing it.
Let’s start by creating a certificate to sign our applications, this is a one time thing, we can reuse this cert for all apps:
keytool -genkey -v -keystore test.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
For this approach I’ll use the Amazon app, once we have it installed, using ADB i’m gonna list all the packages and filter one that has amazon in the name.
adb shell pm list packages | grep amazon
with that package name in mind we request the path of the app:
adb shell pm path com.amazon.mShop.android.shopping
And we finish by pulling the APK to our computer:
adb pull "/data/app/com.amazon.mShop.android.shopping-ekzcShc8TuR6OFaMYTQCLA==/base.apk"
Now that we have the .apk, i’ll rename it to amazon.apk then disassemble it:
mv base.apk amazon.apk
apktool d amazon.apk
This will create several folders/files but all of them inside an “amazon” folder, this is created based on the name of the APK; For now we’re just interested in 2 files:
amazon/res/xml/network_security_config.xml
Open up that one in any text editor (or create it if it doesn’t exist) and then adjust the contents to have at least these nodes (it might have others as on the previous image, but these are the required ones for all apps:
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
The key node is the one that says src=user, this will enable us to use user provided certificates and let our proxy capture traffic.
The second important file is amazon/AndroidManifest.xml, and look for the <application> tag, it should have the following attribute, if not, make sure to add it, so the application can read the previous file we just configured:
android:networkSecurityConfig="@xml/network_security_config"
In this example it already has it, so I don’t change anything there:
Now we reassemble the APK:
apktool b amazon
This will create a file: amazon/dist/amazon.apk, this is the patched one that we will be sending back to the device but first lets sign it with the certificate we created previously:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore test.keystore amazon/dist/amazon.apk alias_name
Then we move over to installing the new app on our device, first we’ll uninstall the previous version, while not officially needed as a new install would count as an update, I’d rather start fresh, first we disable ADB verified installs:
adb shell settings put global verifier_verify_adb_installs 0
And now push the modified APK to our device:
adb install amazon/dist/amazon.apk
Now open up burp, go into: settings>Proxy>Proxy listeners>edit and change to “all interfaces” then hit “OK”:
Now click on “import/export certificate”, select “certificate in DER format” and keep going until you save the file on disk then convert it to PEM by running:
openssl x509 -inform DER -in cacert.der -out cacert.pem
move that file into your device and install it as a certificate, this changes from phone to phone but its mostly going to settings, searching for certificate and that should lead you to the proper option; then change the network settings (WIFI) to use a proxy and point that to burp’s IP/port, make you disable the firewall/open up the port to burp on your PC.
Once that is set up, go back to burp > proxy > intercept, and disable “incercept”:
Head over to the “HTTP History” tab now, immediately burp should start receiving all the telemetry from the phone:
Fire up the Amazon app and we can see the traffic and how amazon uses a custom user agent and a X-requested-with that hints to the mobile app:
If we did the same process but with the CoinMaster game, we could make use of the sequencer to quickly send requests without having to wait for the game client animations/limits to run and the server would register all the commands, then we just log in on the device, this would be handy to create a farming bot or something that automates actions that would be annoying as a human.
Before we continue to the next approach, one cool thing we can do since we unpacked the app is to look for juicy info in the data files, continuing with the amazon app as an example, if i look for “key” in the decompressed files:
I get those Google API keys at the bottom, maybe they could be used to extract some other information or use an API that we’re not intended to.
Another use for this, on the CoinMaster game is that, if we for any weird reason were to build the private server, since we know API requests go to vik-game.moonactive.net, if we search for that string in the game files, we get these two matches:
And upon inspection:
using a hex editor these could be used to change the game’s server address to something like private.mycoingame.net instead of going to the official game server.
Or we could look for a “dev/debug” mode flag and flip that on, to see what we can get from it, some games have a debug console for when they are on development mode.
You can either get creative with this now or lets move forward to our next and final approach.
JAVA Hooks
This is gonna be the most “low level” approach of the three, in this one we’ll add a listener to the Java functions and monitor the traffic there, instead of sending it to a nicer UI as with HTTPToolkit or BurpSuite it will be sent to the CLI.
I start by getting frida-server for android from the github page and then pushing it to the phone like this and elevating to root:
adb push frida-server-16.0.10-android-arm64 /data/local/tmp
adb shell "chmod 755 /data/local/tmp/frida-server-16.0.10-android-arm64"
su
then just run the server setting a custom port:
Now we can list all the apps running on our device:
I’ll be using the Nike app this time, and what I am gonna be doing here is to use an already built listener that hooks into okHTTP calls (common library to do HTTP requests) and logs them on screen, to do this I run the following:
frida -H 10.42.0.203:12345 --codeshare ninjadiary/okhttp -f com.nike.omega
Codeshare is a free database of scripts we can use, and then i tell it the binary to hook up to, you can find more scripts here: https://codeshare.frida.re/.
This will be the first view of that command:
And once the app loads on the device we’ll see the URL/method and headers that are running:
There is some interesting info at first, like the user agent and headers, this can be valuable for crawlers or shoebots.
After peeking more into the app I notice this URL in the list: https://bot-mitigation.nike.com/bot/bravo/v1/nikeapp/android/configuration
“bot mitigation” sounds like something valuable, but if we open that up it doesn’t have anything obvious that can be used, so lets save that for later
After messing up a bit more, another URL pops up:
this one has some secret key, maybe that can be used as well for something.
If we move to another app: Farfetch, we can get some of the auth tokens for castle.io (fraud prevention)
Another code snippet we could use is https://codeshare.frida.re/@0xbad0c0d3/okhttp-proxy-installator/
it allows us to set up a proxy only for a given app and not everything.
And then there are some that detect calls to the crypto methods which could give us encryption keys or the plain text version of some data.
CONCLUSION
So, now you have a starting point on how to monitor and log the HTTP requests an Android App is doing and a way to figure out how they work internally.
Hopefully you find this small intro useful and let me know if you have questions around the process.
P.S. - You may want to replace some of the tools with their alternatives if you feel more comfortable (mitmproxy or charlesproxy instead of Burp) or you might want to look into some others like dex2jar or ghidra.