Frida 10.4 Released ∞release
Frida provides quite a few building blocks that make it easy to do portable instrumentation across many OSes and architectures. One area that’s been lacking has been in non-portable use-cases. While we did provide some primitives like Memory.alloc(Process.pageSize) and Memory.patchCode(), making it possible to allocate and modify in-memory code, there wasn’t anything to help you actually generate code. Or copy code from one memory location to another.
Let’s take a look at an example on x86:
Which means we replaced the beginning of our target function with simply:
I.e. assuming the return type is
int, we just replaced the function body with
As a side-note you could also use Memory.protect() to change the page protection and then go ahead and write code all over the place, but Memory.patchCode() is very handy because it also
- ensures CPU caches are flushed;
- takes care of code-signing corner-cases on iOS.
So that was a simple example. Let’s try something a bit crazier:
Finally, let’s look at how to copy instructions from one memory location to another. Doing this correctly is typically a lot more complicated than a straight memcpy(), as some instructions are position-dependent and need to be adjusted based on their new locations in memory. Let’s look at how we can solve this with Frida’s new relocator APIs:
We just made our own replica of puts() in just a few lines of code. Neat!
Note that you can also insert your own instructions, and use skipOne() to selectively skip instructions in case you want to do custom instrumentation. (This is how Stalker works.)
Anyway, that’s the gist of it. You can find the brand new API references at:
Also note that Process.arch is convenient for determining which
writer/relocator to use. On that note you may wonder why there’s just a single
implementation for 32- and 64-bit x86. The reason is that these instruction sets
are so close that it made sense to have a unified implementation. This also
makes it easier to write somewhat portable code, as some meta register-names are
xax resolves to
rax depending on the kind of
process you are in.