Frida 10.8 Released ∞
release
Get ready for a major upgrade. This time we have solved our three longest standing limitations – all in one release.
Limitation #1: fork()
As a quick refresher, this old-school UNIX API clones the entire process and returns the child’s process ID to the parent, and zero to the child. The child gets its own copy of the parent’s address space, usually at a very small cost due to copy-on-write.
Dealing with this one gets tricky once multiple threads are involved. Only the thread calling fork() survives in the child process, so if any of the other threads happen to be holding locks, those locks will still be held in the child, and nobody is going to release them.
Essentially this means that any application doing both forking and multi-threading will have to be really carefully designed. Even though most applications that fork are single-threaded, Frida effectively makes them multi-threaded by injecting its agent into them. Another aspect is file-descriptors, which are shared, so those also have to be carefully managed.
I’m super-excited to announce that we are finally able to detect that a fork() is about to happen, temporarily stop our threads, pause our communications channel, and start things back up afterwards. You are then able to apply the desired instrumentation to the child, if any, before letting it continue running.
Limitation #2: execve(), posix_spawn(), CreateProcess(), and friends
Or in plain English: programs that start other programs, either by replacing themselves entirely, e.g. execve(), or by spawning a child process, e.g. posix_spawn() without POSIX_SPAWN_SETEXEC.
Just like after a fork() happened you are now able to apply instrumentation and control when the child process starts running its first instructions.
Limitation #3: dealing with sudden process termination
An aspect that’s caused a lot of confusion in the past is how one might hand off some data to Frida’s send() API, and if the process is about to terminate, said data might not actually make it to the other side.
Up until now the prescribed solution was always to hook exit(), abort() etc. so you can do a send() plus recv().wait() ping-pong to flush any data still in transit. In retrospect this wasn’t such a great idea, as it makes it hard to do this correctly across multiple platforms. We now have a way better solution.
So with that, let’s talk about the new APIs and features.
Child gating
This is how we address the first two. The Session object, the one providing create_script(), now also has enable_child_gating() and disable_child_gating(). By default Frida will behave just like before, and you will have to opt-in to this new behavior by calling enable_child_gating().
From that point on, any child process is going to end up suspended, and you will be responsible for calling resume() with its PID. The Device object now also provides a signal named delivered which you should attach a callback to in order to be notified of any new children that appear. That is the point where you should be applying the desired instrumentation, if any, before calling resume(). The Device object also has a new method named enumerate_pending_children() which can be used to get a full list of pending children. Processes will remain suspended and part of that list until they’re resumed by you, or eventually killed.
So that’s the theory. Let’s have a look at a practical example, using Frida’s Python bindings:
And action:
Flush-before-exit
As for the third limitation, namely dealing with sudden process termination, Frida will now intercept the most common process termination APIs and take care of flushing any pending data for you.
However, for advanced agents that optimize throughput by buffering data and only doing send() periodically, there is now a way to run your own code when the process terminates, or the script is unloaded. All you need to do is to define an RPC export named dispose. E.g.:
In closing
Building on the brand new fork()-handling in Frida, there is also a fully reworked Android app launching implementation. The frida-loader-{32,64}.so helper agents are now gone, and our behind-the-scenes Zygote instrumentation is now leveraging the brand new child gating to do all of the heavy lifting. This means you can also instrument Zygote for your own needs. Just remember to enable_child_gating() and resume() any children that you don’t care about.
So that’s pretty much it for this release. Enjoy!