Reverse engineering a C Binary using Frida and Ghidra
@shubh_exists|Nov 17, 2024 (1y ago)272 views
These are my notes of how to inject scripts into C binaries using Frida.
Context
Frida is a tool to dynamically inject script to memory locations and extract data from them. It is most commonly used in Reverse Engineering and modding applications.
Example C Script which will be taken as reference -
// main.c
#include <stdio.h>
#include <unistd.h>
int checkPassword(int input)
{
if (input == 1234)
{
return 1;
}
return 0;
}
int main()
{
int input = 0;
int result = 0;
while (1)
{
printf("%s\n", "Enter the pin: ");
scanf("%d", &input);
sleep(10);
result = checkPassword(input);
if (result)
{
printf("%s\n", "Win!");
break;
}
else
{
printf("%s\n", "Fail.");
}
}
return 0;
}
This function repeatedly takes Input from User till he enter's the correct number. It sleeps for 10 secs after every attempt
Compile this C script into a binary named demo
gcc -o demo demo.c
Frida
Installation
sudo apt install build-essential
sudo pip install frida
sudo pip install frida-tools
Now first let's try to remove the 10 secs timer (which anyways makes no sense) -
Run the binary in one terminal instance.
Run sudo frida demo on the other. demo is the process name which we are trying to hook frida to -

Run
Module.enumerateSymbolsSync("demo")

Output of the above command has a lot of objects. We would focus on memory where "sleep" is called.
To get the address of this function -
Module.getExportByName(null, "sleep");

This address keeps changing every time we run the demo binary. So, we need to write a script to dynamically get the memory address of this function using Frida.
// script.js
var sleep = Module.getExportByName(null, 'sleep');
Interceptor.attach(sleep, {
onEnter: function (args) {
console.log('Entering sleep...');
console.log('Sleeping for: ' + parseInt(args[0]) + ' seconds.');
console.log('Overriding sleep argument.');
args[0] = ptr('0x00');
},
onLeave: function (ret) {
console.log('Leaving sleep...');
},
});
sleep variable has the memory address of the sleep function.
We use the onEnter function provided by Frida to make the argument of the sleep function to "0".
Inject this script into the binary by sudo frida demo -l script.js
Now the binary won't have any sleep time after every request. :)
Now the issue is we could only get the addresses of the functions that are present in C. Injecting scripts in custom functions like checkPassword is a tricky process and we need decompilers to help us here.
In this tutorial, I'll use Ghidra to decompile our C binary.

In the left sidebar, navigate to checkPassword function and then press the Display Function Graph for better visualization.

Navigate to the desired function here, checkPassword and on right clicking it, click on copy special and then copy the address.

NOTE - This address is a relative address of the binary (offset). This assumes the start of the binary as 0x00 and gives the relative address of the
checkPasswordfunction.
Since, we know the relative path of the function, we can write the script to get the relative to get the function.
// script.js
var checkPass = Process.enumerateModulesSync()[0].base.add('0x11a9');
Interceptor.attach(checkPass, {
onEnter: function (args) {
console.log('Entering checkPass');
},
onLeave: function (ret) {
console.log('Returning: ' + parseInt(ret));
console.log('Overwriting return value.');
ret.replace(ptr('0x01'));
console.log('Returning: ' + parseInt(ret));
},
});
Assuming the "address" to be 0x11a9 NativeFunction takes in address, return
type and an array of arguments.
This takes the return value and makes it always "1". So any number will now pass the check password test.
// script.js
var checkPass = Process.enumerateModulesSync()[0].base.add('0x11a9');
var checkPassFunc = new NativeFunction(checkPass, 'int', ['int']);
for (let i = 1000; i < 2000; i++) {
var res = checkPassFunc(i);
console.log(res);
if (res == 1) {
console.log('Win: ' + i);
break;
}
}
This takes in the function from the address and bruteforces it 1000 times.
Similarly, a lot of functions are provided by Frida to dynamically inject scripts in C binaries.