I've recently had another need to fix an issue in frida and I had to re-discover some things about how to develop for this tool. With that in mind, this blog post is meant to cover some quick tips for how to go about developing or fixing frida's code base. Mostly so I can reference it later once I forget how it works again...
What is frida?
Frida is a dynamic binary instrumentation (DBI) tool, brought to life by Ole André Vadla Ravnås. At it's core, frida utilizes javascript to do much of the heavy lifting. There are actually two separate javascript engines that can be used, namely v8 and duk. This becomes important for development as the two engines can behave a bit differently. Also, they have separate (as well as shared) code bases in the framework. This means that developing for one engine does not automatically mean that the code you created (or debugged) will be available to the other engine.
Components
Frida is a very complicated piece of software, with many abstraction layers and inherited functionality. It also utilizes different languages, such as C, C++, javascript and vala to name a few. My plan is to update the information here as I better understand myself. Here's a limited breakdown of what is in what repo:
Component | Repo | What |
---|---|---|
Frida | https://github.com/frida/frida | This repo is pretty much for combining all the pieces of frida together. It does not contain much functionality itself. |
Frida-core | https://github.com/frida/frida-core |
Static linkings for frida's bindings Loaders for frida (preload, injection, etc) vala for handling things like what to do when a process forks |
Frida-gum | https://github.com/frida/frida-gum |
This is where most of the magic is v8/duk js bindings code to bind together js with low level access specific handling of linux vs windows vs darwin |
Basic Debugging Tips
These have mostly been stolen from things that Ole has said on issue posts...
Build Environment
The first step is to recursively clone the frida repos:
git clone --recursive https://github.com/frida/frida.git
Next, you likely want to edit the "config.mak" file in the root of that and remove the "--strip". This will ensure that when you build frida, the symbols will not be stripped away.
The build process is simple, but the build environment can sometimes be irritating to setup. I've create a docker image that is built just for this purpose.
alias frida_build="sudo docker run -it --rm -v \$PWD:/frida bannsec/frida_build"
# Give it the same arguments you would give make
# Run all tests for x86_64
frida_build check-gum-linux-x86_64-thin
# Run specific test for V8
frida_build check-gum-linux-x86_64-thin tests=/GumJS/Script/Process/process_nested_signal_handling#V8
# Run specific test for duk
frida_build check-gum-linux-x86_64-thin tests=/GumJS/Script/Process/process_nested_signal_handling#DUK
# Build python3 .so module
frida_build python-linux-x86_64 PYTHON=/usr/bin/python3
Printing Output
It's often helpful to print output during execution of frida. This can generally be done to either stderr or the syslog service:
// Logs to the system logger
g_info ("Foo %d\n", foo);
// Prints to stderr
g_printerr ("Foo %d\n", foo);
When those print primitives aren't enough, Ole has posted some of his own more advanced print statements for debugging: https://gist.github.com/oleavr/00d71868d88d597ee322a5392db17af6
Stepping Through Frida
It's possible to source step through frida just like any other software. It's important that you removed the --strip mentioned above, and that the frida library that's injected is not stripped and has debugging information. If all compiled just fine, then you should be able to attach gdb to the process and use the "layout src" view and "step" to step through the source code of frida. Obviously, you will likely have to use the "directory" command in gdb to tell it where frida's source code is.
Sometimes, you may want to debug very early in execution. On linux, frida utilizes ptrace to load, which means that you cannot start the program in gdb and simply attach frida. That said, you can add some shim code into the software you're debugging to help with this.
// If you want to pause in the C code
while (!gum_process_is_debugger_attached ())
{
g_printerr ("Waiting for debugger in PID %u...\n", getpid ());
g_usleep (G_USEC_PER_SEC);
}
// You can do something similar in javascript
while (!Process.isDebuggerAttached()) {
console.log('Waiting for debugger in PID:', Process.id);
Thread.sleep(1);
}