Getting Started with Frida : Hooking a Function and Replacing its Arguments

Getting started with frida.

Getting Started with Frida : Hooking a Function and Replacing its Arguments

Hi there,

Today we will talk about frida a dynamic code instrumentation toolkit, to put it in simple words, frida injects itself to the target process memory and allows you to manipulate the process in some cool ways, more on that later.

I was introduced to frida at work for some android related stuff about 2 weeks ago, back then I used a ready made script to complete the task, I don't like using tools without understanding them and it was very interesting to me how frida was able to complete the job so smoothly.

I started looking at the API docs, frida has an API that allows you to hook functions (execute certain code when a function is called), this particular API is called the Interceptor API, It also allows you to trace the execution of the process using the Stalker API, it also allows you to modify process memory or allocate new memory, you can even compile c code directly into process memory using the CModule API , it also has an API for Java and Objective C which why it's very popular for mobile hacking.

You can now see how powerful frida is, combining the above features you can build any tool you need.

My experience with frida is limited, however you will see how easy it is to start using frida and how easy it is to learn.

In this post I will show you how to use the frida Javascript API to hook the main function and print its arguments, also I will show you how to replace one of the arguments with a string allocated in memory by frida.

Our sample application is very simple (let's call it test.c).

#include <stdio.h>

int main(int argc, char **argv) {
	printf("Ouptut from C program : argc is : %d\n",argc);
	int i = 0;
	while(argv[i] != NULL) {
		printf("Output from C program arg[%d] is : %s : Address : %p\n", i, argv[i],argv[i]);
		i++;
	}
}

The program is going to loop through the arguments (argv array ends with NULL) and print the arguments and the address of each argument.

You can compile the above program with gcc using the command.

gcc test.c

That will result in an executable named a.out which is the default name used by gcc.

To print the values of the main function arguments using frida we will use frida Interceptor API, the Interceptor allows you to define two functions, the first one is onEnter which is the handler that will be called right before the execution of the hooked function (in this case we will hook the main function) and the second one is onLeave which is the handler that will be called when the function finishes execution.

frida also provides useful functions that will return the function addresses which is necessary to define the hook, for example to get the address of the main function for an executable compiled with symbols you can use DebugSymbol.fromName("main").address which will return the address to main.

Let's take a look at our code that will print main arguments (let's call it printargs.js).

Interceptor.attach(DebugSymbol.fromName("main").address, {
  onEnter: function (args) {
    console.log("\n----Output from Frida Script----")
    console.log("Main at : " + DebugSymbol.fromName("main").address)
    console.log("argc : " + args[0].toInt32())
    console.log("argv[0]:" + args[1].readPointer().readCString())
  },
  onLeave: function () {
  }
});

You can see that first we are calling the Interceptor.attach function which is used to hook a certain function, then we pass the address of the function we want to hook, in this case we obtained the address of main using the DebugSymbol.fromName function.

Then we define our onEnter function, as you can see there's a parameter named args passed to the handler, that will contain the pointers to the function arguments, the arguments are passed as NativePointer objects which allows easy access to a location in memory and contains useful functions to convert that location to different types.

For example, the first argument to main is argc which is of type int to access the argument we used args[0].toInt32() , as you can see we used toInt32 to convert the NativePointer to int.

For argv[0] which usually contains the program name, i.e in this case it should contain a.out , we used args[1].readPointer().readCString() the reason is that the second argument to main is a pointer to an array of pointers and each pointer in that array points to a c string, so arg[1].readPointer() is going to read the first pointer in the array, then calling readString() on that pointer will read the the string pointed to by argv[0].

To run this script using frida we used the -l command line option which loads a script, we can also use --no-pause which will run the program and script without pausing (without that option frida will pause the execution at the beginning of the program execution similar to what a debugger does), then we will follow those options with the program we want to instrument along with its command line options.

The final command should be.

frida -l ./modifyargs.js --no-pause ./a.out arg1 arg2

When you run it you will see the following output.

Notice the --Output from Frida Script– that's the output from our script

That was it for printing the arguments, as you can see the API provides everything you need.

Now let's do something different let's replace argv[0] with our own string, that's a very useful trick for example if you want to implement something like in-memory fuzzer.

So the first thing we will need to do is to allocate the space and store our string in memory, frida is providing us with all what we need, we can do the allocation in one line of code.

var strPtr = Memory.allocUtf8String("Fady was here!!!!")

That line will allocate space in program memory and store the string in the allocated memory and will return a NativePointer object referring to that string all what we need to do now is to replace argv[0] with that string, to do so we will use the writePointer method which will write a pointer to a location in memory, so to replace argv[0] we will need to add the line args[1].writePointer(strPtr) to our onEnter handler.

To summarize our modifyargs.js script will be

//Allocating memory.
var strPtr = Memory.allocUtf8String("Fady was here!!!!")

Interceptor.attach(DebugSymbol.fromName("main").address, {
  onEnter: function (args) {
    //Overwriting argc.
    args[0] = ptr(12)
    //Overwriting argv[1]
    args[1].writePointer(strPtr)
  },
  onLeave: function (retval) {

  }
});

Now let's run the script with frida to see if we can actually replace our arguments.

As you can see the program printed the string we provided instead of a.out you can also notice that the address of arg[0] is very different than the address of arg[1] and arg[2] that's because those arguments are on the stack but frida allocUtf8String allocates memory in the heap.

I hope that helps you getting started with frida, I found the frida documentation super useful if you want to get your hands dirty with frida, the javascript API docs can be found at https://frida.re/docs/javascript-api/