MASTG-TECH-0017: Decompiling Java Code
In Android app security testing, if the application is based solely on Java and doesn't have any native code (C/C++ code), the reverse engineering process is relatively easy and recovers (decompiles) almost all the source code. In those cases, black-box testing (with access to the compiled binary, but not the original source code) can get pretty close to white-box testing.
Nevertheless, if the code has been purposefully obfuscated (or some tool-breaking anti-decompilation tricks have been applied), the reverse engineering process may be very time-consuming and unproductive. This also applies to applications that contain native code. They can still be reverse engineered, but the process is not automated and requires knowledge of low-level details.
If you want to look directly into Java source code on a GUI, simply open your APK using jadx or Bytecode Viewer.
Android decompilers go one step further and attempt to convert Android bytecode back into Java source code, making it more human-readable. Fortunately, Java decompilers generally handle Android bytecode well. The above mentioned tools embed, and sometimes even combine, popular free decompilers such as:
Alternatively you can use the APKLab extension for Visual Studio Code or run apkx on your APK or use the exported files from the previous tools to open the reversed source code on your preferred IDE.
In the following example we'll be using Android UnCrackable L1. First, let's install the app on a device or emulator and run it to see what the crackme is about.
Seems like we're expected to find some kind of secret code!
We're looking for a secret string stored somewhere inside the app, so the next step is to look inside. First, unzip the APK file (unzip UnCrackable-Level1.apk -d UnCrackable-Level1
) and look at the content. In the standard setup, all the Java bytecode and app data is in the file classes.dex
in the app root directory (UnCrackable-Level1/
). This file conforms to the Dalvik Executable Format (DEX), an Android-specific way of packaging Java programs. Most Java decompilers take plain class files or JARs as input, so you need to convert the classes.dex file into a JAR first. You can do this with dex2jar
or enjarify
.
Once you have a JAR file, you can use any free decompiler to produce Java code. In this example, we'll use the CFR decompiler. CFR releases are available on the author's website. CFR was released under an MIT license, so you can use it freely even though its source code is not available.
The easiest way to run CFR is through apkx, which also packages dex2jar
and automates extraction, conversion, and decompilation. Run it on the APK and you should find the decompiled sources in the directory Uncrackable-Level1/src
. To view the sources, a simple text editor (preferably with syntax highlighting) is fine, but loading the code into a Java IDE makes navigation easier. Let's import the code into IntelliJ, which also provides on-device debugging functionality.
Open IntelliJ and select "Android" as the project type in the left tab of the "New Project" dialog. Enter "Uncrackable1" as the application name and "vantagepoint.sg" as the company name. This results in the package name "sg.vantagepoint.uncrackable1", which matches the original package name. Using a matching package name is important if you want to attach the debugger to the running app later on because IntelliJ uses the package name to identify the correct process.
In the next dialog, pick any API number; you don't actually want to compile the project, so the number doesn't matter. Click "next" and choose "Add no Activity", then click "finish".
Once you have created the project, expand the "1: Project" view on the left and navigate to the folder app/src/main/java
. Right-click and delete the default package "sg.vantagepoint.uncrackable1" created by IntelliJ.
Now, open the Uncrackable-Level1/src
directory in a file browser and drag the sg
directory into the now empty Java
folder in the IntelliJ project view (hold the "alt" key to copy the folder instead of moving it).
You'll end up with a structure that resembles the original Android Studio project from which the app was built.
See the section Reviewing Decompiled Java Code to learn on how to proceed when inspecting the decompiled Java code.