For this challenge, I'll be using Genymotion as my Android emulator. Before we start reversing, let's install the app and check out what it looks like.
It looks like there isn't much to interact with in the application; it just has a text view with "Hello Hackers" in the center of the screen.
Let's dig deeper. I'm going to use jadx to explore the inner workings of the app.
After opening the app in jadx, I immediately focused on the native functions at the top.
This indicates that the application is using JNI, which stands for Java Native Interface. JNI is a framework that enables Java code running in a Java Virtual Machine (JVM) to interact with native applications and libraries written in other languages like C or C++.
At the bottom, you can see that the native library is being loaded.
As you can see from the above snippet, the library frida0xa
is being loaded. Let's dig even deeper. Using apktool, let's decompile the application and put frida0xa.so
under Ghidra to see what's going on inside.
You can use the following command to decompile the application.
After decompiling the app, you can find the library under app-name/lib/
.
Now, let's open libfrida0xa.so in Ghidra.
After opening the library, we can see two interesting functions:
Java_com_ad2001_frida0xa_MainActivity_stringFromJNI
: This function is called on MainActivity
. On line 12, you can see the string "Hello Hackers" being set.get_flag
: This function is another interesting one.In the function decompile window, you can see that the flag is encrypted with some sort of operation. We could take the code, reverse the operation, and get the flag. However, this is not the intended solution for the challenge. Additionally, the function takes two parameters, and the flag is only decoded if a certain if
instruction evaluates to true.
We can simply pass
If you look closely at the function, you'll notice that an Android log function is being called after decrypting the flag.
Now that we know what we need to do, since the get_flag function is not being called, we need to call the function somehow to print the flag in the Android log. To do that, we can use Frida.
Frida is a dynamic instrumentation toolkit that allows developers, reverse engineers, and security researchers to inject JavaScript into running processes on Windows, macOS, Linux, iOS, and Android, enabling them to analyze and manipulate the behavior of applications and the operating system.
Frida is a powerful tool, and for this challenge, we are going to call the get_flag function using Frida.
Before solving the challenge using Frida, we need to set it up. You can check out their documentation on how to do that.
Let's see if Frida is working properly. By running the following command, we should see our target app starting and the Frida CLI.
Good! Let's now run the following command to get the get_flag
function name.
Great! We got the function name, which is _Z8get_flagii
. Now we can get the address of the function and use the NativeFunction
function to call it.
The final code looks something like this. Let's see what the code is doing:
It gets the address of the get_flag
function using Module.findExportByName
.
In this line, it's preparing a NativeFunction
based on the address of get_flag
. The second parameter is the return type of the function, which is void
, and the last parameter is the type of the parameters we need to pass, which are both int
as we've seen in Ghidra.
Lastly, we call the function.
Let's open logcat and see if calling the function spits out the flag for us.
Boom! As you can see, we get our flag.
For the final challenge, let's open the app on an emulator and see what it looks like.
The app looks simple enough, with a CLICK ME button in the center of the screen that does nothing when clicked. Let's use jadx to see what's going on.
In this challenge, the app is also using a native library. When the button is clicked, the getFlag function is being called, but nothing seems to be happening. Let's check the log to see if the flag is being output there.
No flag yet. Let's dig deeper. Like the previous challenge, let's decompile the app using apktool and load the library using ghidra.
Decompiling the getFlag
function shows nothing. It looks like it's doing nothing. Is it not? If you check out the comments left by ghidra, you can see there is some unreachable code. Let's check out the assembly code.
Okay, good. The assembly code tells us what the function is doing, and it explains why ghidra marked the function as unreachable.
After the function prologue…
You can see the following instruction: it's comparing 0xdeadbeef == 0x539
, which is always false. The rest of the code will not be executed. To understand the code more, let's enable the option in ghidra to show the unreachable code.
Unset Eliminate unreachable code
Now we can see the decompiled code a little clearer. If 0xdeadbeef == 0x539
equals true, we can see that the flag will be decrypted. Like the previous challenge, the flag will be logged.
Once again, we are going to use frida with radare2 or r2 for short. We are going to install a special plugin for radare2 called r2frida.
r2frida is a plugin for the radare2 reverse engineering framework that enables interaction with Frida, a dynamic instrumentation toolkit.
To install the plugin, we can use the following command:
To verify if the plugin is installed, use the following command:
To spawn the app, use the following command:
To list all the native libraries and filter for the challenge library, you can use the following command:
To seek to the base address of the library and search for the getFlag
function address, you can use the following commands in r2:
To seek to the address of getFlag
and view the instructions at that address, you can use the following command in r2:
From the above screenshot, we can see that we're at the right address.
The first thing we need to do is seek to the function we're patching, then press v
.
Set the instruction we're patching to the top of the line.
Then A
, type the instruction that we want to patch.
Now, save the changes and to verify the instruction is patched, let's use the pd
command to view the instruction.
Good, it's patched. Let's now open logcat and click on the button.
Awesome! As you can see, our patch worked, and when we clicked on the button, it logged the flag.
Let's use the following code to get the base address of the native lib. Since we've solved the challenge using r2frida, we can start our app using just Frida, attach to the process using r2frida, and check if the base address of the native lib is the same.
Now, let's attach to the process using r2frida and verify that we have the same address.
As we can see from the above screenshot, both are showing the same base address.
In the following code, we get the base address of libfrida0xb.so
, add the offset of the getFlag function, and finally, we add the exact offset, which is 0x1c
, where the 0xdeadbeef
value is being moved to the stack.
then in this location we're going to patch the instruction, changing the value from 0xdeadbeef
to 0x539
Let's run our code and verify using r2frida to check if the instruction is patched or not.
The code ran without any errors. Let's verify the patch.
The instruction has been patched. Let's click on the button and see what happens.
Awesome, it looks like the patch worked!
Challenge link: https://github.com/DERE-ad2001/Frida-Labs