FRIDA 101

Z4ki
6 min readJan 6, 2024

--

Hey there, today I will talk about FRIDA with practical examples on how to use it. So let’s begin

What is FRIDA?

Frida is a free open-source tool to analyze, manipulate, and modify an application running in memory. We can use it to see the output of a function, manipulate its parameters, or even insert new code in the application to help with our testing. All without needing to decompile the application and compile it again.

Today I will explain some of the basic functions FRIDA can do some CTF-like challenges.

FRIDA setup

First, we need to download Frida’s server and client, the client can be downloaded with a simple command

pip3 install frida-tools

for the server, we need to go to Frida’s repo and download the server that is compatible with our phone or emulator. I use Android Studio’s emulator with 86x architecture so that’s what I downloaded.

The server will be put in the /data/local/tmp directory and run the binary. Make sure that the client and the server are the same version so that we don’t get a version mismatch error.

Challenge 0x1: Frida setup, Hooking a method

This is the only activity in the application, so let’s see its decompiled code

We can see in the highlighted area that when we enter a number and click the button, it takes our number and passes it to the check function with another random number, so let’s see the check and get_random() functions:

the get_random function is simple, and returns a random number from 0 to 99; while the check function takes our input and makes sure that the first parameter * 2 + 4 equals the second parameter. So all we need to do is pass 2 numbers to the function that evaluates the condition to true, for example, 1,5. How would we do that with Frida? let’s see the code and break it down:

Java.perform(function() {
var maicActivity = Java.use("com.ad2001.frida0x1.MainActivity");
maicActivity.check.implementation = function(i, i2) {
this.check(1, 6);
}
});

Java.perform() is where we tell Frida to hook itself to the JVM, and it takes a function as a parameter, in this function we write our code.

with Java.use() we get an instance from the MainActivity class to be able to access its functions like the check function, its parameter is the absolute path to the class i.e. package_name.class_name

last line, we override the code for the check function and add our code and whenever the function is called our code will be executed not the original one. In our case, we don’t want to change the code because it has our flag, instead, we need to control the 2 arguments given to the function rather than the original ones. So, we will call the original function but with arguments that satisfy the condition 1 and 5 and click the button with any random numbers, and we get the flag.

$ frida -U -f com.ad2001.frida0x1 -l hook.js 
# -U connects to USB connected device
# -f package_name-> starts the application with the package_name
# -l provide the hook script from file

Challenge 0x4: Creating a class instance

In this challenge, we need to create a class instance and call a method not originally called within the application code. We can do that without needing to decompile the application, insert new code, and compile and sign it back again. With Frida life is easy :D

As you can see, there’s nothing in the UI so let’s see the decompiled source code:

Nothing in the MainActivity code as well, but there’s an unused class called Check let’s see what’s inside of it

Interesting, that’s the flag function we need to call, so how would we call a function that is not called? pretty easy let’s see the code that does the magic

Java.perform(function() {
var check = Java.use("com.ad2001.frida0x4.Check");
var check_instance = check.$new();
console.log(check_instance.get_flag(1337));
});

The first 2 lines we already discussed before so let’s understand the third line. The check variable here is like a pointer that points to the location of the Checkclass in memory, so before we could only call or manipulate static methods and variables without the need for a class instance, now it is a bit different because we have to have an instance to call a class method and we don’t have that instance in memory. That’s what the third line is doing, creating a new instance of the check class so we are able to execute methods and access its variables. Now all we need to do is get the output, we can connect our code to the layout of the application but that’s a topic for another time, we can log the output on our Frida terminal for the time being.

Challenge 0x5: Invoking methods on an existing instance

This is the challenge, like the last challenge the UI doesn’t provide us with anything, so we need to look at the source code

There’s the flag function, notice it is not static so it needs to be called with a class instance. The first approach we might try is to create an object of the MainActivity class and like the last challenge call the function, so let’s try that and see

Java.perform(function() {
var mainAct = Java.use("com.ad2001.frida0x5.MainActivity");
var main_instance = mainAct.$new();
console.log(main_instance.flag());
});

let’s run the code and see,

That’s a huge error, so what went wrong?

Creating an instance of MainActivity or any Android component directly using Frida can be tricky due to Android's lifecycle and threading rules. Android components, like Activity subclasses, rely on the application context for proper functioning. In Frida, you might lack the necessary context. Android UI components often require a specific thread with an associated Looper. If you're dealing with UI tasks, ensure you're on the main thread with an active Looper. Activities are part of the larger Android application lifecycle. Creating an instance of MainActivity might need the app to be in a specific state, and managing the entire lifecycle through Frida might not be straightforward. In conclusion, it's not a good idea to create an instance of the MainActivity.

So what should we do? We can just use Frida to get the already-created instance of MainActivity then call the flag() method to get our flag. Let’s learn how to do that

Java.perform(function() {
Java.choose('com.ad2001.frida0x5.MainActivity', {
onMatch: function(instance) {console.log(instance.flag(1337));},
onComplete: function() {console.log("done.");}
});
});
  • Java.choose: enumerates through instances of the first argument, the MainActivityclass, at runtime.

It has two callbacks:

  • onMatch function is executed when the instance is found.
  • onComplete function executes when the Java.choose()operation ends.

That’s it for today, thanks for reading, and stay tuned for more.

Contacts

LinkedIn

--

--

No responses yet