Getting inside APK files
Disclaimer: Never try to reverse engineer apps, which are not developed by you. I’m not responsible for any damage you may cause to third-party developers using this tutorial, I insist that you should use this knowledge only to audit your own apps!
So, your Android app was pirated? You have Google in-app purchases, but someone published a full paid version for free on pirating websites? How is it even possible?
Let’s try to understand and try to decompile APK file. Here is the guide. Not for pirates or hackers, but for developers — so you will know better your app’s security weak sides.
In this tutorial, I will be using Mac OS X, but the tools I’m using are multi-platform — and you can install them also on Linux and even maybe Windows.
To start with, you will obviously need the APK file of the app you want to reverse engineer. As it is your own project, you can get it from
app\build\outputs\apk folder of your project, alternatively get it using ApkPure on your PC or from your device (you can back up any of your installed apps without root, for example, with ES File Explorer).
Installing the required tools
Next, let’s start with installing the required tools. You will need:
- apktool to unpack APK file to a smali project and pack it back.
- apk-signer to sign your final APK file
- IntelliJ IDEA and Smali Support Plugin (I haven’t tried to install it on Android Studio, but I think it should work)
Let’s start with apktool. Go to this webpage and follow the instructions. After that, you should be able to run
apktool command from your Terminal:
Next, let’s download
apk-signer. Go to this page, click “Download” button near APK-Signer. It’s just a simple .jar file with GUI, so you can open it as any usual app with double-click to run it in Mac OS X. You should see the following window:
For Mac OS X users you don’t have to specify JDK path.
And the last piece of our tools installation journey is smalidea plugin. Download it from here, just search on the page for
smalidea-*.*.zip and choose the latest version. To install it, open IntelliJ IDEA, go to Preferences->Plugins->Install plugin from disk… After that, you will have to restart IntelliJ IDEA.
Now we’re ready to begin.
Decompiling APK file
To decompile APK file, open Terminal and run following command:
apktool d path/to/your/app.apk
By default, after running that command, you should see a folder with the decompiled app in your Home folder.
If you open the decompiled app folder, you should see the structure similar to this:
I suggest you open IntelliJ IDEA, click File->Open… and choose the decompiled app folder in the following window. Now it’s easier to edit files there.
Let’s make changes
To start with, let’s try to understand the files structure. You can see AndroidManifest.xml file in the root folder and also you can edit it as is — because it is default Android manifest file.
res folder you can find app’s resources — strings, values, drawables.
The most interesting folder for us is
smali. In multidex app cases, you may see the
smali_classes2 folder — this is the location for classes from the second .dex file.
smali folder. Here you will see the basic structure of all packages included in the app. For example:
Of course, if you go down the tree, you will finally see files with class names. But they are not .java or .kt — they are all .smali. And it is kind of a way to interpret code inside .dex files in readable and editable format.
The syntax is loosely based on Jasmin’s/dedexer’s syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.)
Sometimes you will see one file with source file name and a lot of files with almost similar names:
These are internal classes. For example, each lambda has its own .smali file, as well as each internal class.
So, finally, let’s try to open any of these .smali files!
The folder structure should be similar to your source code structure (of course, if the app you decompiled is not obfuscated), so you can easily find any classes you may be looking for. Also, you can search in IntelliJ IDEA, using double-shift.
You can notice that .smali files inside are really different from usual source code files. For example, here is a class declaration:
But don’t worry — it’s easy to read and understand .smali files. In this case, we have an interface file, with name Event. It implements parcelable, and its parent class is Object.
Also, you can see declared static fields, and of course, edit them:
As mentioned previously, long and double primitives (J and D respectively) are 64 bit values, and require 2 registers.
So, basically, J means that it is a 64-bit value, and we can edit it, but keep in mind, that this is a 16-bit literal.
And here is one of method/function declarations:
In this case, the method name is
isUserSubscribed, it has parameter with name
p2 with Java type List, and it is a list of Purchases, which is, by the way, annotated as Nullable.
Let’s imagine, that the method has serious and reliable logic inside, a lot of conditions and iterations, method calls to check if the end-user really bought the premium subscription, but finally it ends with this return code:
As you can see, in one if-branch (cond_0), using line
const/4 p1, 0x1 we set “true” value to p1, and in other if-branch (cond_1), using line
const/4 p1, 0x0 we set “false” to p1 and in both cases return p1. Basically, changing 0x0 in the last line — and they can change it using IntelliJ IDEA or any other text editor, — hackers can make this method always to return “true” and break all premium subscription logic.
Of course, this is the most obvious case — to try to pirate the app, to disable some purchase checks — most primitive and simple one. But using the same tools, detractors can do a lot of even worse things with your app, so be careful.
Build APK from smali
So, we’ve changed some code here, let’s try to build and run our modified APK. It is simple — just run:
apktool b path/to/your/decompiled/folder
After a few seconds, there will be new APK file inside
dist folder which is inside your decompiled app folder.
But if you will try to install it on your device, you will get an error saying “Application not installed”. It’s because your APK file is not signed at all. Of course, a hacker wouldn’t be able to sign it using the original keystore — of course, if you keep your keystore and passwords in a safe place. So in order to run it, we will have to sign it with a new keystore, or we can use an existing one. To create one, open
apk-signer.jar, go to “Key Generator” tab, fill in all required info and click “Generate Keyfile”. Now you can use this keystore to sign APK files.
In Apk Signer tool, go to “Signer” tab.
Select the keystore you generated in the previous step, fill in passwords and alias names, choose the APK file you want to sign, and click the “Sign!” button.
You will find final installable APK file in the same place where unsigned one was located — in the
dist folder. Try to run it and if you’ve done everything correctly, you should see the changes in the app.
What if I use React Native? Am I safe?
Not really. If your app is written on React Native, using the same apktool you can find
index.android.bundle file inside
assets folder of decompiled APK folder.
Just use js-beautify — to install it use:
pip install jsbeautifier
and run it using:
js-beautify -o out.js in.js
index.android.bundle file with beautified one, because we have it only to understand source code — that’s why after finding required lines in the beautified file, replace what you want inside the minified source
index.android.bundle file, and rebuild APK.
How to be safe
If you’re suffering from piracy and don’t know how intruders modified your APK, you can try to think as they do and reproduce these steps with your APK, and later use that knowledge.
How to be safe? One of the possible solutions is to make all checks on the back-end side, but in some cases, it is not possible. But for really sensitive operations, somehow linked to your or your users’ money you should do back-end side checks anyway. Because front-end is almost always reverse engineerable. Use front-end only to validate data, obtained from the back-end.
Also, remember to use ProGuard and don’t forget to turn it on for release builds — it can help in some cases.