Frida 16.5.0 Released ∞
release
Some of you may have found yourself in a situation where a piece of data in memory looks interesting, and you want to locate the code responsible for it. You may have tried Frida’s MemoryAccessMonitor API, but found the page granularity hard to work with. That is, you might have to collect many samples until you get lucky and catch the code that accesses those specific bytes on the page that they’re on. This might be hard enough on systems with 4K pages, and even worse on modern Apple systems with 16K pages.
To address this, @hsorbo and I filled up our coffee cups and got to work, implementing support for hardware breakpoints and watchpoints. The long story short is that thread objects returned by Process.enumerateThreads() now have setHardwareBreakpoint(), setHardwareWatchpoint(), and corresponding methods to unset them at a later point. These are then combined with Process.setExceptionHandler() where you call the unset method and return true to signal that the exception was handled, and execution should be resumed.
Demo time
Let’s take these new APIs for a spin. As our target we’ll pick id Software’s brand new 2024 re-release of DOOM + DOOM II.
The first thing we might want to do is figure out where in memory the number of bullets is stored. Let’s cook up a tiny agent to help us achieve this:
And load it into the game:
We know that we have 50 bullets currently, so let’s find all heap allocations containing the value 50, encoded as a native uint32:
That’s quite a few. Let’s narrow it down by firing one bullet, and checking which of those locations now contain the value 49:
Bingo! Now that we know where in memory the number of bullets is stored, our next step is to find the code that updates the number of bullets when one is fired. Let’s add another helper function to our agent:
And call it:
Next we’ll flip back to the game and fire another bullet:
Yay, that looks promising. Let’s symbolicate that address:
Let’s take a closer look using r2:
We can see that the program counter that we observed in our exception handler is
on the instruction right after the sub
that triggered our watchpoint.
So from here we can set up an inline hook that gets fired whenever a bullet is fired:
We can just as easily make ourselves a cheat for infinite ammo:
Look ma, infinite ammo!
Note that we could also have achieved this by using Memory.patchCode() to
replace the sub
with a 3-byte nop
, which X86Writer can do for us through
putNopPadding(3). The Interceptor hook has the advantage of automatically
being rolled back when our script is unloaded, and makes it easy to execute
arbitrary code.
Windows on ARM
Another highlight of this release is that we now support Windows on ARM. This means that an arm64 version of Frida can inject into native arm64 processes, as well as emulated x86_64 and x86 processes.
We don’t yet provide binaries however, as we’re waiting for GitHub to provide arm64 runners to OSS projects, which for now is limited to their Team and Enterprise Cloud customers. While it is technically feasible to cross-compile from an x86_64 build machine, we decided to punt on this as we quickly ran into issues with Meson’s MSVC support.
EOF
There’s also a slew of other exciting changes, so definitely check out the changelog below.
Enjoy!
Changelog
- thread: Support hardware breakpoints and watchpoints.
- fruity: Fix deadlock in perform_on_lwip_thread().
- windows: Add support for arm64.
- windows: Migrate Exceptor to Microsoft’s VEH API.
- linux: Handle process gone when detaching. Thanks @ajwerner!
- linux: Fix the clone() wrapper on MIPS.
- java: Handle Android GC cycle handlers not being exported. Thanks @thinhbuzz!
- java: Add preliminary support for OpenJDK 17 on Windows. Thanks @FrankSpierings!
- meson: Add frida-netif to the public frida-core, so frida-core devkits include all needed symbols.
- node: Add Cancellable.withTimeout() convenience factory function. Thanks @hsorbo!
- node: Add Cancellable.combine() convenience method. Thanks @hsorbo!