Frida 15.2.0 Released ∞
release
Super-excited about this one. What I’ve been wanting to do for years is to streamline Frida’s JavaScript developer experience. As a developer I may start out with a really simple agent, but as it grows I start to feel the pain.
Early on I may want to split up the agent into multiple files. I may also want to use some off-the-shelf packages from npm, such as frida-remote-stream. Later I’d want code completion, inline docs, type checking, etc., so I move the agent to TypeScript and fire up VS Code.
Since we’ve been piggybacking on the amazing frontend web tooling that’s already out there, we already have all the pieces of the puzzle. We can use a bundler such as Rollup to combine our source files into a single .js, we can use @frida/rollup-plugin-node-polyfills for interop with packages from npm, and we can plug in @rollup/plugin-typescript for TypeScript support.
That is quite a bit of plumbing to set up over and over though, so I eventually created frida-compile as a simple tool that does the plumbing for you, with configuration defaults optimized for what makes sense in a Frida context. Still though, this does require some boilerplate such as package.json, tsconfig.json, and so forth.
To solve that, I published frida-agent-example, a repo that can be cloned and used as a starting point. That is still a bit of friction, so later frida-tools got a new CLI tool called frida-create. Anyway, even with all of that, we’re still asking the user to install Node.js and deal with npm, and potentially also feel confused by the .json files just sitting there.
Then it struck me. What if we could use frida-compile to compile frida-compile into a self-contained .js that we can run on Frida’s system session? The system session is a somewhat obscure feature where you can load scripts inside of the process hosting frida-core. For example if you’re using our Python bindings, that process would be the Python interpreter.
Once we are able to run that frida-compile agent inside of GumJS, we can communicate with it and turn that into an API. This API can then be exposed by language bindings, and frida-tools can consume it to give the user a frida-compile CLI tool that doesn’t require Node.js/npm to be installed. Tools such as our REPL can seamlessly use this API too if the user asks it to load a script with a .ts extension.
And all of that is precisely what we have done! 🥳
build()
Here’s how easy it is to use it from Python:
The bundle variable is a string that can be passed to create_script(), or written to a file.
Running that example we might see something like:
That makes us wonder why it failed, so let’s add a handler for the diagnostics signal:
And suddenly it’s all making sense:
We forgot to actually create the file! Ok, let’s create agent.ts:
And let’s also write that script to a file:
If we now run it, we should have an _agent.js ready to go:
This weird-looking format is how GumJS’ allows us to opt into the new ECMAScript Module (ESM) format where code is confined to the module it belongs to instead of being evaluated in the global scope. What this also means is we can load multiple modules that import/export values. The .map files are optional and can be omitted, but if left in they allow GumJS to map the generated JavaScript line numbers back to TypeScript in stack traces.
Anyway, let’s take _agent.js for a spin:
It works! Now let’s try refactoring it to split the code into two files:
agent.ts
log.ts
If we now run our example compiler script again, it should produce a slightly more interesting-looking _agent.js:
Loading that into the REPL should yield the exact same result as before.
watch()
Let’s turn our toy compiler into a tool that loads the compiled script, and recompiles whenever a source file changes on disk:
And off we go:
If we leave that running and then edit the source code on disk we should see some new output:
Yay!
frida-compile
We can also use frida-tools’ new frida-compile CLI tool:
It also supports watch mode:
REPL
Our REPL is also powered by the new frida.Compiler:
Shoutout
Shoutout to @hsorbo for the fun and productive pair-programming sessions where we were working on frida.Compiler together! 🙌
EOF
There are also quite a few other goodies in this release, so definitely check out the changelog below.
Enjoy!
Changelog
- core: Add Compiler API. Only exposed by Python bindings for now, but available from C/Vala.
- interceptor: Improve replace() to support returning original. Thanks @aviramha!
- gumjs: Fix typing for pc in the writer options.
- gumjs: Fix V8 ESM crash with circular dependencies.
- gumjs: Handle ESM bundles with multiple aliases per module.
- gumjs: Tighten up the Checksum data argument parsing.
- android: Fix null pointer deref in crash delivery. Thanks @muhzii!
- fruity: Use env variables to find usbmuxd. Thanks @0x3c3e!
- ios: Make Substrate detection logic a bit more resilient. Thanks @lemon4ex!
- meson: Only try to use V8 if available. Thanks @muhzii!
- windows: Add support for building without V8.
- devkit: Fix library dependency hints on Windows. Thanks @nblog!