- Published on
- Juan Urbano Stordeur
TL;DR: In this second post, we will show you the resolution of the challenge using two tools. The first is focused on the “Jadx interpretation” and the second is focused on the “Apktool interpretation” (_You can see Part I here).
In the previous article, we named certain tools that can be used to decompile an APK and obtain the source code of the applications, like apktool, jadx, cfr, etc.
Here we will explain how we can make use of some of those tools to obtain a really good approximation of the application code. For example, using apktool, we can decode resources to nearly original form. We can even rebuild them after making some modifications. With jadx or cfr (both java decompilers), we can analyze the java code obtained after the decompilation process. This practice allows us to look at the code in a more natural way, since the output from the java decompilers are .java files whereas the output from apktool are .smali code files.
We will not delve into Java decompilers in this post, because they are out of scope; we will simply use them to analyze the code for the application in the challenge. Then, we will modify the application from the .smali code. We will show how to use apktool to obtain a good approximation of the code, to be able to modify it as we need to and then rebuild it.
With this in mind, we will take a look at which is the process to create an APK file, since it will be useful to start trying to solve the challenge.
The process of creating an APK file
- First, the developer creates its application in .java to then be compiled into .class files.
- Once these .class files are created, they are converted into .dex (Dalvik EXecutables) files. These files contain byte code for the Dalvik Virtual Machine (DVM) which is a non-standar JVM that runs on Android devices.
- The DVM runs the DEX files while ART runs OAT (ELF) files.
- Some other XML files are converted to a binary format optimized for space.
- The last step is the APK creation from the .dex files, binary XML files and other resources needed to run the application and are packaged into an Android Package file (.apk).
- After the APK file is signed by the developer (we will come back to this in the Manual patching with apktool section), the APK is ready to be installed.
- If we want to look at the APK file, we can check its content by unpacking it, for example: $unzip -e example.apk -d example_folder.
Briefly, the APK file is just a signed zip file that we can unzip using the unzip command:
If we take a look at the manifest, we notice that the resources are encoded, and we can use apktool to decode them later.
Jadx challenge interpretation
If we install the application in the emulator and run it, we will see something similar to the screenshot below. If we write some alphanumeric input, a warning stating This Device is not supported will appear. Since we do not know why this happens, we can use jadx to obtain the .java code and use it as a starting point to determine the reason.
Of course, we can also use apktool or unzip the APK file to know more about the application, and maybe obtain some other kind of information. In this approach, we will focus on the .java code and try to understand the application workflow.
To decompile the APK, using jadx is enough for this challenge, although there are lots of Java decompilers out there that we could also use.
We can see some errors and warnings in the images above, but for the purpose of this post they are not important. Once the decompilation process has finished, the tool should have created a folder with all the decompiled files, which look like this:
If we look for the text with the warning we saw earlier, we will find a toast, which is a view containing a quick little message for the user. The toast class helps you create and manage them. We can also note that the message is shown depending on the value returned by ChallengeJNI.this.checkIfDeviceIsEmulator().booleanValue().
What do you think about this line?
Let’s take a look at the implementation of the checkIfDeviceIsEmulator() function:
Basically, what it is doing is checking some strings against a set of predefined strings, like we saw in the Anti-Emulation Checks before. Now we will try to bypass them.
Apktool challenge interpretation
Like we already saw, we need to modify the checkIfDeviceIsEmulator() function in order to bypass the application’s validation, so now we are going to use apktool to do that.
Apktool patching and reversing engineering
After we have installed apktool, we can check the options of the tool. For the time being, we will focus on the decode (‘d’) and build (‘b’) options. Apktool needs an input .apk, which is in this case the one from the challenge we are trying to solve.
To decode the application execute the following command:
We can see the internal structure of the decoded APK, the AndroidManifest.xml file and the different folders, like the smali code. It is important to remember the normal APK structure.
- smali — disassembled java code
- res — resources, strings
- assets — files bundled inside the APK
- lib — native libraries (*.so files)
- AndroidManifest.xml — decoded version
- original and apktool.yml — used by apktool
After decoding the app, we can see the AndroidManifest.xml.
If we look inside the Smali folder we can see all the smali files.
As we can see, working with smali code is harder than with java, so we will move to java decompilers to analyze and interpret the application code. And after that, we will modify the application to obtain the bypass’ smali code and re-build the application. To do that, we will make use of some dalvik opcodes.
Understanding dalvik opcodes
This link is really useful, I used it to create a table showing some of the most interesting examples from the “dalvik opcodes” used by the application.
Something that we will see very often in the code is a line like this:
“.method private checkIfDeviceIsEmulator ()Ljava/lang/Boolean;”
It is important to understand the meaning of this line, so let’s break it down:
- “.method private” -> is the type of method.
- checkIfDeviceIsEmulator -> the method name.
- ()Ljava/lang/Boolean; -> the type of the return value, prefixed with L, dots “.” replaced with slashes “/” and suffixed with semicolon ;
That is all! In Part (III) we will solve the challenge using what we have learned.
Don’t forget to follow us!