We show how to use Frida to inspect functions as they are called, modify their
arguments, and do custom calls to functions inside a target process.
Setting up the experiment
Create a file hello.c:
Compile with:
Start the program and make note of the address of f() (0x400544 in the
following example):
Hooking Functions
The following script shows how to hook calls to functions inside a target
process and report back a function argument to you. Create a file hook.py
containing:
Run this script with the address you picked out from above (0x400544 on our
example):
This should give you a new message every second on the form:
Modifying Function Arguments
Next up: we want to modify the argument passed to a function inside a target
process. Create the file modify.py with the following contents:
Run this against the hello process (which should be still running):
At this point, the terminal running the hello process should stop counting
and always report 1337, until you hit Ctrl-D to detach from it.
Calling Functions
We can use Frida to call functions inside a target process. Create the file
call.py with the contents:
Run the script:
and keep a watchful eye on the terminal (still) running hello:
Experiment No. 2 - Injecting Strings and Calling a Function
Injecting integers is really useful, but we can also inject strings,
and indeed, any other kind of object you would require for fuzzing/testing.
Create a new file hi.c:
In a similar way to before, we can create a script stringhook.py, using Frida
to inject a string into memory, and then call the function f() in the following
way:
Keeping a beady eye on the output of hi, you should see something along these
lines:
Use similar methods, like Memory.alloc() and Memory.protect() to manipulate
the process memory with ease. Couple this with the python ctypes library, and
other memory objects, like structs can be created, loaded as byte arrays, and
then passed into functions as pointer arguments.
Anyone who has done network programming knows that one of the most commonly
used data types is the struct in C. Here is a naive example of a program
that creates a network socket, and connects to a server over port 5000, and
announces itself by sending the string "Hello there!" over the connection.
This is fairly standard code, and calls out to any IP address given as the
first argument. If you run nc -lp 5000 and in another terminal window run
./client 127.0.0.1, you should see the message appear in netcat, and also
be able to send messages back to client in return.
Now, we can start having some fun - as we saw above, we can inject strings and
pointers into the process. We can do the same by manipulating the struct
sockaddr_in which the program spits out as part of its operation:
If you are not fully familiar with the structure of a struct, there are many
resources online that will tell you what’s what. The important bits here are the
bytes 0x1388, or 5000 in dec. This is our port number (the 4 bytes that
follow are the IP address in hex). If we change this to 0x1389 then we can
re-direct our client to a different port. If we change the next 4 bytes we
can change the IP address that the client points at completely!
Here’s a script to inject the malicious struct into memory, and then hijack the
connect() function in libc.so to take our new struct as its argument.
Create the file struct_mod.py as follows:
Note that this script demonstrates how the Module.getExportByName() API can
be used to find any exported function by name in our target. If we can supply a
module then it will be faster on larger binaries, but that is less critical
here.
Now, run ./client 127.0.0.1, in another terminal run nc -lp 5001, and in a
third terminal run ./struct_mod.py. Once our script is running, press ENTER
in the client terminal window, and netcat should now show the string sent
by the client.
We have successfully hijacked the raw networking by injecting our own data
object into memory and hooking our process with Frida, and using Interceptor
to do our dirty work in manipulating the function.
This shows the real power of Frida - no patching, complicated reversing, nor
difficult hours spent staring at dissassembly without end.