Frida 15.1 Released

Introducing the brand new Swift bridge! Now that Swift has been ABI-stable since version 5, this long-awaited bridge allows Frida to play nicely with binaries written in Swift. Whether you consider Swift a static or a dynamic language, one thing is for sure, it just got a lot more dynamic with this Frida release.


Probably the first thing a reverser does when they start reversing a binary is getting to know the different data structures that the binary defines. So it made most sense to start by building the Swift equivalent of the ObjC.classes and ObjC.protocols APIs. But since Swift has other first-class types, i.e. structs and enums, and since the Swift runtime doesn’t offer reflection primitives, at least not in the sense that Objective-C does, it meant we had to dig a little deeper.

Luckily for us, the Swift compiler emits metadata for each type defined by the binary. This metadata is bundled in a TargetTypeContextDescriptor C++ struct, defined in include/swift/ABI/Metadata.h at the time of writing. This data structure includes the type name, its fields, its methods (if applicable,) and other useful data depending on the type at hand. These data structures are pointed to by relative pointers (defined in include/swift/Basic/RelativePointer.h.) In Mach-O binaries, these are stored in the __swift5_types section.

So to dump types, Frida basically iterates over these data structures and parses them along the way, very similar to what dsdump does, except that you don’t have to build the Swift compiler in order to tinker with it.

Frida also has the advantage of being able to probe into internal Apple dylibs written in Swift, and that’s because we don’t need to parse the dyld_shared_cache thanks to the private getsectiondata API, which gives us section offsets hassle-free.

Once we have the metadata, we’re able to easily create JavaScript wrappers for object instances and values of different types.


To be on par with the Objective-C bridge, the Swift bridge has to support calling Swift functions, which also proved to be not as straight forward.

Swift defines its own calling convention, swiftcall, which, to put it succinctly, tries to be as efficient as possible. That means, not wasting load and store instructions on structs that are smaller than 4 registers-worth of size. That is, to pass those kinds of structs directly in registers. And since that could quickly over-book our precious 8 argument registers (on AARCH64 x0-x7), it doesn’t use the first register for the self argument. It also defines an error register where callees can store errors which they throw.

What we just described above is termed “physical lowering” in the Swift compiler docs, and it’s implemented by the back-end, LLVM.

The process that precedes physical lowering is termed “semantic lowering,” which is the compiler front-end figuring out who “owns” a value and whether it’s direct or indirect. Some structs, even though they might be smaller than 4 registers, have to be passed indirectly, because, for example, they are generic and thus their exact memory layout is not known at compile time, or because they include a weak reference that has to be in-memory at all times.

We had to implement both semantic and physical lowering in order to be able to call Swift functions. Physical lowering is implemented using JIT-compiled adapter functions (thanks to the Arm64Writer API) that does the necessary SystemV-swiftcall translation. Semantic lowering is implemented by utilizing the type’s metadata to figure out whether we should pass a value directly or not.

The compiler docs are a great resource to learn more about the calling convention.


Because Swift passes structs directly in registers, there isn’t a 1-to-1 mapping between registers and actual arguments.

And now that we have JavaScript wrappers for types, and are able to call Swift functions from the JS runtime, a good next step would be extending Interceptor to support Swift functions.

For functions that are not stripped, we use a simple regex to parse argument types and names, same for return values. After parsing them we retrieve the type metadata, figure out the type’s layout, then simply construct JS wrappers for each argument, which we pass the Swift argument value, however many registers it occupies.


Note that the bridge is still very early in development, and so:

  • Currently supports Darwin arm64(e) only.
  • Performance is not yet in tip-top shape, some edge case might not be handled properly and some bugs are to be expected.
  • There’s a chance the API might change in breaking ways in the short-to-medium term.
  • PRs and issues are very welcome!

Refer to the documentation for an up-to-date resource on the current API.


Changes in 15.1.0

  • Implement the Swift bridge, which allows Frida to:
    • Explore Swift modules along with types implemented in them, i.e. classes, structs, enums and protocols.
    • Create JavaScript wrappers for object instances and values.
    • Invoke functions that use the swiftcall calling convention from the JavaScript runtime.
    • Intercept Swift functions and automatically parse their arguments and return values.
  • Fix i/macOS regression where changes related to iOS 15 support ended up breaking support for attaching to Apple system daemons.
  • gadget: Add interaction.parameters in connect mode. These parameters are then “reflected” into app’s info under parameters.config. Thanks @mrmacete!

Changes in 15.1.1

  • gumjs: Fix Swift lifetime logic in the V8 runtime.

Changes in 15.1.2

  • control-service: Fix signal wiring, so signals such as Device.output get emitted correctly by a remote frida-server. Thanks @mrmacete!
  • gadget: Fix the “runtime” option, which was forgotten during the refactorings leading up to Frida 15.
  • relocator: Optimize handling of x86 RIP relative code, by simply adjusting the offset where possible.
  • gumjs: Add ESM support, so tools like frida-compile can output better code.
  • gumjs: Throw away source code after parsing it.
  • gumjs: Plug leak when compiling to QuickJS bytecode.
  • java: Expose JNIEnv->GetDirectBufferAddress. Thanks @pandasauce!

Changes in 15.1.3

  • objc: Fix the Proxy respondsToSelector implementation. Thanks @hot3eed!
  • gumjs: Fix V8 runtime crash when module is missing.
  • gumjs: Emit V8 exceptions thrown by ESM entrypoints.

Changes in 15.1.4

  • gumjs: Fix double-free related to weak ref callbacks in the QuickJS runtime. Thanks @mrmacete!
  • gumjs: Ignore Interceptor context in unrelated NativeCallback invokes. In this way invalid Interceptor contexts from higher up the call stack will be safely ignored in favor of the minimal but correct callback context. Thanks @mrmacete!
  • gumjs: Fix ESM module name normalization logic.
  • gumjs: Add Process getters for cwd, home, and tmp dirs.
  • swift: Improve metadata caching performance by ~3x. Thanks @hot3eed!
  • node: Publish Electron prebuilds for v15 instead of v13.

Changes in 15.1.5

  • gumjs: Fix crash when QuickJS stringifies large numbers. Thanks @vfsfitvnm!
  • gumjs: Expose GError and GIConv to CModule. Thanks @0xDC00!
  • droidy: Support ADB server host/port environment variables. Thanks @amirpeles90!
  • swift: Improve load behavior on non-Darwin.

Changes in 15.1.6

  • swift: Fix crash during load on non-Darwin. Thanks @hot3eed!

Changes in 15.1.7

  • swift: Fix CoreSymbolication integration on older OS versions. Thanks @hot3eed!
  • python: Add download logic for Android/ARM. Our CI doesn’t yet build and upload such a binary, though.

Changes in 15.1.8

  • darwin: Add support for working in the SRD environment. Thanks @Nessphoro!
  • darwin: Add support for building with newer iOS SDKs.

Changes in 15.1.9

  • x86-relocator: Fix patching of RIP relative instructions. This was a regression introduced in 15.1.2, resulting in Stalker becoming unreliable.
  • portal-service: Always remove ClusterNode sessions whenever a session ID is unset from PortalService. This avoids both NULL dereferences and leaks. Thanks @mrmacete!
  • frida-portal: Fix typo in –help output.

Changes in 15.1.10

  • p2p: Gracefully handle unsupported ICE candidates.
  • p2p: Disable ICE-TCP for now.
  • p2p: Rework PeerSocket to fix synchronization issues.
  • socket: Fix WebService teardown.
  • x86-writer: Add UD2 instructions after/in indirect branches.
  • x86-writer: Emit larger NOPs when possible.
  • stalker: Improve performance of the x86/64 backend.
  • objc-api-resolver: Guard against disposed ObjC classes. Thanks @mrmacete!
  • gumjs: Fix V8 Interceptor.{replace,revert}() regression. Thanks @3vilWind!
  • gumjs: Expand Instruction API w/ regsAccessed and operand.access. Thanks @3vilWind!

Changes in 15.1.11

  • x86-writer: Add put_sahf() and put_lahf().
  • x86-relocator: Fix handling of out-of-range Jcc branch targets. Thanks @0xDC00!
  • stalker: Optimize target address retrieval.
  • stalker: Avoid expensive XCHG instructions.
  • stalker: Optimize IC prolog to use SAHF/LAHF.
  • memory: Improve scan() to support regex patterns. Thanks @hot3eed!
  • kernel: Get base from all_image_info where supported. Thanks @mrmacete!
  • windows: Improve dbghelp backtracer reliability. Thanks @HonicRoku!

Changes in 15.1.12

  • agent: Fix hang on unload w/ NO_REPLY_EXPECTED calls, where we would wait for a reply to be sent. Due to the server-side DBus code being generated by the Vala compiler, and it previously (in Frida <= 15.1.9) ignoring NO_REPLY_EXPECTED, this bug went unnoticed.
  • android: Move to NDK r24 Beta 1.

Changes in 15.1.13

  • linux: Improve module resolution on glibc systems.
  • fruity: Fix spawn on dyld v4 case (jailed iOS 15.x). Thanks @mrmacete!
  • objc-api-resolver: Protect against objc_disposeClassPair() via mutex. Thanks @mrmacete!
  • gumjs: Update Kernel.scan*() to match Memory.scan*(). Thanks @hot3eed!
  • stalker: Fix emitted branch op-code on x86.
  • stalker: Fix handling of Thumb IT AL.
  • stalker: Handle excluded Linux calls through PLT on x86.
  • stalker: Fix Linux exception handling on x86.
  • java: Add Java.backtrace(), without any API stability guarantees for now.

Changes in 15.1.14

  • backtracer: Improve fuzzy backtracers to also include the immediate caller, and avoid walking past stack end when known.
  • windows: Implement Thread.try_get_ranges().
  • linux: Implement Thread.try_get_ranges().
  • ios: Check in with launchd on SRD systems.
  • stalker: Fix accidental clobbery in call depth code on x86.
  • node: Publish Node.js prebuilds for v17, too.

Frida 15.0 Released

So much has changed. Let’s kick things off with the big new feature that guided most of the other changes in this release:


Part I: Conception

Earlier this year @insitusec and I were brainstorming ways we could simplify distributed instrumentation use-cases. Essentially ship a Frida Gadget that’s “hollow”, where application-specific instrumentation is provided by a backend.

One way one could implement this is by using the Socket.connect() JavaScript API, and then define an application-specific signaling protocol over which the code is loaded, before handing it off to the JavaScript runtime.

But this way of doing things does quickly end up with quite a bit of boring glue code, and existing tools such as frida-trace won’t actually be usable in such a setup.

That’s when @insitusec suggested that perhaps Frida’s Gadget could offer an inverse counterpart to its Listen interaction. So instead of it being a server that exposes a frida-server compatible interface, it could be configured to act as a client that connects to a Portal.

Such a Portal then aggregates all of the connected gadgets, and also exposes a frida-server compatible interface where all of them appear as processes. To the outside it appears as if they’re processes on the same machine as where the Portal is running: they all have unique process IDs if you use enumerate_processes() or frida-ps, and one can attach() to them seamlessly.

In this way, existing Frida tools work exactly the same way – and by enabling spawn-gating on the Portal, any Gadget connecting could be instructed to wait for somebody to resume() it after applying the desired instrumentation. This is the same way spawn-gating works in other situations.

Part II: Implementation

Implementing this was a lot of fun, and it wasn’t long until the first PoC was up and running. It took some time before all the details were clear, though, but this eventually crystallized into the following:

The Portal should expose two different interfaces:

  1. The cluster interface that Gadgets can connect to, allowing them to join the cluster.
  2. Optionally also a control interface that controllers can talk to. E.g. frida-trace -H -n Twitter -i open

To a user this would be pretty simple: just grab the frida-portal binary from our releases, and run it on some machine that the Gadget is able to reach. Then point tools at that – as if it was a regular frida-server.

That is however only one part of the story – how it would be used for simple use-cases. The frida-portal CLI program is actually nothing more than a thin CLI wrapper around the underlying PortalService. This CLI program is just a bit north of 200 lines of code, of which very little is actual logic.

One can also use our frida-core language bindings, for e.g. Python or Node.js, to instantiate the PortalService. This allows configuring it to not provide any control interface, and instead access its device property. This is a standard Frida Device object, on which one can enumerate_processes(), attach(), etc. Or one can do both at the same time.

Using the API also offers other features, but we will get back to those.


Given how useful it might be to run a frida-portal on the public Internet, it was also clear that we should support TLS. As we already had glib-networking among our dependencies for other features, this made it really cheap to add, footprint-wise.

And implementation-wise it’s a tiny bit of logic on the client side, and similarly straight-forward for the server side of the story.

For the CLI tools it’s only a matter of passing --certificate=/path/to/pem. If it’s a server it expects a PEM-encoded file with a public + private key, where it will accept any certificate from incoming clients. For a client it’s also expecting a PEM-encoded file, but only with the public key of a trusted CA, which the server’s certificate must match or be derived from.

At the API level it boils down to this:

import frida

manager = frida.get_device_manager()
device = manager.add_remote_device("",
session = device.attach("Twitter")

Part IV: Authentication

The next fairly obvious feature that goes hand in hand with running a frida-portal on the public Internet, is authentication. In this case our server CLI programs now support --token=secret, and so do our CLI tools.

At the API level it’s also pretty simple:

import frida

manager = frida.get_device_manager()
device = manager.add_remote_device("",
session = device.attach("Twitter")

But this gets a lot more interesting if you instantiate the PortalService through the API, as it makes it easy to plug in your own custom authentication backend:

import frida

def authenticate(token):
    # Where `token` might be an OAuth access token
    # that is used to grab user details from e.g.
    # GitHub, Twitter, etc.
    user = 

    # Attach some application-specific state to the connection.
    return {

cluster_params = frida.EndpointParameters(authentication=('token', "wow-such-secret"))
control_params = frida.EndpointParameters(authentication=('callback', authenticate))
service = frida.PortalService(cluster_params, control_params)

The EndpointParameters constructor also supports other options such as address, port, certificate, etc.

Part V: Offline Mode

That leads us to our next challenge, which is how to deal with transient connectivity issues. I did make sure to implement automatic reconnect logic in PortalClient, which is what Gadget uses to connect to the PortalService.

But even if the Gadget reconnects to the Portal, what should happen to loaded scripts in the meantime? And what if the controller gets disconnected from the Portal?

We now have a solution that handles both situations. But it’s opt-in, so the old behavior is still the default.

Here’s how it’s done:

session = device.attach("Twitter",

Now, once some connectivity glitch occurs, scripts will stay loaded on the remote end, but any messages emitted will get queued. In the example above, the client has 30 seconds to reconnect before scripts get unloaded and data is lost.

The controller would then subscribe to the Session.detached signal to be able to handle this situation:

def on_detached(reason, crash):
    if reason == 'connection-terminated':
        # Oops. Better call session.resume()

session.on('detached', on_detached)

Once session.resume() succeeds, any buffered messages will be delivered and life is good again.

The above example does gloss over a few details such as our current Python bindings’ finicky threading constraints, but have a look at the full example here. (This will become a lot simpler once we port our Python bindings off our synchronous APIs and onto async/await.)

Part VI: Latency and Bottlenecks

Alright, so next up we’ve got a Portal running in a data center in the US, but the Gadget is at my friend’s place in Spain, and I’m trying to control it from Norway using frida-trace. It would be a shame if the script messages coming from Spain would have to cross the Atlantic twice, not just because of the latency, but also the AWS bill I’ll have to pay next month. Because I’m dumping memory right now, and that’s quite a bit of traffic right there.

This one’s a bit harder, but thanks to libnice, a lightweight and mature ICE implementation built on GLib, we can go ahead and use that. Given that GLib is already part of our stack – as it’s our standard library for C programming (and our Vala code compiles to C code that depends on GLib) – it’s a perfect fit. And this is very good news footprint-wise.

As a user it’s only a matter of passing --p2p along with a STUN server:

$ frida-trace \
    -H \
    --p2p \ \
    -n Twitter \
    -i open

(TURN relays are also supported.)

The API side of the story looks like this:


That’s all there is to it!

Part VII: Are Only Gadgets Invited To The Party?

You may have noticed that our Gadget has been a recurring theme so far. I’m not very excited about adding features that only apply to one mode, such as only Injected mode but not Embedded mode. So this was something that came to mind quite early on, that Portals had to be a universally available feature.

So say my buddy is reversing a target on his iPhone from his living room in Italy, and I’d like to join in on the fun, he can go ahead and run:

$ frida-join -U ReversingTarget cert.pem secret

Now I can jump in with the Frida REPL:

$ frida \
    -H \
    --certificate=cert.pem \
    --token=secret \
    --p2p \ \
    -n ReversingTarget

And if my buddy would like to use the API to join the Portal, he can:

session = frida.get_usb_device().attach("ReversingTarget")
membership = session.join_portal("",

Part VIII: The Web

Something I’ve been wanting to build since before Frida was born, is an online collaborative reversing app. Back in the very beginning of Frida, I built a desktop GUI that had integrated chat, console, etc. My not-so-ample spare-time was a challenge, however, so I eventually got rid of the GUI code and decided to focus on the API instead.

Now we’re in 2021, and single-page apps (SPAs) can be a really appealing option in many cases. I’ve also noticed that there’s been quite a few SPAs built on top of Frida, and that’s super-exciting! But what I’ve noticed when toying with SPAs on my own, is that it’s quite tedious to have to write the middleware.

Well, with Frida 15 I had to make some protocol changes to accomodate the features that I’ve covered so far, so it also seemed like the right time to really break the protocol and go ahead with a major-bump. This is something I’ve been trying to avoid for a long time, as I know how painful they are to everyone, myself included.

So now browsers can finally join in on the fun, without any middleware needed:

async function start() {
  const ws = wrapEventStream(new WebSocket(`ws://${}/ws`));
  const bus = dbus.peerBus(ws, {
    authMethods: [],

  const hostSessionObj = await bus.getProxyObject('re.frida.HostSession15',
  const hostSession = hostSessionObj.getInterface('re.frida.HostSession15');

  const processes: HostProcessInfo[] = await hostSession.enumerateProcesses({});
  console.log('Got processes:', processes);

  const target = processes.find(([, name]) => name === 'hello2');
  if (target === undefined) {
    throw new Error('Target process not found');
  const [pid] = target;
  console.log('Got PID:', pid);

  const sessionId = await hostSession.attach(pid, {
    'persist-timeout': new Variant('u', 30)

(Full example can be found in examples/web_client.)

This means that Frida’s network protocol is now WebSocket-based, so browsers can finally talk directly to a running Portal/frida-server, without any middleware or gateways in between.

I didn’t want this to be a half-baked story though, so I made sure that the peer-to-peer implementation is built on WebRTC data channels – this way even browsers can communicate with minimal latency and help keep the AWS bill low.

Part IX: Assets

Once we’ve built a web app to go with our Portal, which is speaking WebSocket natively, and thus also HTTP, we can also make it super-easy to serve that SPA from the same server:

$ ./frida-portal --asset-root=/path/to/web/app

This is also easy at the API level:

control_params = frida.EndpointParameters(asset_root="/path/to/web/app")
service = frida.PortalService(cluster_params, control_params)

Part X: Collaboration

A natural next step once we have a controller, say a web app, is that we might want collaboration features where multiple running instances of that SPA are able to communicate with each other.

Given that we already have a TCP connection between the controller and the PortalService, it’s practically free to also let the developer use that channel. For many use-cases, needing an additional signaling channel brings a lot of complexity that could be avoided.

This is where the new Bus API comes into play:

import frida

def on_message(message, data):
    # TODO: Handle incoming message.

manager = frida.get_device_manager()
device = manager.add_remote_device("")
bus = device.bus

bus.on('message', on_message)
    'type': 'rename',
    'address': "0x1234",
    'name': "EncryptPacket"
    'type': 'chat',
    'text': "Hey, check out EncryptPacket everybody"

Here we’re first attaching a message handler so we can receive messages from the Portal.

Then we’re calling attach() so that the Portal knows we’re interested in communicating with it. (We wouldn’t want it sending messages to controllers that don’t make use of the Bus, such as frida-trace.)

Finally, we post() two different message types. It is up to the PortalService to decide what to do with them.

So this means that the remote PortalService needs to be instantiated through the API, as incoming messages need to be handled – the Portal won’t forward them to other controllers on its own.

Worry not, though, this is easy:

import frida
import sys

def on_message(connection_id, message, data):
    # TODO: Handle incoming message.

cluster_params = frida.EndpointParameters()
control_params = frida.EndpointParameters()
service = frida.PortalService(cluster_params, control_params)
service.on('message', on_message)

In on_message() it should look at the message and decide what to do.

It might choose to reply to the controller that sent it the message:, {
    'type': 'rename-rejected',
    'reason': "Not authorized"

Another useful thing to do is sending a welcome message whenever somebody calls attach() on their Bus object:

def on_subscribe(connection_id):, {
        'type': 'welcome',
        'users': [user.nick for user in connected_users]

service.on('subscribe', on_subscribe)

Depending on your application, you might also need a way to broadcast a message to all controllers who are attached to their Bus:

    'type': 'announce',
    'text': "Important Service Announcement"

You can also narrowcast() a message to a subset of controllers:

service.narrowcast("#reversing", {
    'type': 'chat',
    'sender': user.nick,
    'text': "Hello everyone"

This means any controller connection tagged with #reversing will receive that message. Tagging is done like this:

service.tag(connection_id, "#reversing")

Such tags could then be added based on actions, like a controller sending a “join” message to join a channel. They could also be applied based on authentication, so that only connections belonging to a certain GitHub organization receive that message – just as an example.

Lastly, on the cluster side, it is also possible to specify an Access Control List (ACL) when joining the Portal. The ACL is an array of strings that specify tags which will grant controllers access to discover and interact with the given process. This means that service.tag() needs to be used for each controller that should be granted access to a certain node/group of nodes.

That is pretty much all there is to it. For a more comprehensive example, check out examples/ and examples/, which implement an IRC-esque chat service.

System Parameters

Back in May I had a chat with @Hexploitable, who was working on a tool where he needed to pick a Device object based on whether it’s running iOS vs Android. This is a feature that’s been requested in the past, and it felt like it might be time to finally address it.

While one could do device.attach(0) and load a script in the system session, in order to run code inside Frida itself (e.g. in a remote frida-server), it is somewhat tedious. It also doesn’t work if the Device represents a jailed/non-rooted device, where code execution is a lot more constrained.

So after brainstorming this a bit, @Hexploitable started working on implementing it, and quickly got to the point where he had the first draft working. This was later refined by me, and got merged shortly after the Portals feature had finally landed.

The API is simple, and easy to extend in the future:

$ python3 -c 'import frida; import json; \
    print(json.dumps(frida.query_system_parameters()))' \
    | jq

macOS Device

And if I attach a jailed iOS device, I can also query it:

$ python3 -c 'import frida; import json; \
    device = frida.get_usb_device(); \
    print(json.dumps(device.query_system_parameters()))' \
    | jq

iOS Device

An important detail to note here is access: 'jailed'. This is how you can determine whether you’re accessing the device through our support for jailed iOS/Android systems, i.e. limited to debuggable apps, or actually talking to a remote frida-server – which is what access: 'full' means.

Things are not quite as juicy for Android yet (PRs welcome, btw!), but there are still plenty of useful details:

Android Device

We’re also able to identify the specific Linux distro if it’s LSB-compliant:

Ubuntu Device

And last but not least, Windows:

Windows Device

Application and Process Parameters

Another cool idea that started taking shape after some impromptu chats, was when @pancake told me it would be useful to know the particular version of an installed iOS app.

As I had just broken the protocol in so many ways working on the Portals feature, it also seemed like a great time to break it some more, and avoid another painful major bump down the road.

Fast forward a bit, and here’s how it turned out: Our Application and Process objects no longer have any small_icon or large_icon properties, but they now have a parameters dict.

By default, with enumerate_applications(), things look familiar:


But by changing that to enumerate_applications(scope='metadata'), things get a lot more interesting:


Here we can see the iOS Twitter app’s version and build number, where its app bundle is on the filesystem, the containers that it owns, that it is currently the frontmost app, how long ago it was started, etc.

We can also crank that up to enumerate_applications(scope='full') and get icons as well:


The debuggable: true parameter is very useful if query_system_parameters() reported access: 'jailed', as that means your application may want to filter the list of apps to only show the ones it is able to spawn() and/or attach() to, or perhaps show debuggable apps more prominently to provide a better UX.

It’s probably also worth mentioning that get_frontmost_application() now supports passing a scope as well.

Those of you familiar with the old API may have noticed that icons may now be delivered in compressed form, as PNGs. Previously this was always uncompressed RGBA data, and the iOS side would do the PNG decoding and downscaling to two fixed resolutions (16x16 and 32x32).

All of this meant that we would waste a lot of CPU time, memory and bandwidth to include icons, even if all of that data would end up in a CLI tool that doesn’t make use of it. So now with Frida 15 you might notice that application and process listing is a lot faster. And even if you do request icons, it should also be faster than before as we don’t do any decompression and downscaling.

That was application listing. All of the above is also true for process listing, and this is what enumerate_applications(scope='full') might look like now:


Here it is also clear that the Twitter app is currently frontmost, that its parent PID is launchd (PID 1), the user it is running as, when it was started, etc.

You might be wondering why applications is an array though, and the answer is probably best illustrated by an example from Android:


The “” process actually hosts six different “applications”!

And once again, last but not least, I didn’t forget about Windows:


So that’s the “scope” option. There’s also another one, meant for UIs. The idea is that a UI might want to grab the list of applications/processes quickly, and may not actually need metadata/icons until the user interacts with a particular entry, or scrolls a subset of entries into view. So we now provide an option to support such use-cases.

Say we only want to grab the metadata for two specific apps, we can now do:

ids = [
apps = device.enumerate_applications(identifiers=ids,


We also support the same feature for process listing, where it looks like this:

processes = device.enumerate_processes(pids=[1337, 1338],

Portals and Application/Process Parameters

Now that we have covered application parameters and portals, there’s an important detail that’s worth mentioning: Given that it doesn’t make much sense to implement query_system_parameters() in the case of a PortalService, as it’s surfacing processes from any number of (potentially remote) systems, we can use application/process parameters to fill this void.

This means that any Application and Process coming from a PortalService will, if scope is set to metadata or full, provide one parameter named system, which contains the system parameters for that particular application/process. This way an application can still know ahead of time if it’s interested in a particular process.

Jailed iOS and Android Improvements

I had a lot of fun implementing the Application and Process parameters feature, and tried to see how narrow I could make the gap between jailed (non-rooted) and jailbroken (rooted). For example on Android, we didn’t even fetch app labels in the non-rooted code-path. This was because we were relying on running shell commands over ADB, and I couldn’t find a way to grab labels in that case.

The shell command route is very fragile, as most tools output details in a format that’s meant to be consumed by a human, not a machine. And obviously such output is likely to change as Android evolves.

Because of this we now have a tiny prebuilt .dex that we copy over and run, and grabbing metadata is only a matter of making RPC calls to that helper process. This means we are able to provide all the same details for non-rooted as we provide in the rooted case, where we have a frida-server running on the Android side.

Another thing worth mentioning is that we no longer consider Android’s launcher a frontmost app, which means this is now consistent with our behavior on iOS, where SpringBoard is never considered the frontmost app.

As part of these major changes I also added code to fetch icons on Android as well, both non-rooted and rooted, so that this feature is no longer just limited to iOS, macOS, and Windows.

We didn’t provide icons for jailed iOS though, but that feature gap is now also closed. There is however still one difference between jailed and jailbroken iOS: the ppid and user parameters are not available in the jailed case, as this is not exposed by any lockdown/DTX API that I’m currently aware of. But other than that, things are in pretty good shape.

Massively improved backtraces on i/macOS

Thanks to a very exciting pull-request by @hot3eed, we now have an i/macOS symbolication fallback that uses the Objective-C runtime. In this way, instead of showing module!0x1234, we may be able to resolve that to an Objective-C method. Yay!

We also got another awesome contribution by @mrmacete, where NativeCallback now always exposes a context, so you can do Thread.backtrace(this.context) and expect it to work in all cases.

This was previously only possible when NativeCallback was used as an Interceptor replacement. So if you were using ObjC.implement() to swizzle an Objective-C API, you couldn’t actually capture a backtrace from that NativeCallback. So this is a super-exciting improvement!

Unextracted Native Libraries on Android

For those of you using Frida on Android, you may have encountered apps where native libraries don’t reside on the filesystem, but are loaded directly from the app’s .apk. Thanks to a great contribution by @P-Sc, we now support this transparently – no changes needed in your existing instrumentation code.

Upgraded OS Support

We now also support the latest betas of macOS Monterey, iOS 15, and Android 12. Special thanks to @alexhude at Corellium for helping debug and test things on iOS 15, and @pengzhangdev who contributed a fix for frida-java-bridge to support Android 12.

Networked iOS Devices

Another feature that’s been requested a few times is support for networked iOS devices. This is great if you don’t want to destroy your iPhone/iPad’s battery by leaving it plugged in all day. What’s great about this feature is that it “just works” – you should see them if you run frida-ls-devices.

Only two pitfalls worth mentioning: You may now have two different Device objects with the same ID, in case a networked iOS device is reachable through the network while also being plugged in.


$ frida-ls-devices
Id                         Type    Name
-------------------------  ------  -------------------------------------
local                      local   Local System
00008027-xxxxxxxxxxxxxxxx  usb     iPad
socket                     remote  Local Socket
00008027-xxxxxxxxxxxxxxxx  remote  iOS Device [fe80::146f:75af:d79:630c]

So if you’re using -U or frida.get_usb_device() things will work just like before, where you’ll be using your device through USB. But if you want to use the networked device, then resolving it by ID means the USB entry will take precedence, as it’s typically ahead of the networked device in the list of devices.

This means you would also need to check its type. Our CLI tools don’t yet provide a switch to do this, but this would be a welcome pull-request if anyone’s interested!

The second pitfall is that frida-server only listens on the loopback interface by default, meaning we won’t be able to connect to it over the network. So if you’re using our iOS .deb either manually or through Cydia, you will have to edit /Library/LaunchDaemons/re.frida.server.plist to add the --listen switch, and then use launchctl to restart it.

This may also be a situation where you want to make use of the new TLS and authentication features mentioned earlier, depending on how much you trust your network environment.


There’s also a bunch of other exciting changes, so definitely check out the changelog below.


Changes in 15.0.0

  • Introduce PortalService API and daemon, a network service that orchestrates a cluster of remote processes instrumented by Frida. Implements both a frida-server compatible control interface, as well as a cluster interface that agents and gadgets in target processes can talk to. Connected controllers can enumerate processes as if they were local to the system where the portal is running, and are able to attach() and also enable spawn-gating to apply early instrumentation.
  • Add Session.join_portal(), making it easy to share control of a process with a remote PortalService, joining its cluster together with other nodes.
  • Add “connect” interaction to frida-gadget, so it can join a PortalService cluster as well.
  • Add PortalClient, used to implement Session.join_portal() and frida-gadget’s “connect” interaction. Connects to the PortalService and joins its cluster. Implements automatic reconnect in case of transient failures. Also supports specifying an ACL, which is a list of tags that the PortalService must require connected controllers to possess at least one of. It’s up to the application to implement tagging of controllers based on e.g. authentication.
  • Add Device.bus API to allow clients connected to a PortalService to exchange application-specific messages with it. Requires instantiating the service using the API in order to wire up message handlers and protocol logic.
  • Add Session persistence support, enabled by specifying a non-zero “persist_timeout” option when attach()ing to a process. When the server subsequently detects that the client owning the session got disconnected, it will allow scripts to stay loaded until the timeout (in seconds) is reached. Any script and debugger messages emitted in the meantime are queued, and may later be delivered if the client returns before the timeout is reached.
  • Add TLS support, enabled by specifying a certificate. On the server end this is a PEM with a public and private key, where the server will accept any certificate from the client’s side. However for the client this is a PEM with the public key of a trusted CA, which the server’s certificate must match or be derived from.
  • Add authentication support, enabled by specifying a token. The daemons allow specifying a static token through a CLI option, and the APIs allow plugging in a custom authentication backend – which means the token can be interpreted as desired.
  • Move network protocols to WebSocket.
  • Add protocol-level keepalives.
  • Implement WebRTC Data Channel compatible peer-to-peer support, enabled by calling setup_peer_connection() on Session. This allows a direct connection to be established between the client and the remote process, which is useful when talking to it through e.g. a Portal.
  • Optimize protocol by skipping the DBus authentication handshake and telling GDBus not to fetch properties, saving an additional roundtrip.
  • Drop deprecated protocol bits, such as Session.enable_jit().
  • Add Device.query_system_parameters(). Thanks @Hexploitable!
  • Improve the application and process query APIs. (Covered extensively above.)
  • Switch Crash parameter names to kebab-case.
  • Add Session.is_detached(), useful in multi-threaded scenarios where the “detached” signal may already have been emitted by the time we manage to connect to it.
  • Fix Stalker handling of SYSCALL instructions on Linux/x86.
  • Fix the iPad device name on macOS.
  • Massively improve backtraces/symbolication on i/macOS in cases where symbols are missing, but the address to be symbolicated belongs to an Objective-C method. Thanks @hot3eed!
  • Improve backtracer accuracy and flexibility, now exposing a minimal context to NativeCallback in cases where it’s not used as an Interceptor replacement. One such example is ObjC.implement(), used for swizzling APIs. Thanks @mrmacete!
  • Improve Interceptor reliability on macOS/arm64, by switching to the mapping strategy that we use on iOS.
  • Add support for network-connected iOS devices.
  • Avoid leaving behind a file when sandbox check fails on iOS.
  • Fix Stalker on newer iOS hardware jailbroken with checkra1n. This was solved by disabling RWX support on iOS for now: Even if the jailbreak appears to make it available, whether it actually works boils down to which mitigations the hardware supports. Shout-out to @stacksmashing for reporting and helping get to the bottom of this one!
  • Remove bashisms from iOS maintainer scripts, for improved jailbreak compatibility. Thanks @nyuszika7h!
  • Improve Interceptor frame layout on arm64, so backtracing code is able to walk the stack past our generated code. This also makes it easier to use a debugger together with Interceptor.
  • Fix ObjC ApiResolver deadlock, which occurred when free() was resolved lazily from a dyld image callback, at which point it is a bad idea to call dlsym().
  • Add support for unextracted native libraries on Android. Thanks @P-Sc!
  • Fix Android get_frontmost_application() name truncation.
  • Don’t consider the Android launcher a frontmost app.
  • Use the Android app’s label as the process name if the process represents is its main UI process. This is finally consistent with what we do on iOS.
  • Improve jailed Android injector: Now using a long-lived shell session to speed things up. Also ensure filenames dropped into /data/local/tmp are unique, and clean up temporary files.
  • Drop Firefox OS support.
  • Rename the weak ref API to avoid clashing with ES2021. So instead of WeakRef.bind() and WeakRef.unbind(), these are now Script.bindWeak() and Script.unbindWeak().
  • Massively improve memory allocation performance on Windows, by lowering the dlmalloc spin-lock sleep duration to zero – which means it will only yield the remainder of its time slice. The default of 50 ms is prone to introduce significant delay as soon as threads start competing for the lock.
  • Lazily create the ScriptScheduler thread pool. This means anyone using GumJS can avoid background threads until they’re really needed. Very useful if you’re writing a tool or agent that needs to handle fork(), without having to implement logic to stop and later restart threads. (Something frida-agent does, but which isn’t necessarily needed for simple use-cases.)
  • Java: Add support for Android 12 beta. Thanks @pengzhangdev!
  • Java: Fix Java.array() for unloaded array types: If the type of the array was not used in the application it means that it wasn’t loaded into memory, and Java.array() previously failed because of this. Thanks @yotamN!
  • Java: Add toString() to primitive arrays, so that instead of the generic “[Object Object]” string it will generate a string where the array values are separated by commas. Thanks @yotamN!
  • CModule: Add missing TinyCC builtins for 32-bit x86.
  • python: Add Script.is_destroyed property.
  • python: Add Session.is_detached property.
  • python: Add Device.is_lost property.
  • python: Throw when attempting an RPC call on a destroyed script.
  • node: Fix compatibility with newer versions of Node.js, where any given Buffer’s backing store needs to be unique. We solve this by simply making a copy in cases where we cannot guarantee that the same buffer can only be observed once.
  • node: Fix use-after-free in signal transform callbacks.
  • node: Fix use-after-free in signal connection callbacks.
  • node: Move to C++17, for compatibility with newer Node.js headers.
  • node: Add Script.isDestroyed property.
  • node: Add Device.isLost property.

Changes in 15.0.1

  • Ensure DarwinGrafter’s merging of binds doesn’t make gaps in __LINKEDIT. Not doing so triggers a bug in codesign for which the resulting signed binary turns out corrupted. Thanks @mrmacete!
  • node: Fix compilation error when building with MSVC.

Changes in 15.0.2

  • Fix handling of large messages, for both client-server and p2p. Also bump the WebSocket payload size limit to 256 KiB - same as is typically negotiated for data channels in p2p mode.
  • Move WebSocket I/O to the DBus thread, to avoid unnecessary thread hopping.
  • Plug leaks when using p2p.

Changes in 15.0.3

  • Implement a new frida-pipe strategy for i/macOS. Turns out that our previous strategy of directly setting up a Mach port in the target process becomes problematic with guarded Mach ports. So instead we register a Mach service that’s globally visible, and have the target process contact it to fetch its end of the socketpair. We also check with the sandbox up front, and issue an extension token if needed. However, if we’re unable to register the Mach service with launchd, we fall back to our previous strategy.
  • Skip iOS platformized detection on iOS >= 15. It interacts badly with guarded Mach ports.
  • Port early instrumentation to macOS 12 and iOS 15.
  • Gracefully handle agent failing to start. Instead of crashing the target process, simply log the error and unload.
  • Linux: Use an abstract name for frida-pipe when supported.
  • Unlink UNIX socket when frida-pipe is done with it.
  • Fix iOS policyd Mach message lifetime logic.

Changes in 15.0.4

  • Fix the i/macOS injector’s data size logic. It’s been hard-coded since the beginning, and the recent entrypoint data size adjustment broke it on systems with 4K pages.
  • Fix i/macOS early instrumentation regression on dyld < 4.

Changes in 15.0.5

  • Fix the __CFInitialize() code-path on dyld >= 4.
  • Fix i/macOS sandbox extension logic during spawn().
  • Port jailed iOS injector to iOS 15.
  • Implement Android USAP interop.
  • Improve DarwinGrafter lazy binds merge logic. Thanks @mrmacete!

Changes in 15.0.6

  • Fix Windows build regression.

Changes in 15.0.7

  • Fix early instrumentation on Android 12.
  • node: Fix the ApplicationParameters typings. Thanks for reporting, @pancake!

Changes in 15.0.8

  • iOS: Add support for unc0ver v6.1.2.
  • iOS: Update Substrate interop logic to support 0.9.7113.

Changes in 15.0.9

  • iOS: Revive support for older OS versions. (Verified on iOS 10.3.)
  • iOS: Fix support for older Apple usbmuxd versions.
  • Android: Handle devices where jailed is unsupported.
  • Fix DarwinGrafter alignment issue, aligning to 16 bytes also when lazy binds follow regular binds. Thanks @mrmacete!

Changes in 15.0.10

  • Fix regression where Device.open_channel(“lockdown:”) ended up closing the stream right away.
  • Fix crash when talking to older versions of ADB.

Changes in 15.0.11

  • Rewrite macOS spawn gating to use DTrace. This means we’ve now dropped support for our kernel extension, and we don’t need to worry about future OSes no longer supporting extensions. It also means we finally support spawn gating on Apple Silicon as well.
  • Work around i/macOS arm64 single-step delay during early instrumentation.

Changes in 15.0.12

  • Fix macOS spawn gating task port lifetime issue. Should not try to be clever and keep task ports around – bad things happen if a task port is used after an exec transition.
  • Remove the i/macOS task port caching logic. It is dangerous in exec transitions, adds some complexity, and doesn’t help all that much performance-wise, anyway.
  • Make the macOS spawn gating DTrace predicate environment variable optional.
  • Improve the macOS spawn gating error message.

Changes in 15.0.13

  • Improve clients to specify Host header and use TLS SNI.
  • Revert i/macOS single-step delay workarounds.
  • iOS: Fix usbmux port number encoding in the connect request.
  • Python, Node.js: Fix ownership in Device.spawn() aux options logic.

Changes in 15.0.14

  • Port iOS crash reporter integration to iOS 14.7.1.
  • Configure internal agents to be less intrusive on i/macOS.
  • Improve i/macOS error-handling during process preparation.
  • Revive i/macOS single-step delay workarounds, which turned out to be needed after all.
  • Handle reentrancy when about to yield JS lock, which may happen if a replaced function gets called by Interceptor during end_transaction().
  • Fix conditional Interceptor unignore logic in GumJS. This was preventing Interceptor from ignoring internal calls, and resulted in noise and reduced performance.
  • Enhance the i/macOS DebugSymbol fallback name to also include the unslid address. This makes it easy to plop it into a static analysis tool.
  • Fix Stalker code slab refill logic.
  • Add Stalker stats interface.

Changes in 15.0.15

  • Rework i/macOS Exceptor to support latest iOS 14. This would previously result in a deadlock whenever a native exception occurred.

Changes in 15.0.16

  • Fix i/macOS single-step handling on ARM during early instrumentation, which would result in attach() failing randomly for a freshly spawn()ed process. Thanks @mrmacete!
  • Add Stalker backpatch prefetching support.
  • Make Stalker inline cache size configurable on x86.
  • Optimize Stalker x86 return handling.
  • ObjC: Allow proxies to implement methods. Thanks @hot3eed!

Changes in 15.0.17

  • gadget: Support packaging as framework on i/macOS. Load config from “Resources/config.json”, and resolve relative paths relative to the same directory. As an added bonus, we now also support specifying relative paths for “certificate” and “asset_root”.
  • web-service: Implement basic directory listing with NGINX-style output. This is useful with e.g. “frida-server –asset-root=/”.
  • stalker: Fix backpatch args for 32-bit x86.

Changes in 15.0.18

  • Plug long-standing memory leaks, both in our internal heap’s realloc behavior being misconfigured and causing leaks, and in how we register JavaScript classes with QuickJS. Kudos to @mrmacete for discovering and helping track down these long-standing bugs!

Changes in 15.0.19

  • gadget: Fix framework resource lookup logic on iOS.

Frida 14.2 Released

So much to talk about. Let’s kick things off with a big new feature:


Frida has supported Android for quite a while, but one particular feature has kept getting requested (mostly) by users who thought they were looking at a bug. The conversation usually started something like: “I’m using Frida inside the hardware-accelerated Android emulator X, and when I attach to this process Y, Process.enumerateModules() is missing JNI library Z. But I can see it in Process.enumerateRanges() and /proc/$pid/maps. How come?”

As you may have guessed, we’re talking about Android’s NativeBridge, typically used on Intel-powered Android devices to enable them to run apps that only support ARM – i.e. apps with one or more JNI components only built for ARM.

In a Frida context, however, we’re usually talking about a VirtualBox-based emulator that runs an Android system built for x86. This system then ships with NativeBridge support powered by libhoudini, a proprietary ARM translator.

There’s quite a few of these emulators, e.g. BlueStacks, LDPlayer, NoxPlayer, etc. While the ones mentioned are optimized for running games, there’s now also Google’s official Android 11 AVDs which ship with NativeBridge support out of the gate.

Through the years I’ve been thinking about how we could support such scenarios in Frida, but thinking about it always made my head hurt a little. It did feel like something we should support at some point, though, I just had a hard time figuring out what the API would look like.

Then along came 2020 and Apple announced their transition to ARM, and suddenly Rosetta became relevant once again. “Alright”, I thought, “now we have two platforms where it would be useful to support processes containing an emulated realm that’s running legacy code.”

And yeah there’s also Windows, but we don’t yet support Windows on ARM. We totally should though, so if somebody’s interested in taking a stab at this then please do get in touch.

Anyway, I’m exited to announce that our Android binaries for x86 and x86_64 now support such processes out of the box. You may already be familiar with the following frida-core API, where the Python flavor looks like this:

session = device.attach(target)

(Or frida.attach() if your code only deals with the local system.)

If target has an emulated realm, you can now do:

session = device.attach(target, realm='emulated')

The default is realm='native', and you can actually use both realms at the same time. And when using our CLI tools, pass --realm=emulated to act on the emulated realm.

One important caveat when using this on Android is that you will need to apply your Java-level instrumentation in the native realm.

Lastly it’s worth noting that this new feature is only supported on Android for now, but it shouldn’t be hard to support Rosetta on macOS down the road. Definitely get in touch if you want to help out with this.

Taking Android Java Hooking Inline

The way Frida’s Java bridge replaces Java methods on Android has up until now been accomplished by mutating the in-memory method metadata so that the target method becomes native – if it wasn’t already. This allows us to install a NativeCallback that matches the given method’s JNI signature.

This has presented some challenges, as the ART runtime does have other internal state that depends on the given method’s personality. We have devised a few hacks to dance around some of these issues, but some particularly gnarly edge-cases remained unsolved. One such example is JIT profiling data maintained by the ART VM.

An idea I had been thinking about for a while was to stop mutating the method metadata, and instead perform inline hooking of the AOT-generated machine code – for non-native methods that is. That still leaves methods run on the VM’s interpreter, but the assumption was that we could deal with those by hooking VM internals.

I took a stab at an early prototype to explore this approach further. It seemed like it could work, but there were still many challenges to work through. After some brainstorming with @muhzii, he kept working on evolving this rough PoC further in his spare time. Then one day I almost fell off my chair out of pure excitement when I saw the amazing pull-request he had just opened.

Thanks to Muhammed’s amazing work, you can now all enjoy a much improved Java instrumentation experience on Android. This means improved stability and also that direct calls won’t bypass your replacement method. Yay!


For those of you using Java.deoptimizeEverything() on Android to ensure that your hooks aren’t skipped due to optimizations, there’s now a more granular alternative. Thanks to @alkalinesec’s neat contribution to our Java bridge, you can now use Java.deoptimizeBootImage(). It ensures only code in the boot image OAT files gets deoptimized. This is a serious performance gain in some situations where the app code itself is slow when deoptimized, and it is not necessary to deoptimize it in order for hooks to be hit reliably.


Another really exciting update here. The next hero in our story is @mephi42, who started porting Frida to S390x. Our CModule implementation relies on TinyCC behind the scenes, and it doesn’t yet support this architecture. The system might have a C compiler though, so @mephi42 proposed that we add support for using GCC on systems where TinyCC cannot help us out.

I really liked this idea. Not only from the perspective of architecture support, but also because of the potential for much faster code – TinyCC optimizes for small compiler footprint and fast compilation, not fast code.

So needless to say I got more and more excited with each pull-request towards GCC support. Once the last one landed it inspired me to add support for using Apple’s clang on i/macOS.

In the end we arrived at this:

const cm = new CModule(`…`, {}, { toolchain: 'external' });

Where toolchain is either any, internal, or external. The default is any, which means we will use TinyCC if it supports your Process.arch, and fall back to external otherwise.

The story doesn’t end here, though. While implementing support for i/macOS, it wasn’t really clear to me how we could fuse in symbols provided by the JavaScript side. (The second argument to CModule’s constructor.)

The GCC implementation uses a linker script, which is a really elegant solution that Apple’s linker doesn’t support. But then it hit me: we already have our own dynamic linker that we use for our injector.

Once I had wired that up, it seemed really obvious that we could also trivially support skipping Clang entirely, and allow the user to pass in a precompiled shared library.

The thinking there was that it would enable cross-compilation, but also make it possible to implement a CModule in languages such as Swift and Rust: basically anything that can interop with C.

So this means we now also support the following:

const cm = new CModule(blob);

Where blob is an ArrayBuffer containing the shared library to construct it from. For now this part is only implemented on i/macOS, but the goal is to support this on all platforms. (Contributions welcome!)

Also, as of frida-tools 9.2, the REPL’s -C switch also supports this, making it easy to use an external toolchain without missing out on live-reload – which makes for a much shorter feedback loop during development.

Taking that one step further, the CModule API now also provides a the property CModule.builtins, which scaffolding tools can use to obtain the built-in headers and preprocessor defines.

And on that note we now have such a tool in frida-tools:

$ mkdir pewpew
$ cd pewpew
$ frida-create cmodule
Created ./
Created ./pewpew.c
Created ./.gitignore
Created ./include/glib.h
Created ./include/gum/gumstalker.h
Created ./include/gum/gumprocess.h
Created ./include/gum/gummetalarray.h
Created ./include/gum/guminterceptor.h
Created ./include/gum/gumspinlock.h
Created ./include/gum/gummetalhash.h
Created ./include/gum/gummemory.h
Created ./include/gum/gumdefs.h
Created ./include/gum/gummodulemap.h
Created ./include/json-glib/json-glib.h
Created ./include/gum/arch-x86/gumx86writer.h
Created ./include/capstone.h
Created ./include/x86.h
Created ./include/platform.h

Run `meson build && ninja -C build` to build, then:
- Inject CModule using the REPL: frida Calculator -C ./build/pewpew.dylib
- Edit *.c, and build incrementally through `ninja -C build`
- REPL will live-reload whenever ./build/pewpew.dylib changes on disk

$ meson build && ninja -C build
[2/2] Linking target pewpew.dylib
$ frida Calculator -C ./build/pewpew.dylib

And yes, it live-reloads! Taken to the extreme you could use a file-watcher tool and make it run ninja -C build whenever pewpew.c changes – then just save and instantly see the instrumentation go live in the target process.

It’s worth noting that you can also use the above when using the internal CModule toolchain, as having the headers available on disk is handy for editor features such as code completion.


There’s also a bunch of other exciting changes, so definitely check out the changelog below.


Changes in 14.2.0

  • Brand new realms API for instrumenting emulated realms inside native processes. Only implemented on Android for now.
  • Add Java.deoptimizeBootImage(). Thanks @alkalinesec!
  • Add –disable-preload/-P to frida-server. Useful in case of OS compatibility issues where Frida crashes certain OS processes when attaching to them.
  • Fix libc detection on older versions of Android.
  • Fix crash when resolving export of the vDSO on Android. Thanks @ant9000!
  • Restore support for libhoudini on Android.
  • Fix ARM cache flushing on Android 11’s translator.
  • Fix linker offsets for Android 5.x. Thanks @muhzii!
  • Start refactoring CModule’s internals to prepare for multiple backends. Thanks @mephi42!
  • Fix CModule aggregate initializations on ARM.
  • Fix ModuleApiResolver fast-path emitting bad matches.

Changes in 14.2.1

  • Fix CModule constructor error-path in the V8 runtime.
  • Use V8 runtime for the “system_server” agent on Android.

Changes in 14.2.2

  • Fix Darwin.Mapper arm64e handling of pages without fixups. This went unnoticed out of pure “luck”, until our binaries eventually mutated sufficiently to expose this bug.

Changes in 14.2.3

  • Upgrade to using inline hooking for the ART runtime. Thanks @muhzii!
  • Fix direct transport regression on i/macOS, introduced by GLib upgrade where GLib.Socket gained GLib.Credentials support on Apple OSes. A typical symptom of this regression is that frida-server gets killed by Jetsam.
  • Fix libffi support for stdcall, thiscall, and fastcall on 32-bit Windows.
  • Extend Memory.alloc() to support allocating near a given address. Thanks @muhzii!
  • Fix relocation of RIP-relative indirect branches on x86_64. Thanks @dkw72n!
  • Improve the JVM C++ allocator API probing logic by consulting debug symbols before giving up. Thanks @Happyholic1203!
  • Upgrade SELinux libraries to support bleeding edge Android systems.
  • Add gum-linux-x86_64-gir target for GIR generation. Thanks @meme!

Changes in 14.2.4

  • Fix Android performance regression when ART’s interpreter is used, such as when using deoptimizeEverything() or deoptimizeBootImage(), which results in our JS callbacks becoming extremely hot. Move the hot callbacks to CModule to speed things up.
  • Fix V8 debugger support in Node.js bindings on Linux.
  • Fix crash on ELF init error in the libdwarf backend.

Changes in 14.2.5

  • Fix regression on older Android systems, introduced in 14.2.4.

Changes in 14.2.6

  • Fix compatibility with legacy NativeBridge v3 and newer, where a namespace needs to be specified.

Changes in 14.2.7

  • Fix frida-java-bridge crash on systems where printf() renders %p without “0x” prefix.
  • Fix jni_ids_indirection_ offset parsing on ARM64. Thanks @muhzii!

Changes in 14.2.8

  • Fix GLib SO_NOSIGPIPE regression on i/macOS. This would typically result in frida-server dying due to SIGPIPE. Thanks @mrmacete!
  • Refactor CModule internals and lay foundations for GCC backend. Thanks @mephi42!
  • Add EventSink.make_from_callback() for Stalker C API consumers that only care about events, and don’t need lifecycle hooks or code transformations.
  • Emit Stalker BLOCK event at the start of the block, as this is what’s the most intuitive, as one would expect at least as many BLOCK events as COMPILE events. This behavior is also the most suitable for measuring coverage.
  • Add Stalker prefetch support, useful for optimizing “AFL fork server”-like use-cases.

Changes in 14.2.9

  • Handle permanent entries in Darwin CodeSegment backend. Starting from iOS 14.3 on A12+ devices, mach_vm_remap() can return KERN_NO_SPACE when the target VM map entries are marked as “permanent”. Thanks @mrmacete!
  • Wire up GCC support in CModule. Thanks @mephi42!
  • Add CModule backend for Clang on Apple OSes.
  • Add support for linking in a prebuilt CModule. (Only on i/macOS for now.)
  • Finalize the CModule toolchain selection API.
  • Add CModule.builtins property for tooling support.
  • Generate frida-core GIR by default. Thanks @meme!
  • Fix regressions on Linux/MIPS.

Changes in 14.2.10

  • Improve frida-inject to support bidirectional stdio.
  • Add support for Termux in frida-python: pip install frida-tools now works.

Changes in 14.2.11

  • Improve frida-inject to support raw terminal mode.
  • Add internal policy daemon for Darwin.
  • Improve Gum.Darwin.Mapper to support strict kernels.

Changes in 14.2.12

  • Fix ART method hooking reliability after GC. Thanks @muhzii!

Changes in 14.2.13

  • Fix Instruction operands parsing on x86, ensuring the immediate value is always represented by an Int64 and never a number. Thanks @muhzii!
  • Fix frida-inject when process is not attached to a terminal. Thanks @muhzii!
  • Expose Base64 and Checksum GLib primitives to CModule. Thanks @mrmacete!

Changes in 14.2.14

  • Fix Gadget crash on i/macOS when loaded early.
  • Make frida-inject stdin communication optional. Thanks @muhzii!
  • Support iOS app spawn on unc0ver 6.x. Thanks @mrmacete!
  • Work around single-step delay when spawning iOS apps, to avoid failing randomly. Thanks @mrmacete!
  • Fix read() signature mismatch in the libc shim, which would result in a compilation error on newer Apple toolchains. Thanks @Manouchehri!
  • Fix Android enumerate_applications() name truncation on newer versions of Android. Kudos to @pancake for reporting and helping figure this one out!
  • Fix hang when target is unable to load frida-agent.
  • Fix support for attaching to Windows services.
  • Clean up stale Windows services before registering new ones.
  • Add build option to support using installed assets instead of embedding them.
  • Update iOS packaging to use installed assets.
  • Add basic support for jailed Android. Thanks @enovella_ for all the fun and productive pair-programming on this one!
  • Extend Arm64Writer API to support more immediates.
  • Improve Stalker to support temporarily misaligned stack on arm64.
  • Fix crash in Stalker follow() without a sink.
  • Implement Stalker invalidation support. This allows updating the instrumentation without throwing away all of the translated code. Thanks for the assist on this one, @p1onk!
  • Add Gum.DarwinModule.enumerate_function_starts().
  • Add Gum.DarwinGrafter for AOT grafting to be able to prepare binaries so they can be instrumented when runtime code modification isn’t possible. Thanks for the assist, @mrmacete!
  • Add Memory.allocate_near().
  • Improve Stalker performance and robustness on all supported architectures:
    • Improve call probes to probe at the target instead of at the call site, and take advantage of the new invalidation infrastructure.
    • Handle adding/removing call probes from within call probes.
    • Refactor callout-handling so user data can be destroyed on invalidate. This also eliminates the callout lock.
    • In case of self-modifying code, recompile instead of allocating a new block.
    • Use separate slabs for code and data, to avoid cases where we only use part of the slab because we run out of metadata storage.
    • Inline the first code/data slab in ExecCtx, so we use a lot less memory when following threads that either don’t touch much code, or none at all in case they don’t wake up.
    • Don’t bother storing original code when trust_threshold is 0.
    • Simplify Stalker block metadata to reduce the per-block memory consumption.
  • Fix result of Java.enumerateMethods() on ART. This was a bug where static initializer methods were being included in the enumerated set as ‘$init’ when they should be skipped altogether. Thanks @muhzii!
  • Fix the Android/ART near memory allocation code path used during Java method hooking. Thanks @muhzii!
  • Fix handling of generic Java array types. This allows array objects obtained from the runtime to be reused later when marshalling array types, which is necessary to preserve type information particularly in cases where the type is dynamic. Thanks @muhzii!
  • Port Android/ART StackVisitor to x86, x64, and ARM32. Thanks @P-Sc!
  • Fix ARM cache flushing on Android. Turns out cacheflush() expects a range on Linux/ARM. This 32-bit Android/ARM regression was introduced in 14.2.0.
  • Add a few missing TinyCC builtins for 32-bit ARM. Kudos to @giantpune for reporting and helping figure this one out!
  • Fix wrap-around in Stalker block recycling logic.
  • Fix V8 NativePointer construction from large numbers.
  • Fix Stalker local thread actions on Windows.
  • Fix the CModule temporary directory cleanup logic.
  • Remove forgotten InspectorServer debug code.
  • Fix the V8 debugger integration. Kudos to @taviso for reporting!

Changes in 14.2.15

  • Fix compatibility with latest unc0ver iOS jailbreak. Thanks @mrmacete!
  • Add support for Anbox. Thanks @asabil!
  • Add Java.deoptimizeMethod(). Thanks @liuyufei!
  • Handle replacing ART methods that may be devirtualized. Thanks @liuyufei!

Changes in 14.2.16

  • Add many missing TinyCC builtins for 32-bit ARM. Kudos to @giantpune for reporting and helping figure this one out!
  • Fix Android ART trampoline alignment when using ADRP on arm64, previously resulting in Error: invalid argument exception being thrown when attempting to replace some methods. Kudos to @pandasauce for reporting and helping figure this one out!

Changes in 14.2.17

  • Enumerate Darwin imports from chained fixups, to support the latest arm64e binaries. Thanks @mrmacete!
  • Fix chained fixups handling in the jailed iOS injector. Thanks @mrmacete!
  • qml: Compile with no_keywords for GLib compatibility. Thanks @suy!

Changes in 14.2.18

  • Fix i/macOS injector on recent XNU versions, where mach_port_extract_right() fails with KERN_INVALID_CAPABILITY when trying to steal the target process’ POSIX thread port send right. This resulted in the injector assuming that we’d uninjected, subsequently deallocating memory still in use.
  • Fix ___error symbol name in the jailed iOS injector. Thanks @mrmacete!
  • Fix enumeration of modules with spaces on path in the Linux backend. Thanks @suy!
  • Fix Stalker handling of direct branch addresses on x64.
  • python: Add RPC exports listing functionality. Thanks @NewbieGoose!

Frida 14.1 Released

Lots of goodies this time! 🎉 Let’s dive in.


We’ve just upgraded all of our dependencies to the latest and greatest. Part of this work included refurbishing the build system bits used for building them.

With these improvements we will finally support building past versions of Frida fully from source, which has been a long-standing issue that’s caused a lot of frustration.

It is now also a whole lot easier to tweak our dependencies, e.g. while debugging an issue. Say you’re troubleshooting why Thread.backtrace() isn’t working well on Android, you might want to play around with libunwind’s internals. It is now really easy to build one specific dependency:

$ make -f FRIDA_HOST=android-arm64 libunwind

Or if you’re building it for the local system:

$ make -f libunwind

But you might already have built Frida, and want to switch out libunwind in the prebuilt SDK that it is using. To do that you can now do:

$ make -f symlinks-libunwind

You can then keep making changes to “deps/libunwind”, and perform an incremental compilation by re-running:

$ make -f libunwind


We now support iOS 14.2. It was kinda already working, but our crash reporter integration would deadlock Apple’s crash reporter, and this isn’t great for system stability overall.

GumJS support for size_t and ssize_t

Thanks to @mame82 we finally support “size_t” and “ssize_t” in APIs such as NativeFunction. This means that your cross-platform agents no longer need to maintain mappings to the native types that these correspond to. Yay!

System GLib support

Gum can finally be built with the upstream version of GLib, and we now support generating GObject introspection definitions. This paves the way for future language bindings that are fully auto-generated.

Kudos to @meme for these awesome improvements!

Windows inprocess injection

Our Windows backend finally supports inprocess injection. By this I mean that in the most common cases where the target process’ architecture is the same – and no elevation is needed – we can now avoid writing out “frida-helper-{32,64}.exe” to a temporary directory and launching it before we’re able to attach() to a given target. As an added bonus this also reduces our startup time.

The motivation behind this improvement was to fix a long-standing issue where some endpoint security products would prevent our injector from working, as our logic was prone to trigger false positives in such software. We will still obviously run into such issues when we do need to spawn our helpers, but there’s now a good chance that the most common use-cases will actually work.

Stalker ARM improvements

For those of you using Stalker on 32-bit ARM, it should now be working a whole lot better than ever before. A whole slew of fixes landed in this release.

Bytecode and frida-tools

One of the realizations since 14.0 was released is that QuickJS’ bytecode format is a lot more volatile than expected. Because of this I would caution against using “frida-compile -b” unless your application is designed to only be used with one exact version of Frida.

As I wasn’t aware of this pitfall when cutting the previous release of frida-tools, I opted to precompile the frida-trace agent to bytecode. Upon realizing my mistake while working on releasing 14.1, I reverted this mistake and released a new version of frida-tools.

So make sure you also grab its latest release while upgrading:

$ pip3 install -U frida-tools


There’s also a bunch of other exciting changes, so definitely check out the changelog below.


Changes in 14.1.0

  • All dependencies upgraded to the latest and greatest.
  • Heavily refurbished build system for dependencies. Going forward we will finally support building past versions of Frida fully from source.
  • Port iOS crash reporter integration to iOS 14.2.
  • Fix error propagation when talking to iOS devices.
  • Add support for “size_t” and “ssize_t” in GumJS. Thanks @mame82!
  • Support linking against system GLib and libffi. Thanks @meme!
  • Support GObject Introspection. Thanks @meme!
  • Improve Windows backend to support inprocess injection. This means we can dodge common AV heuristics and speed things up in the most common cases where the target process’ architecture is the same, and no elevation is needed.
  • Fix Stalker ARM handling of “ldr pc, [sp], #4”.
  • Fix Stalker ARM clobbering of flags in IT blocks.
  • Fix Stalker ARM handling of CMN/CMP/TST in IT blocks.
  • Fix suppression flags for ThumbWriter instruction.
  • Fix Stalker ARM exclusion logic reliability.
  • Fix ARM Stalker follow() of thread in Thumb mode.
  • Fix ARM Stalker SVC handling in Thumb mode.
  • Fix ThumbRelocator handling of unaligned ADR.
  • Move Stalker ARM to runtime VFP feature detection.
  • Refuse to Interceptor.attach() without any callbacks.
  • Improve GumJS error message formatting.
  • Fix starvation in the V8 debugger integration.
  • Keep NativeCallback alive during calls on V8 also.

Changes in 14.1.1

  • Fix CModule regression where Capstone went missing.
  • Add missing CModule builtins for 32-bit ARM.
  • Fix Thread.backtrace() on Android/ARM64.

Changes in 14.1.2

  • Fix Thread.backtrace() on Android/ARM.

Changes in 14.1.3

  • Fix ObjC.choose(). The TinyCC upgrade in 14.1.0 exposed an existing bug.
  • Re-enable V8 by default. Turns out we have use-cases where it is a much better fit than QuickJS, and its debugger features were also being sorely missed.
  • Add –ignore-crashes/-C to frida-server for disabling the native crash reporter integration. For use-cases where the integration is undesired, or when running on bleeding edge OS versions that we don’t yet fully support. (Crash reporter integration is only available on iOS and Android for now.)
  • Enhance devkits to ensure Capstone APIs are exposed on all platforms.
  • Improve devkit examples.

Frida 14.0 Released

Here’s a major new release that took weeks of intense coding, with way too many cups of coffee. But before we dive into it, we need to have a quick look down memory lane.

For years now our V8-based runtime has served us well. But eventually we needed to support constrained systems where V8 isn’t a great fit, so we introduced a second runtime.

This has worked out nicely, but there were some trade-offs we were left with:

  • Language feature support being wildly different between the two runtimes. We tried to alleviate some of this by making the minimalistic runtime be the default, as it’s available everywhere and is the lowest common denominator in terms of features.
  • Needing to sacrifice performance when using a tool such as frida-compile to compile modern JavaScript to old JavaScript that runs on both runtimes.
  • Non-trivial agents with lots of code and data floating around make it clear not just how fast V8 is – no surprise there – but that it’s really good at packing objects to avoid wasting precious RAM. And to widen the gap between the two runtimes even more, V8 can run the modern JavaScript as-is and doesn’t need to run a bloated version that contains compatibility shims to fill in the missing runtime bits such as Map and Set.
  • Example code and documentation tends to look arcane to avoid confusing users who might try to run modern code on the default runtime.
  • Garbage collector implementation differences may hide the user’s bugs in one runtime that instantly blow up in the other where resources are released way more eagerly. One such example is failing to keep a NativeCallback alive while external code is still using it.
  • Terrible UX: All of the above is a very frustrating and confusing story to tell our users.
  • New features and refinements need to be implemented twice. This is a real pain for me as a maintainer for obvious reasons.

Fast-forward to 2019 and QuickJS caught my eye. I was really busy with other things at the time, though, so by the time I looked closer at it I noticed it supports ES2020, and also performs impressively well for an interpreter.

But as I started thinking about bringing up a new runtime from scratch, and seeing as the other two are roughly ~25 KLOC each, it just felt overwhelming.

I kept coming back to the QuickJS website though, devouring the technical details, and even started reading deeper into the public API at some point.

Then I noticed that it didn’t support cooperative multi-threaded use, where multiple threads execute JavaScript in lockstep. This made the mountain of work ahead feel even more daunting, but then I remembered that I’d already contributed support for this in Duktape, and it wasn’t that hard.

Eventually I mustered up the courage. Picked a super-simple test from GumJS’ extensive test-suite as my first challenge, and went ahead and copy-pasted the ScriptBackend and Script implementations from the youngest of the existing two runtimes. First renaming things, then stubbing out all of the modules (Interceptor, Stalker, etc.), just wanting to get a near-empty “shell” to compile and run.

At this point I was hooked and couldn’t stop. Lots of coffee was consumed, and before I knew it I’d gotten the core bits and the first module implemented. Then another, and then one more.

After working quite a bit with the QuickJS API, and jumping around its internals to make sure I understood the reference counting rules etc., it suddenly seemed really clear what was needed to implement the cooperative multi-threading API that would be needed to make this a real runtime and not just a toy.

What we need to be able to do is suspend JS execution while calling out to a NativeFunction. This is because the called function may block waiting for a lock which another thread might already be holding, but that other thread may have just called a hooked function and is waiting to enter the JS runtime. So if we didn’t let go of the JS lock before calling the NativeFunction, we’d now be in a deadlock.

Another use-case is calling Thread.sleep() or some other blocking API where we’d cause starvation if we did that while holding the JS lock.

Anyway, the QuickJS multi-threading API turned out to be straight-forward, so from there I kept on going, until it was all finally done! 🎉

At this point I was really curious about the performance of this brand new runtime, starting with the question of what it costs to enter and leave it.

Went ahead and took it for a spin on an iPhone 6, running the GumJS test that uses Interceptor to hook a nearly empty C function, supplying an empty JS callback, and then measures the wall-clock time spent on each call as it keeps calling the C function over and over.

The idea is to simulate what would happen if the user hooks a function that’s called frequently, to get an idea of the base overhead.

Here’s what I got:

# QuickJS
<min: 1.0 us, max: 7.0 us, median: 2.0 us> ok 1 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#QJS
<min: 2.0 us, max: 54.0 us, median: 2.0 us> ok 2 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#QJS
<min: 3.0 us, max: 18.0 us, median: 3.0 us> ok 3 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#QJS
# Duktape
<min: 2.0 us, max: 8.0 us, median: 3.0 us> ok 4 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#DUK
<min: 2.0 us, max: 6.0 us, median: 3.0 us> ok 5 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#DUK
<min: 4.0 us, max: 89.0 us, median: 4.0 us> ok 6 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#DUK
# V8
<min: 13.0 us, max: 119.0 us, median: 14.0 us> ok 7 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#V8
<min: 15.0 us, max: 127.0 us, median: 16.0 us> ok 8 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#V8
<min: 26.0 us, max: 198.0 us, median: 28.0 us> ok 9 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#V8

Wow, so that was looking promising! How about baseline memory usage, i.e. how much memory is consumed by one instance of the runtime itself?

QJS Memory Baseline

That’s quite an improvement – only one fifth of the previous runtime!

The next thing I was curious about was the approximate initial size of Frida’s internal heap when using our REPL. That includes all of the memory used by frida-agent, the JS runtime, and the REPL agent that was loaded:


Yay, 1 MB freed up for other purposes!

So with that, I hope you’re as excited as I am about this new release. We’ve replaced our previous default runtime with this brand new one built on QuickJS.

And as an experiment I have also decided to build our official binaries without our V8 runtime. This means that the binaries are way smaller than they’ve ever been before.

I do realize that some of you may have use-cases where the V8 runtime is essential, so my hope is that you will take the new QuickJS runtime for a spin and let me know how it works for you. If it’s an absolute disaster for your particular use-case then don’t worry, just let me know and we will figure something out.

If you’d like to build Frida yourself with the V8 runtime enabled, it’s only a matter of tweaking this line. But please do let me know if you can’t live without it, so we can decide on whether we need to keep supporting this runtime down the road.

The only other change in this major release applies to i/macOS, where we’re finally following Apple’s move to drop support for 32-bit programs. We’ll keep the codepaths around for now though, but our official binaries have a lot less fat, and the top-level build system is also a bit slimmer. E.g. make core-macos-thin is now just make core-macos.

That’s all in Frida itself, but there’s more. We’ve also released frida-tools 9.0, freshly upgraded to make use of modern JavaScript features everywhere. That includes frida-trace, where the generated boilerplate hooks have become a lot more readable after some syntax upgrades. Last but not least we have also released frida-compile 10.0, where the Babel dependencies are gone and so are the corresponding command-line switches; it’s faster and so much simpler.

So with that, I hope you’ll enjoy this new release!

Changes in 14.0.0

  • Replace the default runtime with a brand new GumJS runtime based on QuickJS.
  • Disable V8 by default.
  • Retain callback object in Interceptor.attach() on V8.
  • Drop “enumerate” trap from the global access API.

Changes in 14.0.1

  • QJS: Fix nested global access requests.
  • qml: Update to the new frida-core API.

Changes in 14.0.2

  • QJS: Keep NativeCallback alive during calls.
  • QJS: Speed up the NativeCallback construction logic.
  • QJS: Disable stack limitation for now.
  • iOS: Port the iOS crash reporter integration to iOS 14.
  • iOS: Remove packaging logic for 32-bit.
  • Android: Use the default runtime for the “system_server” agent.
  • Modernize internal JavaScript agents.

Changes in 14.0.3

  • Disable V8 on Windows also.
  • iOS: Improve the packaging script.

Changes in 14.0.4

  • iOS: Fix arm64e regression caused by toolchain upgrade.

Changes in 14.0.5

  • QJS: Fix Interceptor error-handling.

Changes in 14.0.6

  • ObjC: Fix lifetime of replaced methods so they’re not tied to the class wrapper, and also kept alive in chaining use-cases. Kudos to @Hexploitable and @mrmacete for the assist!
  • Fix Exceptor sigaction() registration failure when act == oact. Thanks @hluwa!
  • Improve Linux libc detection.
  • Fix intermittent hang when enumerating and modifying threads on Linux.
  • Fix inconsistent PC vs CPSR Thumb bit handling.
  • Fix build regressions on Linux/armhf and Linux/arm64.
  • Publish binaries for Raspberry Pi 32- and 64-bit.

Changes in 14.0.7

  • Avoid deadlocking in scenarios where we crash while executing JS code, e.g. when calling out to a NativeFunction w/ exceptions: 'propagate', or in case of a bug in GumJS. Thanks @mrmacete!
  • Fix CModule on macOS/arm64.
  • Publish Python and Node.js binaries for Raspberry Pi 32-bit.
  • Publish binaries for Fedora 33 instead of Fedora 32.
  • Publish binaries for Ubuntu 20.10.

Changes in 14.0.8

  • Improve jailed iOS upload reliability by adding some bi-directional communication into the upload connection. This is to prevent DoS protections from kicking in during gadget upload in complex remote configurations. Thanks @mrmacete!

Frida 12.11 Released

In anticipation of Apple releasing macOS 11, it’s time for Frida 12.11! This release brings full compatibility with macOS 11 Beta 3. Not only that, we now also support macOS on Apple silicon. Yay!

It’s worth noting that we didn’t stop at arm64, we also support arm64e. This ABI is still a moving target, so if you have a Developer Transition Kit (DTK) and want to take this for a spin you will have to disable SIP, and then add a boot argument:

$ sudo nvram boot-args="-arm64e_preview_abi"

Considering this awesome convergence of platforms, there’s actually a chance that we may already support jailbroken iOS 14. We will know once a public jailbreak becomes available. At least it shouldn’t require much work to support.

So for those of you exploring your DTK, you can grab our CLI tools and Python bindings the usual way:

$ pip3 install frida-tools

As a sidenote we just released CryptoShark 0.2.0 and would highly recommend checking it out. Only caveat is that we only provide binaries for macOS/x86_64 for now, so if you want to try this on macOS/arm64 you will be able to run it thanks to Rosetta, but attaching to processes on the “Local System” device won’t work.

The workaround is simple though – just grab a frida-server binary from our releases and fire it up, then point CryptoShark at the “Local Socket” device. You can also use local SSH port forwarding if you’d like to run CryptoShark on one system and attach to processes on another:

$ ssh -L 27042: dtk

There’s also a lot of other exciting changes in this release, so definitely check out the changelog below.


Changes in 12.11.0

  • Add support for macOS 11 and Apple silicon.
  • Daemonize helper process on Darwin. Thanks @mrmacete!
  • Daemonize helper process on Linux.
  • Fix unreliable iOS device handling when using usbmuxd. Thanks @mrmacete!
  • Fix infinite wait when i/macOS frida-helper dies early.
  • Add Android spawn() “uid” option to specify user ID. Thanks @sowdust!
  • Add support for the latest checkra1n jailbreak. Thanks for the assist, @Hexploitable!
  • Improve Stalker ARM stability.
  • Plug leak in Interceptor arm64 backend error path.
  • Fix interception near memcpy() on systems w/o RWX pages.
  • Fix encoding of CpuContext pointers on Darwin/arm64e.
  • Always strip backtrace items on Darwin/arm64.
  • Fix Linux architecture detection on big-endian systems.
  • Fix Capstone endianness configuration on ARM BE8.

Changes in 12.11.1

  • Handle i/macOS targets using different ptrauth keys.
  • Fix Linux CPU type detection on big-endian systems.
  • Fix early instrumentation on Linux/ARM-BE8.
  • Fix injection into Linux processes blocked on SIGTTIN or SIGTTOU.

Changes in 12.11.2

  • Fix Stalker thread_exit probing on macOS 11/x86_64.
  • Fix slow exports resolution on macOS 11/x86_64.
  • Fix CModule support for Capstone headers on ARM.
  • Add ArmWriter to the CModule runtime for ARM.
  • qml: Add support for specifying the script runtime to use.

Changes in 12.11.3

  • Fix prototype of ModuleMap.values() in the V8 runtime.
  • qml: Sync DetachReason enum with the current Frida API.
  • qml: Fix Device lifetime logic.

Changes in 12.11.4

  • Fix injector on macOS 11 beta 3. Drop support for older betas.
  • Drop helper hack made redundant by macOS 11 beta 3.
  • Fix handling of i/macOS introspection modules.

Changes in 12.11.5

  • Fix i/macOS early instrumentation of processes using dyld’s modern code path on macOS 11 and iOS 14.
  • Make JVM method interception safer by installing new methods using VMThread::execute(), which blocks all Java threads and makes it safer to do interception of hot methods. Thanks @0xraaz!
  • Add support for SUB instruction to ARM Relocator. This means improved reliability when using Interceptor and Stalker on 32-bit ARM.
  • qml: Fix build with GCC by adding missing include.

Changes in 12.11.6

  • Port iOS jailed injector to the new arm64e ABI. This means iOS 14 beta 3 is now fully supported in jailed mode, even on A12+ devices.

Changes in 12.11.7

  • Improve libc detection on Linux and QNX. Thanks @demantz!
  • Fix checking of symbol sizes in libdwarf backend. This means more reliable debug symbol resolution on Linux.
  • Fix brittle Android activity start logic. Thanks @muhzii!
  • Improve Android Java hooking reliability by clearing the kAccFastInterpreterToInterpreterInvoke flag. Thanks @deroko!
  • Guard against using Java wrappers after $dispose(), to make such dangerous bugs easier to detect.
  • Improve the frida-qml build system and add support for standalone use.

Changes in 12.11.8

  • Add support for macOS 11 beta 4 on Apple silicon.

Changes in 12.11.9

  • Add support for jailed iOS w/ Xcode 12 developer disk images.

Changes in 12.11.10

  • node: Plug leak in IOStream’s WriteOperation. Thanks @mrmacete!
  • qml: Add support for listing applications.
  • qml: Expose a “count” property on each model.
  • Fix ARM relocation of “add sb, pc, r4”.
  • Fix ARM relocation of “add ip, pc, #4, #12”.
  • Fix ARM writer support for LDMIA when Rn is in reglist.

Changes in 12.11.11

  • Add support for opaque JNI IDs on Android R, to support debuggable apps. Thanks @muhzii!
  • qml: Add support for spawning processes.
  • qml: Add missing libraries when linking with devkit on Linux.
  • qml: Fix static linking on Linux.
  • qml: Optimize startup to not wait for enumerate_devices().

Changes in 12.11.12

  • Initialize CoreFoundation during early instrumentation on i/macOS. Thanks @mrmacete!
  • Support a NULL EventSink in Stalker. Thanks @meme!
  • node: Provide Electron prebuilds for v10 and v11. Next release will drop prebuilds for v9.
  • qml: Add post(QJsonArray).

Changes in 12.11.13

  • Fix ART internals probing on Android 11/arm64. Thanks @enovella_!
  • Build GumJS runtime for V8 without compression for now, as we need to improve frida-compile to use the latest version of terser.

Changes in 12.11.14

  • Build GumJS runtime for V8 with compression now that frida-compile has been upgraded to the latest version of terser.

Changes in 12.11.15

  • Add support for iOS 14.x secure DTX. Thanks @mrmacete!
  • Fix Java.deoptimizeEverything() on Android 11. Thanks @Gh0u1L5!

Changes in 12.11.16

  • Fix arm64e support in Arm64Relocator.can_relocate(). Thanks @mrmacete!
  • Add “onEvent” option to Stalker.follow(). This allows synchronous processing of events in native code – typically implemented using CModule. Useful when wanting to implement custom filtering and/or queuing logic to improve performance, or sacrifice performance in exchange for reliable event delivery.
  • Expose Stalker’s live CpuContext to EventSink. This can be accessed through the “onEvent” callback, and through the Gum C API.
  • Add Spinlock to the CModule runtime.

Changes in 12.11.17

  • Kill via LLDB on jailed iOS, to avoid killing via ProcessControl when possible. Turns out our previous behavior left debugserver in a bad state for which killed apps sometimes would appear as already running, failing early instrumentation on subsequent spawn() attempts. Thanks @mrmacete!
  • Fix Java bridge initialization on older Android API levels by letting the instrumentation field detection fail gracefully. We don’t need it on older API levels anyway.
  • Reduce Duktape memory usage a little per script. There is no need to intern the script source code string.

Changes in 12.11.18

  • Skip app extensions when detecting frontmost app on jailed iOS. Sometimes an app extension was returned as the first matched process, subsequently throwing “Unable to resolve bundle path to bundle ID”. Thanks @mrmacete!
  • Improve Android ART instrumentation offset detection for x86/x86_64. Thanks @Gh0u1L5!
  • Fix JDWP initialization failure on Android 7.1-8.1. Thanks @Gh0u1L5!
  • Fix nearest symbol logic in the libdwarf backend.
  • Plug a leak in the Duktape-based runtime’s argument parsing logic, where any collected memory range arrays would leak in case an error occurs parsing one of the following arguments.

Frida 12.10 Released

This time we have some exciting news for Java developers and reversers: frida-java-bridge now supports the HotSpot JVM. This means our Java runtime bridge is no longer exclusively an Android feature. Huge thanks to Razvan Sima for this amazing addition.

The timing couldn’t have been any better either, as we recently also added Java.enumerateMethods(query), a brand new API for efficiently locating methods matching a given query. We made sure to also implement this for the HotSpot JVM.

The query is specified as "class!method", with globs permitted. It may also be suffixed with / and one or more modifiers:

  • i: Case-insensitive matching.
  • s: Include method signatures, so e.g. "putInt" becomes "putInt(java.lang.String, int): void". Handy to match on argument and return types, such as "*!*: boolean/s" to match all methods that return a boolean.
  • u: User-defined classes only, ignoring system classes.

For instance:

Java.perform(() => {
  const groups = Java.enumerateMethods('*youtube*!on*')
  console.log(JSON.stringify(groups, null, 2));

Which might output something like:

    "loader": "<instance: java.lang.ClassLoader, $className: dalvik.system.PathClassLoader>",
    "classes": [
        "name": "",
        "methods": [
        "name": "",
        "methods": [
        "name": "",
        "methods": [

We’ve also enhanced frida-trace to support Java method tracing:

$ frida-trace \
    -U \
    -f \
    --runtime=v8 \
    -j '*!*certificate*/isu'
X509Util.addTestRootCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/"
X509Util.clearTestRootCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/"
X509Util.createCertificateFromBytes: Auto-generated handler at "/Users/oleavr/__handlers__/"
X509Util.isKnownRoot: Auto-generated handler at "/Users/oleavr/__handlers__/"
X509Util.verifyKeyUsage: Auto-generated handler at "/Users/oleavr/__handlers__/"
X509Util.verifyServerCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/"
ResourceLoader$CppProxy.native_enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/"
ResourceLoader$CppProxy.enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/"
AndroidCertVerifyResult.getCertificateChainEncoded: Auto-generated handler at "/Users/oleavr/__handlers__/"
bjbm.a: Auto-generated handler at "/Users/oleavr/__handlers__/bjbm/a.js"
bjbn.a: Auto-generated handler at "/Users/oleavr/__handlers__/bjbn/a.js"
AndroidNetworkLibrary.addTestRootCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/"
AndroidNetworkLibrary.clearTestRootCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/"
AndroidNetworkLibrary.verifyServerCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/"
vxr.checkClientTrusted: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/checkClientTrusted.js"
vxr.checkServerTrusted: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/checkServerTrusted.js"
vxr.getAcceptedIssuers: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/getAcceptedIssuers.js"
ResourceLoader.enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/"
Started tracing 18 functions. Press Ctrl+C to stop.
           /* TID 0x339d */
   955 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
   972 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
  1043 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,4,…],[48,-126,4,…]], "RSA", "")
  1059 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x33a0 */
  1643 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,5,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x339d */
  1651 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a1 */
  1665 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,15,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a0 */
  1674 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x339d */
  1674 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x3417 */
  1674 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,15,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a1 */
  1684 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x3417 */
  1688 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
  2513 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
  2527 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
  2722 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a1 */
  2741 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x339d */
  2758 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a1 */
  2771 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x3417 */
  2772 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x339d */
  2777 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
  2892 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x3417 */
  2908 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x33a1 */
  2926 ms  AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "")
           /* TID 0x3417 */
  2935 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x339d */
  2937 ms  AndroidCertVerifyResult.getCertificateChainEncoded()
           /* TID 0x33a1 */
  2942 ms  AndroidCertVerifyResult.getCertificateChainEncoded()

This was just released as part of frida-tools 8.0 – which you may grab through e.g.: pip3 install -U frida-tools

We’ve also been working hard on quality improvements across the board. One good example is Stalker for 32-bit ARM, which now works a lot better on Android. It is also a lot faster, in part because of a bug resulting in Thumb blocks being recompiled over and over. We have also implemented one of the adaptive optimizations that the other Stalker backends make use of, and this alone typically amounts to a ~5x performance improvement.

So that should cover the highlights – but if you’re curious about the details I’d highly recommend reading the changelog below.


Changes in 12.10.0

  • Java: Add support for HotSpot JVM. Uses JVMTI to enumerate classes and choose objects. Method interception works if the JVM library has symbols (default with JDK on macOS). Tested on macOS with java 8, 11, 13, 14. Thanks @0xraaz!
  • Java: Fix non-return from _getUsedClass(), where calling Java.use() twice without using Java.perform() would result in _getUsedClass() getting stuck in an infinite sleep loop. Thanks @0xraaz!
  • Java: Fix $alloc(), which got broken by the refactoring a while back.
  • ObjC: Add Block.declare() to be able to work with blocks without signature metadata.
  • ObjC: Fix ObjC pointer handling regression introduced in 12.9.8.

Changes in 12.10.1

  • Java: Allow ClassFactory.get(null), for convenience when using enumerateMethods().
  • Java: Restore the JVM method adjustment logic, which got accidentally dropped from the pull-request. Thanks @0xraaz!

Changes in 12.10.2

  • Fix handling of long symbol names on i/macOS. Thanks @mrmacete!
  • Java: Fix JVM interception issues for static/final methods. Thanks @0xraaz!
  • Fix Stalker ARM handling of Thumb-2 “mov pc, <reg>”.
  • Fix Stalker ARM handling of volatile VFP registers.

Changes in 12.10.3

  • Fix device removal wiring in the Fruity backend. Thanks @mrmacete!
  • Avoid clobbering R9 in ArmWriter.put_branch_address().
  • Add ThumbWriter.can_branch_directly_between().
  • Add ThumbWriter.put_branch_address().
  • Improve ThumbRelocator to handle ADR.
  • Fix Stalker ARM block corruption.
  • Fix Stalker ARM block recycling logic for Thumb blocks.
  • Add missing Stalker ARM continuation logic, to support long basic blocks.
  • Implement Stalker ARM backpatching logic to improve performance, typically 5x.

Changes in 12.10.4

  • Fix encoding of in the V8 runtime. Thanks @mrmacete!

Frida 12.9 Released

Our previous big release was all about Stalker. For those of you not yet familiar with it, it’s basically a code tracing engine – allowing threads to be followed, capturing every function, every block, even every instruction which is executed. Beyond just tracing code, it also allows you to add and remove instructions anywhere. It even uses advanced JIT tricks to make all of this really fast.

That may still sound a little abstract, so let’s have a look at a couple of examples. One way to use it is when you want to determine “what other functions does this function call”. Or, perhaps you’d like to use Apple’s speech synthesizer to announce the RAX register’s value at every RET instruction in code belonging to the app? Here is how that can be done. This was one of the demos at my r2con presentation back in 2017.

Up until now Stalker has only been available on Intel architectures and ARM64. So I’m really excited to announce that Stalker is now also available on ARM32! Yay! 🎉 I’m hopeful that this sorely missed Stalker backend will motivate a lot of you to start building really cool things on top of Stalker. I feel like it has a ton of potential beyond “just” code tracing. And combined with CModule it’s become really easy to balance rapid prototyping and dynamic behavior with performance.

There’s so much to talk about in this release. One of the other major changes is that we have upgraded all of our dependencies. Most interesting among them is probably V8, which we have upgraded to 8.4. This means you can use all of the latest JavaScript language features such as optional chaining and nullish coalescing operator without having to frida-compile your agent. That, and performance improvements, another area where V8 just keeps on getting better and better.

We’ve also just added support for Android 11 Developer Preview 4, and iOS/arm64e apps are now fully supported even on jailed iOS. Things have improved so much across all of our supported platforms. One thing in particular that I’d like to highlight is that we have finally eliminated a long-standing resource leak affecting our Duktape-based JS runtime – a bug that’s been around for as long as we’ve been using Duktape as our default JS runtime.

Anyway, there’s really no easy way to dig into all of the areas where things have improved, so definitely check out the changelog below.


Changes in 12.9.0

  • Stalker now also available on ARM32. 🎉
  • Stalker JS integrations no longer clobber errno / LastError.
  • Stalker.follow() now reliable on x86 and ARM64, including when the target thread is in a syscall on Windows.
  • Stalker is finally reliable on WoW64. Thanks @zuypt!
  • All dependencies have been updated to the latest and greatest. Most exciting is V8 8.4, supporting the latest JavaScript language features.
  • Long-standing Duktape memory leak finally discovered and fixed. Thanks to @disazoz for the bug-report that lead to this breakthrough.
  • Socket.connect() no longer leaks the file-descriptor (and associated memory) on error. (Fixed by the GLib dependency upgrade.) Thanks for reporting, @1215clf!
  •*() no longer leaks in the V8 runtime.
  • UNIX build system moved to Meson 0.54.
  • Windows build system moved to VS2019.
  • Node.js prebuilds provided for v14, in addition to v10 and v12.
  • Electron prebuilds provided for v8 and v9.
  • Fedora packages for F32.
  • Ubuntu packages for Ubuntu 20.04.
  • Python bindings no longer using any deprecated APIs.
  • Support for leanback-only Android apps. Thanks @5murfette!
  • iOS jailed spawn() w/o closure supported on arm64e. Thanks @mrmacete!
  • iOS usbmux pairing record plist parsing now also handles binary plists, fixing a long-standing issue where Frida would reject a tethered iOS USB device. Thanks @pachoo!
  • ObjC.choose() also supported on arm64e. Thanks @mrmacete!
  • ObjC.protocols enumeration finally working properly, and not just the first time. Thanks for reporting, @CodeColorist!
  • Initial support for Android 11 Developer Preview. Thanks @abdawoud!
  • MUSL libc compatibility.
  • Support for older versions of glibc, so our binaries can run on a wide variety of desktop Linux systems.
  • Libc shim also covers memalign() and supports newer GNU toolchains.
  • Exceptor’s POSIX backend is now detecting Thumb correctly on ARM32, which would previously result in random crashes.
  • Exceptor no longer clobbers “rflags” (x86_64) and “cpsr” (ARM64) on i/macOS, and provides write access to the native context.
  • Four essential i/macOS 64-bit syscalls added to the libc shim: read(), write(), mmap(), munmap(). Thanks @mrmacete!
  • iOS binaries now signed with the “skip-library-validation” entitlement for convenience. Thanks @elvanderb!
  • The frida-core Vala API bindings are no longer missing the frida.Error type.
  • Our scripts now allow messages to be post()ed to them while they are in the LOADING state. This is useful when a script needs to make a synchronous request during load(). Thanks @Gbps!
  • Gadget finally supports early instrumentation with the V8 runtime on 64-bit ELF targets, where constructor functions would previously be run in the wrong order. Thanks @tacesrever!
  • Support for ADB channels beyond just TCP in Device.open_channel(). Thanks @aemmitt-ns!
  • Many new instructions supported in the ArmWriter and ThumbWriter APIs.
  • Massive improvements to our ARM32 relocator implementations.
  • Linux module enumeration working when invoked through loader.
  • Linux symbol resolution improvements.
  • Better argument list handling in the V8 runtime, treating undefined the same as in the Duktape runtime. Thanks @mrmacete!
  • CModule Stalker API is back in working order.
  • CModule runtime now exposes Thread.{get,set}_system_error().
  • CModule is now a stub on Linux/MIPS, instead of failing to compile due to TinyCC not yet supporting MIPS.
  • Capstone configured to support ARMv8 A32 encodings.

Changes in 12.9.1

  • The Python bindings’ does the right thing for Python 3.x on macOS.

Changes in 12.9.2

  • Fruity (iOS USB) backend no longer emits a warning on stdio.

Changes in 12.9.3

  • Android 11 Developer Preview 4 is now supported. Thanks for the assist, @enovella_!
  • Linux file monitoring is back in great shape.
  • ArmRelocator properly relocates ADD instructions involving the PC register.
  • ThumbRelocator properly handles IT blocks containing an unconditional branch. This means Interceptor is able to hook even more tricky cases. Thanks @bet4it!
  • Stalker ARM32 also supports clone syscalls in Thumb mode.
  • Stalker ARM32 now suppresses events around exclusive ops like the ARM64 backend.
  • Stalker ARM32 trust threshold support.
  • Improved error-handling in ObjC and Java bridges, to avoid crashing the process on unsupported OSes.

Changes in 12.9.4

  • ObjC.available no longer pretends that the Objective-C runtime is available when it indeed is not. The error-handling refactoring in 12.9.3 broke this, and the regression went unnoticed due to this being a blind spot in our test coverage.
  • Electron v9 is out, so we now only provide prebuilds for v9.

Changes in 12.9.5

  • iOS early instrumentation – i.e. spawn() – supported on latest unc0ver.
  • iOS crash reporter integration ported to iOS 13.5.
  • SystemFunction now implements call() and apply() in the Duktape runtime, and not only in the V8 runtime.
  • Java bridge finally handles strings with embedded nuls, fixing a long-standing issue that’s been around for as long as the Java bridge has existed. Thanks @tacesrever!

Changes in 12.9.6

  • No changes except for proper Windows binaries this time. The Windows CI worker did not actually build anything last time around, and released stale binaries.

Changes in 12.9.7

  • iOS early instrumentation more reliable on the unc0ver jailbreak: we now load substrate-inserter.dylib as part of our early instrumentation. This means it gets a chance to bootstrap the process, and lets you hook system APIs hooked by the bootstrapper without worrying about the bootstrapper getting confused when it encounters your hooks. Thanks @mrmacete!

Changes in 12.9.8

  • ApiResolver implementations now support case-insensitive matching by appending “/i” to the query string. Thanks @Hexploitable!
  • The module ApiResolver no longer leaks the MatchInfo instance.
  • CModule runtime gained GLib.PatternSpec and GLib UTF-8 case helpers.
  • DebugSymbol.load() introduced to be able to explicitly load debug symbols. For now this is only implemented on Windows, where we now also support “module!symbol” notation for improved performance and precision. Thanks @ohjeongwook!
  • Java bridge got a brand new API: Java.enumerateMethods(query) This enables efficiently locating methods matching a given query.
  • ObjC.choose() no longer crashes due to a lifetime issue only reproducible in our V8 runtime.

Frida 12.8 Released

Get ready for an exciting new release. This time around we’re giving some long overdue love to our Stalker engine. It’s been around for roughly ten years, but it wasn’t until Frida 10.5 in late 2017 that we started unleashing its massive potential.

Up until now we were able to Stalker.follow() existing threads and not only observe them, but also mutate their instruction streams any way we’d like. It could also be combined with Interceptor to instrument the current thread between strategic points. This allowed us to build tools such as AirSpy.

But, what if we want to Stalker.follow() a NativeFunction call? This may seem really simple, but reentrancy makes this really hard. It’s easy to end up following execution inside e.g. our private heap, and end up needing to allocate memory for the instrumentation itself… all kinds of fun scenarios that are mind-boggling to reason about.

The way we dealt with this was to teach Stalker to exclude certain memory ranges, so that if it sees a call going to such a location it will simply emit a call instruction there instead of following execution. So what we did was to automatically exclude frida-agent’s own memory range, and that way we didn’t have to deal with any of the reentrancy madness.

We also took care to special-case attempts to Stalker.follow() the current thread, so that we queued that work until we’re about to leave our runtime and transition back into user code (or our main loop, in the case of the JS thread).

That still left the big unanswered question of how to use Stalker in conjunction with NativeFunction. We can now finally put that behind us:

var open = new NativeFunction(
    Module.getExportByName(null, 'open'),
    'int', ['pointer', 'int'],
    { traps: 'all' }

  events: {
    call: true
  onReceive: function (e) {

var fd = open(Memory.allocUtf8String('/foo/bar'), 0);
console.log('open() =>', fd);

By setting the traps: 'all' option on the NativeFunction, it will re-activate Stalker when called from a thread where Stalker is temporarily paused because it’s calling out to an excluded range – which is the case here because all of frida-agent’s code is marked as excluded.

We can also achieve the same goal for Objective-C methods:

  events: {
    call: true
  onReceive: function (e) {

var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool;
var NSFileManager = ObjC.classes.NSFileManager;

var fileExistsAtPath = NSFileManager['- fileExistsAtPath:']
    .clone({ traps: 'all' });

var pool = NSAutoreleasePool.alloc().init();
try {
  var manager = NSFileManager.defaultManager();
  var result =, '/foo/bar');
  console.log('fileExistsAtPath() =>', result);
} finally {

And also for Java methods on Android:

  events: {
    call: true
  onReceive: function (e) {

Java.perform(function () {
  var JFile = Java.use('');
  var exists = JFile.exists.clone({ traps: 'all' });

  var file = JFile.$new('/foo/bar');
  var result =;
  console.log('exists() =>', result);

Yay. That said, these examples are barely scratching the surface of what’s possible using Stalker. One of the really cool use-cases is in-process fuzzing, which frida-fuzz is a great example of. There’s also a bunch of other use-cases, such as reversing, measuring code coverage, fault injection for testing purposes, hooking inline syscalls, etc.

So that’s the main story of this release. Would like to thank @andreafioraldi for the great bug-reports and help testing these tricky changes.

Wrapping up

One cool new feature worth mentioning is the new ArrayBuffer.wrap() API, which allows you to conveniently and efficiently access memory regions as if they were JavaScript arrays:

var header = Memory.alloc(16);

var bytes = new Uint8Array(ArrayBuffer.wrap(header, 16));
bytes[0] = 1;
bytes[0] += 2;
bytes[1] = 2;

console.log(hexdump(header, { length: 16, ansi: true }));
console.log('First byte is:', bytes[0]);

This means you can hand over direct memory access to JavaScript APIs without needing to copy memory in/out of the runtime. The only drawback is that bad pointers won’t result in a JS exception, and will crash the process.

We now also allow you to access the backing store of any ArrayBuffer, through the new unwrap() method on ArrayBuffer. An example use-case for this is when using an existing module such as frida-fs where you get an ArrayBuffer that you then want to pass to native code.

Kudos to @DaveManouchehri for contributing the first draft of the ArrayBuffer.wrap() API, and also big thanks to @CodeColorist for suggesting and helping shape the unwrap() feature.

Changes in 12.8.0

  • Stalker reactivation working properly.
  • Stalker thread lifetime properly handled. Will also no longer crash when following a thread to its death on i/macOS.
  • Safer garbage collection logic in Stalker.
  • Making mistakes in the Stalker transform callback which ends up throwing a JS exception now results in Stalker.unfollow(), so the error doesn’t get swallowed by the process crashing.
  • Robust support for Stalker transform calling unfollow().
  • Stalker support for older x86 CPUs without AVX2 support.
  • Support for disabling automatic Stalker queue drain.
  • NativeFunction is better at handling exceptions through the brand new Interceptor unwinding API.
  • Java and ObjC APIs to specify NativeFunction options for methods through clone(options), and blocks through the second argument to ObjC.Block().
  • ObjC class and protocol caching logic finally works. Thanks @gebing!
  • The prebuilt Python 3 extension for Windows finally supports all Python 3 versions >= 3.4 on Windows, just like on the other platforms.
  • ArrayBuffer wrap() and unwrap().
  • DebugSymbol API has better error-handling on Linux/Android.
  • Java integration no longer crashes in recompileExceptionClearForArm64() in system processes on Android 10.
  • GumJS devkit on i/macOS supports V8 once again.

Changes in 12.8.1

  • The CModule Stalker integration is back in business.

Changes in 12.8.2

  • Thumb IT blocks are finally relocated correctly. This means we are able to hook a lot more functions on 32-bit ARM targets, e.g. Android. Thanks @bigboysun!

Changes in 12.8.3

  • Java.ClassFactory.get() introduced to be able to work with multiple class loaders without worrying about colliding class names. This means that assigning to the loader property is now considered deprecated. We still keep it around for backwards compatibility, but using it alongside the new API is not supported.
  • Java.enumerateLoadedClasses() also provides class handles and not just names.
  • The JNI GetByteArrayRegion() function is now part of the Env wrapper. Thanks @iddoeldor!

Changes in 12.8.4

  • Internal hooks no longer result in crashes on Linux/ELF targets when PLT/GOT entries haven’t been warmed up.

Changes in 12.8.5

  • Python bindings finally provide properly encoded error messages on Python 2.x.

Changes in 12.8.6

  • Android linker detection is finally working again in sandboxed processes. This was a regression introduced in 12.7.8. Kudos to @DaveManouchehri for reporting and helping track this one down!

Changes in 12.8.7

  • Our Node.js IOStream bindings received two critical stability improvements. Turns out the cancellation logic had a race-condition that resulted in the cancellable not always being used. There was also a bug in the teardown logic that could result in a stream being closed before all I/O operations had completed. Kudos to @mrmacete for these awesome fixes!

Changes in 12.8.8

  • Gadget no longer deadlocks on Android/Linux during early instrumentation use-cases where Gadget’s entrypoint gets called with dynamic linker lock(s) held. Due to Exceptor now using dlsym() to avoid running into PLT/GOT issues during early instrumentation, we need to ensure that Exceptor gets initialized from the entrypoint thread, and not the Gadget thread.

Changes in 12.8.9

  • Stalker’s JavaScript integration is no longer performing a use-after-free in EventSink::stop(), i.e. after Stalker.unfollow().

Changes in 12.8.10

  • Gadget is once again able to run on iOS without a debugger present. This was a regression introduced in 12.8.8. Kudos to @ddzobov for reporting!

Changes in 12.8.11

  • The i/macOS Exceptor’s API hooks no longer perform an OOB write when a user of the Mach exception handling APIs only requests a subset of the handlers. Such a user would typically be a crash reporter or analytics framework.
  • Electron prebuilds are now provided for v8 (stable) and v9 (beta). We no longer provide prebuilds for v7.

Changes in 12.8.12

  • Massively overhauled Android Java integration, now using Proxy objects and CModule to lazily resolve things. And no more eval-usage to dynamically generate method and field wrappers – i.e. less memory required per wrapper generated. All of these changes reduce memory usage and allow Java.use() to complete much faster.
  • Android Java integration provides uncensored access to methods and fields on Android versions that attempt to hide private APIs, i.e. Android >= 9.
  • Way faster Android device enumeration. No longer running any adb shell commands to determine the device name when the locally running ADB daemon is new enough (i.e. ADB >= sometime during 2017).
  • We’ve finally eliminated a long-standing memory leak on Linux-based OSes, affecting restricted processes such as zygote and system_server on newer versions of Android. This was a bug in our logic that garbage-collects thread-local data shortly after a given thread has exited. The mechanism that determines that the thread has indeed finished exiting would fail and never consider the thread gone. This would result in more and more garbage accumulating, with a longer and longer collection of garbage to iterate over. So not only would we be spending increasingly more time on futile GC attempts, we would also be eating CPU retrying a GC every 50 ms.
  • The Python bindings allow obtaining a file-descriptor from a Cancellable in order to integrate it into event loops and other poll()-style use-cases. Worth noting that frida-tools 7.0.1 is out with a major improvement built on this: The CLI tools no longer delay for up to 500 ms before exiting. So short-lived programs like frida-ls-devices and frida-ps now feel very snappy.
  • Duktape source-map handling now also works with scripts loaded by the REPL – where the inline source-map isn’t the last line of the script due to the REPL appending its own code. This means stack-traces always contain meaningful filenames and line numbers.
  • Duktape: the baked in JavaScript runtime – i.e. GumJS’ glue code, ObjC, and Java – is now Babelified with the loose option enabled, to reduce bloat and improve performance. No modern JavaScript data structures leak out through the APIs, so there’s no need to have Babel be spec-compliant.
  • V8: the baked in JavaScript runtime is compressed, for a smaller footprint and faster code. This was previously only done to the Duktape one.
  • Better Linux process name heuristics in enumerate_processes().

Changes in 12.8.13

  • Java.performNow() is back in working order.
  • Python bindings’ now looks for a local .egg before attempting to download one, and expects the download to complete within two minutes. Kudos to @XieEDeHeiShou for these nice improvements!

Changes in 12.8.14

  • The iOS Simulator is now properly supported, both in Gadget form and attaching to a running Simulator process from macOS. Kudos to @insitusec for helping fix these issues!
  • Gadget now also looks for its .config in the directory above on iOS, but only if its parent directory is named “Frameworks”. Kudos to @insitusec for the suggestion!

Changes in 12.8.15

  • Brand new feature-complete support for iOS/arm64e, including new NativePointer methods: sign(), strip(), blend().
  • Latest iOS Unc0ver jailbreak is now supported. Kudos to @mrmacete for the pull-request, and @Pwn20wnd for the assistance! ❤️
  • Improved support for the Chimera jailbreak, making sure its pspawn_payload-stg2.dylib is initialized. Thanks @mrmacete!
  • The i/macOS injector no longer fails when an agent entrypoint returns instantly.
  • Better error message about needing Gadget for jailed iOS.
  • Improved error-handling in the Windows injector to avoid crashing the target process when our DLL injection fails. Kudos @dasraf9!
  • Support for injection into live newborn targets on i/macOS, and also no longer treating suspended processes as needing to be prepared for injection, regardless of whether they actually need it.
  • Improved iOS fault tolerance, handling frontmost iOS app name query failing.
  • Improved Android fault tolerance, handling zygote and system_server processes dying without having to restart frida-server.
  • Now able to launch frida-server during boot on Android 10, as LD_LIBRARY_PATH no longer interferes with the spawning of frida-helper-32. Kudos to @enovella for helping track this one down!
  • No more infinite loop when failing to handle SIGABRT on UNIXy platforms.
  • We now support nested signals in Exceptor’s POSIX backend. Kudos @bannsec!
  • Proper handling of invalid Windows ANSI strings. Thanks, @clouds56!
  • Java.perform() working properly again on Android < 5.
  • Improved varargs-handling in NativeFunction, now promoting varargs smaller than int. Thanks for reporting, @0x410c!

Changes in 12.8.16

  • Large CModule instances now working on iOS systems with 16K pages. Kudos to @mrmacete for discovering and fixing this long-standing issue!
  • Stalker also works in arm64 processes on iOS/arm64e. Kudos to @AeonLucid for reporting and helping track this one down!

Changes in 12.8.17

  • Support for injection into live newborn targets on i/macOS turned out to cause regressions, so we’ve reverted it for now. Specifically, notifyd on iOS 12.4 is a case where libSystemInitialized is not getting set. Need to dig deeper to figure out why, so decided to walk back this logic for now.

Changes in 12.8.18

  • New and improved Java.scheduleOnMainThread() to allow calling APIs such as getApplicationContext(). Kudos to @giantpune for reporting!
  • Ability to hook CriticalNative methods on newer versions of Android. Kudos to @abdawoud for reporting!

Changes in 12.8.19

  • Scripts are now properly cleaned up if destroyed without first being loaded. This ensures that ultimately when the script core is disposed, it in turn disposes its reference to the Exceptor. Failing to do so causes indefinite hang when attaching after detaching in presence of unloaded scripts, due to the Exceptor thread remaining in the target process. Kudos to @mrmacete for discovering and fixing this long-standing issue!

Changes in 12.8.20

  • The remove_remote_device() API is back in working order. This was an unfortunate regression introduced in 12.7.17. Thanks for reporting, @CodeColorist!

Frida 12.7 Released

There’s only one new feature this time, but it’s a big one. We’re going to address the elephant in the room: performance.

While Frida’s instrumentation core, Gum, is written in C and can be used from C, most use-cases are better off using its JavaScript bindings.

There are however situations where performance becomes an issue. Even when using our V8-based runtime, which means your JavaScript will be profiled while it’s running and optimized based on where the hotspots are… (Which by the way is amazing – V8 is truly an impressive feat of engineering!)

…there is a small price to pay for entering and leaving the JavaScript VM. On an iPhone 5S this might amount to something like six microseconds if you use Interceptor.attach() and only specify onEnter, and leave it empty.

This may not sound like a lot, but if a function is called a million times, it’s going to amount to 6 seconds of added overhead. And perhaps the hook only needs to do something really simple, so most of the time is actually spent on entering and leaving the VM.

There’s also the same kind of issue when needing to pass a callback to an API, where the API walks through potentially millions of items and needs to call the callback for each of them. The callback might just look at one byte and collect the few of the items that match a certain criteria.

Naively one could go ahead and use a NativeCallback to implement that callback, but it quickly becomes apparent that this just doesn’t scale.

Or, you might be writing a fuzzer and needing to call a NativeFunction in a tight loop, and the cost of entering/leaving the VM plus libffi just adds up.

Short of writing the whole agent in C, one could go ahead and build a native library, and load it using Module.load(). This works but means it has to be compiled for every single architecture, deployed to the target, etc.

Another solution is to use the X86Writer/Arm64Writer/etc. APIs to generate code at runtime. This is also painful as there’s quite a bit of work required for each architecture to be supported. But up until now this was the only portable option for use in modules such as frida-java-bridge.

But now we finally have something much better. Enter CModule:

CModule Hello World

It takes the string of C source code and compiles it to machine code, straight to memory. This is implemented using TinyCC, which means that this feature only adds ~100 kB of footprint to Frida.

As you can see, any global functions are automatically exported as NativePointer properties named exactly like in the C source code.

And, it’s fast:

CModule Speed

(Measured on an Intel i7 @ 3.1 GHz.)

We can also use this new feature in conjunction with APIs like Interceptor:

const m = new CModule(`
#include <gum/guminterceptor.h>

#define EPERM 1

open (const char * path,
      int oflag,
  GumInvocationContext * ic;

  ic = gum_interceptor_get_current_invocation ();
  ic->system_error = EPERM;

  return -1;

const openImpl = Module.getExportByName(null, 'open');


(Note that this and the following examples use modern JavaScript features like template literals, so they either need to be run on our V8 runtime, or compiled using frida-compile.)

We can also combine it with Interceptor.attach():

const openImpl = Module.getExportByName(null, 'open');

Interceptor.attach(openImpl, new CModule(`
  #include <gum/guminterceptor.h>
  #include <stdio.h>

  onEnter (GumInvocationContext * ic)
    const char * path;

    path = gum_invocation_context_get_nth_argument (ic, 0);

    printf ("open() path=\\"%s\\"\\n", path);

  onLeave (GumInvocationContext * ic)
    int fd;

    fd = (int) gum_invocation_context_get_return_value (ic);

    printf ("=> fd=%d\\n", fd);

Yay. Though this last particular example actually writes to stdout of the target process, which is fine for debugging but probably not all that useful.

We can however fix that by calling back into JavaScript. Let’s see what that might look like:

const openImpl = Module.getExportByName(null, 'open');

Interceptor.attach(openImpl, new CModule(`
  #include <gum/guminterceptor.h>

  extern void onMessage (const gchar * message);

  static void log (const gchar * format, ...);

  onEnter (GumInvocationContext * ic)
    const char * path;

    path = gum_invocation_context_get_nth_argument (ic, 0);

    log ("open() path=\\"%s\\"", path);

  onLeave (GumInvocationContext * ic)
    int fd;

    fd = (int) gum_invocation_context_get_return_value (ic);

    log ("=> fd=%d", fd);

  static void
  log (const gchar * format,
    gchar * message;
    va_list args;

    va_start (args, format);
    message = g_strdup_vprintf (format, args);
    va_end (args);

    onMessage (message);

    g_free (message);
`, {
  onMessage: new NativeCallback(messagePtr => {
    const message = messagePtr.readUtf8String();
    console.log('onMessage:', message);
  }, 'void', ['pointer'])

That is however just a toy example: doing it this way will actually defeat the purpose of writing the hooks in C to improve performance. A real implementation might instead append to a GLib.Array after acquiring a GLib.Mutex, and periodically flush the buffered data by calling back into JS.

And just like JavaScript functions can be called from C, we can also share data between the two realms:

const calls = Memory.alloc(4);

const openImpl = Module.getExportByName(null, 'open');

Interceptor.attach(openImpl, new CModule(`
  #include <gum/guminterceptor.h>

  extern volatile gint calls;

  onEnter (GumInvocationContext * ic)
    g_atomic_int_add (&calls, 1);
`, { calls }));

setInterval(() => {
  console.log('Calls so far:', calls.readInt());
}, 1000);

For now we don’t have any docs on the built-in C APIs, but you can browse the headers in frida-gum/bindings/gumjs/runtime/cmodule to get an overview. Drop function names into an Internet search engine to look up docs for the non-Frida APIs such as GLib’s.

The intention is to only expose a minimal subset of the standard C library, GLib, JSON-GLib, and Gum APIs; in order to minimize bloat and maximize performance. Things we include should either be impossible to achieve by calling into JS, or prohibitively expensive to achieve that way.

Think of the JS side as the operating system where the functions you plug into it are system calls; and only use CModule for hooking hot functions or implementing high-performance glue code like callbacks passed to performance-sensitive APIs.

Also bear in mind that the machine code generated by TinyCC is not as efficient as that of Clang or GCC, so computationally expensive algorithms might actually be faster to implement in JavaScript. (When using our V8-based runtime.) But for hooks and glue code this difference isn’t significant, and you can always generate machine code using e.g. Arm64Writer and plug into your CModule if you need to optimize an inner loop.

One important caveat is that all data is read-only, so writable globals should be declared extern, allocated using e.g. Memory.alloc(), and passed in as symbols through the constructor’s second argument. (Like we did with calls in the last example.)

You might also need to initialize things and clean them up when the CModule gets destroyed – e.g. because the script got unloaded – and we provide a couple of lifetime hooks for such purposes:

const cm = new CModule(`
#include <stdio.h>

init (void)
  printf ("init\\n");

finalize (void)
  printf ("finalize\\n");

cm.dispose(); // or wait until it gets GCed or script unloaded

Anyway, this post is getting long, but before we wrap up let’s look at how to use CModule with the Stalker APIs:

const cm = new CModule(`
#include <gum/gumstalker.h>

static void on_ret (GumCpuContext * cpu_context,
    gpointer user_data);

transform (GumStalkerIterator * iterator,
           GumStalkerOutput * output,
           gpointer user_data)
  cs_insn * insn;

  while (gum_stalker_iterator_next (iterator, &insn))
    if (insn->id == X86_INS_RET)
      gum_x86_writer_put_nop (output->writer.x86);
      gum_stalker_iterator_put_callout (iterator,
          on_ret, NULL, NULL);

    gum_stalker_iterator_keep (iterator);

static void
on_ret (GumCpuContext * cpu_context,
        gpointer user_data)
  printf ("on_ret!\n");

const mainThread = Process.enumerateThreads()[0];

Stalker.follow(, {
  transform: cm.transform,
  data: ptr(1337)

This shows how you can implement both the transform callback and the callouts in C, but you may also use a hybrid approach where you write the transform callback in JS and only some of the callouts in C.

It is also worth noting that I rewrote ObjC.choose() to use CModule, and it is now roughly 100x faster. When testing it on the login screen of the Twitter app on an iPhone 6S, this went from taking ~5 seconds to now only ~50 ms.

So with that, I hope you’ll enjoy this release. Excited to see what kind of things you will build with the new CModule API. One thing I’m really looking forward to is improving our REPL to support loading a .c file next to the .js, for rapid prototyping purposes.


Changes in 12.7.0

  • Brand new CModule API powered by TinyCC. (You just read about it.)
  • TinyCC was improved to support Apple’s ABI on macOS/x86.
  • Stalker.exclude() is now exposed to JS to be able to mark specific memory ranges as excluded. This is useful to improve performance and reduce noise.
  • Concurrent calls to Java.use() are now supported, thanks to a neat contribution by @gebing.
  • The hexdump() implementation was improved to clamp the length option to the length of the ArrayBuffer, thanks to another neat contribution by @gebing.

Changes in 12.7.1

  • More CModule goodies, including GLib.String, GLib.Timer, and Json.Builder.
  • TinyCC was improved to support Apple’s ABI on iOS/arm64.
  • ObjC.choose() was rewritten using CModule, and is now ~100x faster.

Changes in 12.7.2

  • CModule got some missing ref-counting APIs.

Changes in 12.7.3

  • CModule memory ranges are now properly cloaked.
  • The V8 garbage collector is now informed about externally allocated CModule memory so it can make better decisions about when to GC.
  • Symbols attached to a CModule are now properly kept alive in the V8 runtime also; and the CModule itself is not kept alive indefinitely (or until script unload).
  • CModule.dispose() was added for eagerly cleaning up memory.

Changes in 12.7.4

  • The frida-inject tool now supports spawn(). Kudos to @hunterli for contributing this neat feature.
  • Our V8 runtime no longer deadlocks on i/macOS when thread_suspend() is called with the JS lock still held, like Stalker.follow() indirectly does when asked to follow another thread.

Changes in 12.7.5

  • Brand new channels API for establishing TCP connections to a tethered iOS or Android device, as well as talking to lockdown services on a tethered iOS device.
  • The timeout logic behind DeviceManager.find_device() and its sibling methods is now working properly.
  • Java marshaling of java.lang.Class is now working properly, and instance fields can also be introspected without needing an instance. Kudos to @gebing for contributing these neat fixes!

Changes in 12.7.6

  • The Android linker is now properly detected on Android 10.
  • Our Android SELinux policy patcher now also handles devices like Samsung S10, thanks to a neat contribution by @cbayet.
  • The frida-inject tool now supports -D/–device for working with non-local devices.
  • We now have better error-handling to avoid crashing when i/macOS processes terminate unexpectedly during early instrumentation.
  • iOS crash reporter integration is way more robust, thanks to some awesome fixes contributed by @mrmacete. One of his fixes also ensures parallel calls to recv().wait() for the same message type don’t end up in an infinite wait.
  • Stalking of thread creation is now supported on Linux/arm64. Kudos to @alvaro_fe for this awesome contribution!
  • V8 runtime’s WebAssembly support is working again on non-iOS also.
  • The Gum.DarwinModule API is now part of the cross-platform Gum API. Useful for parsing Mach-O files on non-Apple systems.

Changes in 12.7.7

  • Eternalized agents are now kept around when the last session gets closed, which means they can be reused for as long as the HostSession side, e.g. frida-server, sticks around. This means that additional copies of frida-agent can be avoided in a lot of cases. Kudos to @mrmacete for this awesome improvement.
  • Java bridge no longer triggers a use-after-free when a method returns this.
  • Our Android SELinux policy patcher no longer prints a warning on older versions of Android. This harmless but confusing regression was introduced by the previous release’ fix for Samsung S10 ROMs.
  • Better SELinux-related error messages.
  • Rudimentary support for iOS/arm64e.

Changes in 12.7.8

  • Android 10 support just landed in our Java bridge thanks to a brilliant contribution by @Alien-AV.
  • Better spawn()-handling for Android apps, where the activity parameter can be used in cases where the app doesn’t have a launcher activity. This neat improvement was contributed by @muhzii.
  • Android linker seeking logic was made future-proof thanks to an elegant contribution by @timstrazz.
  • Massively improved fault-tolerance on iOS: our launchd agent now kills pending processes when unloaded. This means that frida-server dying won’t leave processes stuck in a suspended state. Kudos to @mrmacete for this awesome improvement.

Changes in 12.7.9

  • We are back in business on macOS after a last-minute build regression snuck into the previous release.

Changes in 12.7.10

  • MemoryAccessMonitor is now available on all platforms, and even the Duktape runtime. Kudos to @alvaro_fe for these awesome improvements.
  • Android linker detection working properly again. (Regression introduced in 12.7.8.)
  • Gadget was improved to support passing a config through its constructor function on i/macOS.
  • Gadget’s system loop integration – only implemented on i/macOS – was removed to avoid undefined behavior in some scenarios.

Changes in 12.7.11

  • Frida no longer crashes in Android processes without a vDSO. (Regression introduced in 12.7.8.)
  • Better error-handling when parsing Mach-O images.
  • ARM vs Thumb properly handled when restoring exception on i/macOS. Thanks @alvaro_fe!
  • CModule’s header for JSON-GLib is now self-contained, as it should be.

Changes in 12.7.12

  • Full-featured iOS lockdown integration and unified devices, so Frida-based tools don’t need to worry as much about jailed vs jailbroken. When interacting with a jailed iOS device, Gadget is now injected automatically and there is no need to repackage the app, it only has to be debuggable.
  • Frida is finally able to detect recent iOS devices on Windows.
  • Bug in V8 was tracked down and fix backported from upstream. Kudos to @mrmacete for tracking this one down!

Changes in 12.7.13

  • Better handling of structs and unions in frida-objc-bridge. Thanks @gebing!
  • Our Node.js bindings now also expose type definitions for Crash and CrashParameters.
  • Attempts to attach to the system session on jailed iOS throw early and with a clearer error message.
  • The iOS Developer Disk Image-related error messages were tweaked for consistency.

Changes in 12.7.14

  • Frida now ensures code is readable before accessing it on Android >= 10. This was the last missing piece for full-featured Android 10 support. Being able to instrument system processes means that early instrumentation – i.e. spawn() – works, and starting frida-server doesn’t crash system_server due to the attempt it makes at preloading so the first spawn() will be fast. Kudos to @Alien-AV and @esanfelix for the painful research that made the solution possible to implement in one late Saturday evening. :-)

Changes in 12.7.15

  • Node.js bindings also expose the “summary” field of the Crash typing.

Changes in 12.7.16

  • The frida-gadget-ios meta-package comes with type definitions so it can be consumed from TypeScript.
  • Node.js bindings provide proper typings for Stdio and ChildOrigin.

Changes in 12.7.17

  • More robust support for jailed iOS: calling kill() before resume() but after attach() is now working properly.
  • Frida now communicates directly with the remote iOS/Android agent when possible. We achieve this by establishing a fresh TCP connection to the frida-server and having its file-descriptor passed to the agent. This means frida-server can remove itself from the data path, improving performance and reliability.
  • Clients connecting to frida-server to spawn() a process, but getting disconnected before they get a chance to resume() or kill() said process, will no longer leave such orphans in limbo. We now keep track of spawned processes and kill() them if the client suddenly gets disconnected.
  • We no longer crash Zygote on Android 10. Turns out an SELinux rule was missing.
  • Our TCP sockets now have TCP_NODELAY set, and we also support communicating with a remote frida-server or frida-gadget using UNIX sockets in addition to TCP.
  • NativeFunction now supports variadic functions, to avoid needing to create one instance per unique argument list signature. Thanks @gebing!

Changes in 12.7.18

  • Node.js bindings finally link in needed OpenSSL symbols on UNIX, instead of relying on luck where we’d usually end up in processes with another OpenSSL already loaded that’s globally visible and ABI-compatible (!).

Changes in 12.7.19

  • We now properly handle apply() on NativeFunction w/o args in the V8 runtime also. Kudos to @taviso for reporting this long-standing bug.
  • NativeFunction’s handling of optional args in call() and apply() now behaves like the builtin counterparts.

Changes in 12.7.20

  • Frida now supports both cleartext and encrypted iOS lockdown channels, retaining support for “cleartext after TLS handshake”-style channels by appending “?tls=handshake-only” to the channel address. Kudos, @mrmacete!
  • The paired lockdown channel itself can be accessed by using “lockdown:” as the channel address. Thanks @mrmacete!

Changes in 12.7.21

  • We now support iOS 13 on the checkra1n jailbreak. (Jailed iOS 13 was already supported.)

Changes in 12.7.22

  • Our iOS package scripts launch daemon logic is now compatible with checkra1n, so frida-server won’t have to be started/stopped manually.

Changes in 12.7.23

  • Enhanced support for the checkra1n jailbreak: Stalker is now way faster as it makes use of RWX pages.
  • Stability improvements when using Stalker on iOS on jailbreaks without RWX support. Thanks @mrmacete!
  • CModule is now compatible with additional iOS jailbreak flavors. Kudos, @mrmacete!
  • CModule runtime support for working with ModuleMap objects.
  • Support for ARMBE8 thanks to awesome contributions by Jon Wilson.
  • Frida now makes parent file-descriptors available to child processes when spawn()ing on i/macOS. This is consistent with the current behavior on Linux. Thanks @wizche!

Changes in 12.7.24

  • Node.js prebuilds also provided for Node.js v13.

Changes in 12.7.25

  • Log handler APIs got an overhaul in the Python and Node.js bindings, as part of a critical fix: the Node.js setter’s type wasn’t the same as the getter, as it also allowed null. This inconsistency resulted in recent TypeScript compiler versions choking on it. Thanks @mrmacete!
  • No more timestamp truncations in the V8 platform integration. Thanks for reporting, @DaveManouchehri!
  • Our Module.enumerateSymbols() API provides a “size” property when available, i.e. only on Linux/Android for now. Thanks @DaveManouchehri!
  • Java.use(name, { cache: ‘skip’ }) can now be used to bypass caching. Useful when dealing with multiple class-loaders and colliding class names. Thanks @ChaosData and @H4oK3!

Changes in 12.7.26

  • Stalker now supports temporary reactivation, to allow stalking code from inside an excluded memory range.
  • NativeFunction got a brand new option traps: 'all' to allow calls to be stalked even when Frida’s own memory range is marked as excluded.
  • Thread enumeration on Linux is finally working when using Yama. Thanks Jon Wilson!

Frida 12.6 Released

After a flurry of fixes across all platforms over the last few weeks, I figured it was time to do another minor bump to call attention to this release.

One particular fix is worth mentioning specifically. There was a long- standing bug in our Android Java integration, where exception delivery would intermittently result in the process crashing with GetOatQuickMethodHeader() typically in the stack-trace. Shout-out to Jake Van Dyke and Giovanni Rocca for helping track this one down. This bug has been around for as long as ART has been supported, so this fix is worth celebrating. 🎉

Our V8 runtime is also a lot more stable, child-gating works better than ever before, Android device compatibility is much improved, etc.

So bottom line is that this is the most stable version of Frida ever released – and now is the time to make sure you’re running Frida 12.6.


Changes in 12.6.1

  • The exception delivery fix in the Android Java integration introduced a performance bottleneck when running the VM in interpreter mode, e.g. through Java.deoptimizeEverything(). For example when running the Dropbox app from start to login screen, this would take ~94 seconds on a Pixel 3 running Android 9, and now takes ~6 seconds.

Changes in 12.6.2

  • Android Java integration now supports more arm64 systems thanks to a fix contributed by Giovanni Rocca.
  • Android Java integration once again supports being used by more than one script at a time.

Changes in 12.6.3

  • Java.choose() is now working again on Android >= 8.1, thanks to a fix contributed by Eugene Kolo.
  • Android Java integration unhooking is now working again. This also means hooks are properly reverted on script unload.
  • Frida can now talk to old versions of Frida, from before the addition of per-script runtime selection.

Changes in 12.6.4

  • Build system is back in business on all platforms.

Changes in 12.6.5

  • Linux thread enumeration is now working properly on x86-64.
  • Stalker finally handles restartable Linux syscalls.

Changes in 12.6.6

  • Android Java integration is back in fully working condition on 32-bit ARM.

Changes in 12.6.7

  • Latest Chimera iOS jailbreaks are now supported; confirmed working on 1.0.8.
  • Linux injector handles target processes where the libc’s name is ambiguous, which is often an issue on Android.

Changes in 12.6.8

  • ObjC.Object now provides $moduleName, useful for determining which module owns a given class. Kudos to David Weinstein for contributing this neat feature!

Changes in 12.6.9

  • Latest unc0ver jailbreak is now fully supported.
  • Early instrumentation logic has been improved to fully support iOS 12. Kudos to Francesco Tamagni for helping get these tricky changes across the finish-line.
  • Enumeration of memory ranges is now more reliable on iOS 12, as memory ranges belonging to threads are now correctly cloaked.
  • Cloaked memory ranges are now compacted, making lookups faster.
  • Child gating is able to hold children for longer than 25 seconds. Previously these would get resumed automatically after that accidental timeout. This also affects early instrumentation on Android, as it is built on top of child gating.
  • Swift bindings support RPC and have been moved to Swift 5.0, thanks to John Coates’ awesome contribution.

Changes in 12.6.10

  • Enumeration of memory ranges is now reliable on all platforms. There was a long-standing bug when a removal ends up splitting an existing range.

Changes in 12.6.11

  • Enumeration of applications includes icons on iOS >= 12, thanks to a nice fix by CodeColorist.
  • Gadget was taught how to detect the executable and package name of Android apps, thanks to a neat contribution by gebing.
  • Cloaking of memory ranges got a critical fix affecting Windows users.

Changes in 12.6.12

  • The frida-inject tool now supports passing parameters to the script through -P/–parameters. Kudos to Eugene Kolo for contributing this neat feature.
  • Child-gating no longer deadlocks when script unload blocks. Kudos to Ioannis Gasparis for helping track this one down.
  • Child-gating is more reliable as Frida now allocates file-descriptors in a higher range to avoid them getting closed by applications calling dup2() during a fork()+exec(). This would typically happen on Android when an app called Runtime.exec(). Kudos to Ioannis Gasparis for helping track this one down.
  • The painful Android NDK upgrade to r20 landed, thanks to a slew of awesome contributions by Muhammed Ziad.
  • Error-handling was improved to avoid crashing in scenarios where we fail to initialize due to lack of permissions. Kudos to pancake for reporting.
  • The native lockdown integration for iOS that had been sitting in a branch for a very long time was finally merged. It is unfinished and considered unstable API, but had to be merged due to a major refactoring that’s in progress.
  • Stalker now allows unfollow() from the transform callback, instead of crashing the process like it used to. Kudos to Giovanni Rocca for helping fix this.
  • Gadget’s Android package name detection logic was improved to handle one edge-case not previously accounted for. Kudos to xiaobaiyey for reporting and suggesting a fix.
  • The Java.registerClass() API was improved to support specifying the super class, along with a slew of fixes for both that API and handling of arrays with generics. Big thanks to gebing for these awesome improvements.

Changes in 12.6.13

  • Constructor/destructor functions in Agent and Gadget are now finally correctly ordered, and our libc shim’s memory allocator hacks could be dropped. Those fragile hacks broke in a new and colorful way with 32-bit ARM processes on Android due to a subtle change in the toolchain’s libgcc.
  • Our libc shim now also handles __cxa_atexit() and atexit(), where the former is crucial to avoid leaks.

Changes in 12.6.14

  • The Java.registerClass() API was improved to support user-defined constructors and fields, thanks to the awesome improvements contributed by gebing.
  • Temporary files are now cleaned up on all platforms.
  • Ctrl+C is handled by frida-server on Windows to support graceful shutdown without leaving temporary files behind.

Changes in 12.6.15

  • NativePointerValue, i.e. support for passing an object with a handle property in addition to NativePointer, is once again working everywhere.

Changes in 12.6.16

  • The frida-gadget-ios meta-package is now dependency-free.

Changes in 12.6.17

  • Crash reporter integration is now compatible with iOS 12.4.
  • Java integration was improved to eagerly release global handles when a replaced method is called, to avoid running out of handles. Kudos to gebing for this nice improvement.
  • Java.retain() was added to allow storing this for later use outside a replaced Java method.
  • Support for older versions of Android should now be slightly better.
  • Frida no longer crashes on i/macOS if the target process dies while injecting a library into it.
  • Support for MIPS64 thanks to awesome contributions by Jon Wilson.
  • Memory.patchCode() no longer crashes on android-arm64, thanks to a fix by gebing.
  • Interception of already intercepted Thumb function is now working properly.
  • Frida no longer crashes when logging early in the process lifetime on iOS.
  • Simple ModuleApiResolver queries are now blazing fast.
  • Duktape runtime no longer includes the problematic Reflect builtin.
  • Interceptor C API was refactored to improve naming.

Changes in 12.6.18

  • Gadget binaries are once again stripped on all platforms.

Changes in 12.6.19

  • All APIs are now cancellable. Python and Node.js language bindings support passing a Cancellable object. Remaining language bindings work like before, and contributions are most welcome.

Changes in 12.6.20

  • DeviceManager.find_device() no longer crashes on startup.

Changes in 12.6.21

  • Future.wait_async() no longer crashes when cancelled last-minute.

Changes in 12.6.22

  • State management was improved to allow eternalize() and post() in disposed state as well.
  • The dispose() RPC export is now passed a parameter specifying the reason, to be able to differentiate between unload, exit, and exec.
  • Scripts may now be dispose()d again if an exec transition fails.
  • The frida-inject CLI tool was improved to exit on detach.
  • RpcClient no longer crashes when post_rpc_message() fails.
  • Fruity and Droidy backends are now disabled on iOS and Android, to reduce footprint size on platforms where these backends are not applicable.

Changes in 12.6.23

  • Frida no longer crashes when HostSession gets lost mid-request.

Frida 12.5 Released

This is one packed release. So many things to talk about.


The main story this time is V8. But first, a little background.

Frida uses the Duktape JavaScript runtime by default to provide scriptable access to its instrumentation core, Gum. We were originally only using V8, but as V8 used to depend on operating support for RWX pages – i.e. executable pages of memory that are also writable – we ended up adding a secondary JS runtime based on Duktape. Due to this OS constraint with V8, and the fact that Duktape lacks support for the latest JS syntax and features, I decided to make Duktape our default runtime so that scripts written for one platform wouldn’t need any syntactic changes to work on another. Duktape was basically the lowest common denominator.

Fast forward to today, and V8 no longer depends on OS support for RWX pages. It’s actually moving towards flipping between RW- and R-X by default. By doing so it means it can be used on modern iOS jailbreaks, and also on jailed iOS if the process is marked as being debugged, so it is able to run unsigned code. But there’s more; V8 can now even run JIT-less, which means Frida can use V8 on every single platform, and users no longer have to frida-compile their agents to use the latest JavaScript syntax and features. This last point only applies to trivial agents, though, as being able to split a non-trivial agent into multiple source files is still desirable. Plus, frida-compile makes it easy to use TypeScript, which is highly recommended for any non-trivial Frida agent.

So with all of that in mind, it was clearly time to upgrade our V8 to the latest and greatest. As of this release we are running 7.6.48, and we also have a much deeper integration with V8 than ever before. Both C++ memory allocations and page-level allocations are now managed by Frida, so we are able to hide these memory ranges from APIs like Process.enumerateRanges(), and also avoid poisoning the application’s own heap with allocations belonging to Frida. These details may not sound all that significant, but are actually crucial for implementing memory-dumping tools on top of Frida. Not only that, however, we also interfere less with the process that is being observed. That means there’s a smaller risk of it exhibiting a different behavior than when it runs without instrumentation.

Runtime Selection

You may remember the session.enable_jit() API. It has finally been deprecated, as you can now specify the desired runtime during script creation. E.g. using our Python bindings:

script = session.create_script(source, runtime='duk')

And using our Node.js bindings:

const script = await session.createScript(source, {
  runtime: 'v8'


Another significant change in this release is that Stalker no longer depends on RWX pages on arm64, thanks to John Coates’ awesome contribution. This means Stalker is finally much more accessible on iOS.

For those of you using Stalker on 64-bit Windows and stalking 32-bit processes, it finally handles the WOW64 transitions on newer versions of Windows. This brain-twisting improvement was contributed by Florian Märkl.


There are times when you might want to load your own shared library, perhaps containing hooks written in C/C++. On most platforms you could achieve this by using NativeFunction to call dlopen() (POSIX) or LoadLibrary() (Windows). It’s a very different story on newer versions of Android, however, as their dlopen()-implementation looks at the caller and makes decisions based on it. One such decision is whether the app is trying to access a private system API, which would make it hard for them to later remove or break that API. So as of Android 8 the implementation will return NULL when this is the case. This was a challenge that Frida solved for its own injector’s needs, but users wanting to load their own library were basically on their own.

As of Frida 12.5, there’s a brand new JavaScript API that takes care of all the platform-specific quirks for you:

const hooks = Module.load('/path/to/');
Interceptor.replace(Module.getExportByName('', 'read'),


We fixed so many Android-specific bugs in this release. For example, Module.getExportByName() on an app-bundled library no longer results in the library being loaded a second time at a different base address. This bug alone is reason enough to make sure you’ve got all your devices upgraded and running the latest release.


The iOS Chimera jailbreak is also supported thanks to Francesco Tamagni’s awesome contributions.

All the rest

There’s also lots of other improvements across platforms.

In chronological order:

  • Child gating also works on older versions of Windows. Thanks Fernando Urbano!
  • Executables are smaller on UNIX OSes as they no longer export any dynamic symbols.
  • Frida’s agent and gadget no longer crash on 32-bit Linux when loaded at a high address, i.e. where MSB is set.
  • Only one of the two frida-helper-{32,64} binaries for Linux/Android is needed, and none of them for builds without cross-arch support. This means smaller footprint and improved performance.
  • Linux/ARM64 is finally supported, with binaries uploaded as part of the release process.
  • We now provide a hint about Magisk Hide when our early instrumentation fails to instrument Zygote on Android.

Changes in 12.5.1

  • Script runtime can be specified per script, deprecating enable_jit().

Changes in 12.5.2

  • Gadget no longer crashes at script load time when using V8 on Linux and Android. Huge thanks to Leon Jacobs for reporting and helping track this one down.

Changes in 12.5.3

  • Android linker integration supports a lot more devices.
  • Android Java integration no longer crashes with “Invalid instruction” on some arm64 devices. Kudos to Jake Van Dyke for reporting and helping track this one down.
  • LineageOS 15.1 is supported after adding a missing SELinux rule.

Changes in 12.5.4

  • Hooks are no longer able to interfere with our V8 page allocator integration.
  • Android stability improved greatly after plugging a hole in our libc shim. Big thanks to Giovanni Rocca for reporting and helping track this one down!

Changes in 12.5.5

  • Apple USB devices are properly detected on Windows. Thanks @xiofee!

Changes in 12.5.6

  • Android Java integration now has a workaround for a bug in ART’s exception delivery logic, where one particular code-path assumes that there is at least one Java stack frame present on the current thread. That is however not the case on a pure native thread, like Frida’s JS thread. Simplest reproducer is Java.deoptimizeEverything() followed by Java.use() of a non-existent class name. Kudos to Jake Van Dyke for reporting and helping track this one down.
  • Android Java integration no longer crashes the process when calling Java.deoptimizeEverything() in a process unable to TCP listen().
  • Android Java integration supports JNI checked mode like it used to.
  • Node.js 12 supported in addition to 8 and 10, with prebuilds for all supported platforms.
  • Node.js bindings’ enableDebugger() method no longer requires specifying which port to listen on.

Changes in 12.5.7

  • Android teardown no longer crashes on systems where we are unable to spawn logcat for crash reporting purposes.
  • Better SuperSU integration teardown logic on Android.
  • Android Java integration now properly supports JNI checked mode, which massively improves Android ROM compatibility. Kudos to @muhzii for reporting and assisting with testing the changes.
  • V8 backend teardown no longer suffers from a use-after-free, and also no longer crashes when a WeakRef is bound late.

Changes in 12.5.8

  • Linux child-gating now handles children changing architecture, e.g. a 32-bit app doing fork+exec to run a 64-bit executable. Big thanks to @gebing for the fix.
  • Child gating no longer deadlocks in case of a fork+exec where a child process is not followed. Kudos to @gebing for the fix.
  • Module export lookups no longer fail on Android apps’ own modules.

Changes in 12.5.9

  • Our libc shim now includes memcpy(), making it safe to hook. Thanks to Giovanni Rocca for debugging and contributing the fix.
  • Interceptor.flush() now also works even when a thread has temporarily released the JS lock, e.g. while calling a NativeFunction.
  • Android Java integration no longer crashes intermittently during ART exception delivery, e.g. when hooking ClassLoader.loadClass(). Kudos to Jake Van Dyke and Giovanni Rocca for helping track this one down. This bug has been around for as long as ART has been supported, so this fix is worth celebrating. 🎉
  • Android Java integration no longer crashes processes where the JDWP transport cannot be started.

Frida 12.2 Released

Let’s talk about iOS kernel introspection. It’s been a while since Frida got basic support for introspection of the iOS kernel, but in the last months we kept improving on that. Today’s release includes significant additions to our Kernel API to work with recent 64-bit kernels.

Kernel base

You can get the kernel’s base address by reading the Kernel.base property. Having the base allows for example to calculate the slid virtual address of any symbol you already know from static analysis of the kernel cache.

The memory search API has been ported to the Kernel, so you can use Kernel.scan() (or Kernel.scanSync()) in the same way you use Memory.scan() (or Memory.scanSync()) in userland. This is a powerful primitive which, combined with the recent bit mask feature, allows you to create your own symbol finding code by searching for arm64 patterns.

KEXTs and memory ranges

With Kernel.enumerateModules() (or Kernel.enumerateModulesSync()) it’s now possible to get the names and the offsets of all the KEXTs.

Kernel.enumerateModuleRanges() (or Kernel.enumerateModuleRangesSync()) is the way to enumerate all the memory ranges defined by the Mach-O sections belonging to a module (by name) filtering by protection. The result is similar to what you can get in userland when calling Module.enumerateRanges() but it also includes the section names.

Final notes

All Kernel APIs don’t rely on NativePointer because its size depends on the user-space which doesn’t necessarily match the kernel space one. Instead all addresses are represented as UInt64 objects.

All of this, plus the existing JavaScript interfaces for reading, writing, and allocating kernel memory can provide a powerful starting point to build your own kernel analysis or vulnerability research tools.

Note that this is to be considered experimental and messing with the kernel in random ways can wildly damage your devices, so be careful, and happy hacking!


Problem: Kernel.available is false

The Kernel API is available if both of these conditions are met:

  • Your device is jailbroken
  • Frida is able to get a send right to the kernel task, either by traditional task_for_pid (0) or by accessing the host special port 4 (which is what modern jailbreaks are doing)

The recommended way to accomplish the latter is to attach to the system session, i.e. PID 0, and load your scripts there.

Problem: can’t do much with my 32-bit kernel

Yes, that could improve in the future but 32-bit iOS is quite far down on the list of priorities nowadays, but you’re very welcome to contribute and send PRs.

Problem: I was trying to do X and the kernel panicked

Don’t worry that’s normal. You can go to the /private/var/mobile/Library/Logs/CrashReporter directory on your device, or navigate to Settings -> Privacy -> Analytics -> Analytics Data, find your panic log and figure out what you (or Frida) did wrong. Remember: the Kernel is always right!

Problem: I unrecoverably damaged my device using Frida Kernel APIs

Sorry to hear, if the damage is at the hardware level and you can dedicate enough time and money you can probably repair it yourself by following tutorials at

Frida 12.1 Released

Massive changes under the hood this time. All of our dependencies have been upgraded to the latest and greatest. Let’s have a look at the highlights.

V8 7.0

Frida’s V8 dependency was previously at 6.2.2, and has now been upgraded to 7.0.242. The move to such a new version means that the V8 debugger API is gone and has been replaced with the new Inspector API, which the latest Node.js is also using. One thing that’s pretty awesome about it is that it’s natively supported by Google Chrome’s Inspector.

To start using it, just tell Frida to use V8, by calling session.enable_jit(), and then session.enable_debugger().

Or when using the CLI tools:

$ frida-trace --enable-jit --debug -f /bin/cat -i read

Then in Google Chrome, right-click and choose “Inspect”, and click on the green Node.js icon in the upper left corner of the Inspector. That’s it, you are now debugging your Frida scripts. That means a nifty console with auto-completion, pause/continue, stepping, breakpoints, profiling, and heap snapshots. What makes it really convenient is that the server listens on your host machine, so you can call enable_debugger() on a session representing a process on your USB-tethered Android device, and it all works the same.

Here’s what it looks like:

Console Profiler Heap Snapshot

Note however that V8 is currently not included in our prebuilt iOS binaries, but it should be possible to get it running again on iOS now that it’s able to run without RWX pages. We do however plan on bridging Duktape’s binary debugger protocol behind the scenes so debugging “just works” with Duktape also, though probably with a slightly reduced feature-set.

It is often quite useful to know the PID of the process that your agent is executing inside, especially in preloaded mode. So instead of requiring you to use NativeFunction to call e.g. getpid(), this is now a lot simpler as you can use the brand new property.

Anything else?

No other features, but some really nice bug-fixes. Thanks to mrmacete we are now able to attach to* processes on iOS 11.x. And thanks to viniciusmarangoni this release also packs a couple of goodies for frida-java, fixing one Android 8.0 regression and adding the ability to properly instrument system_server.


Frida 12.0 Released

As some of you may have picked up on, there may be a book on Frida in the works. In my day to day at NowSecure I spend a good chunk of time as a user of Frida’s APIs, and consequently I’m often reminded of past design decisions that I’ve since come to regret. Even though I did address most of them over the years, some were so painful to address that I kept them on the backburner. Fast forward to today, and the thought of publishing a book with all these in mind got me thinking that it was time to bite the bullet.

This is why I’m stoked to announce Frida 12. We have finally reached a point in Frida’s evolution where our foundations can be considered sufficiently stable for a book to be written.

Let’s have a look at the changes.

CLI tools

One thing that caused a bit of confusion in the past was the fact that our Python bindings also came with some CLI tools. Frida is a toolkit for building tools, and even though we provide a few sample tools it should be up to you if you want to have them installed.

Up until now this meant anyone building a tool using our Python bindings would end up depending on colorama, prompt-toolkit, and pygments, because our CLI tools happen to depend on those.

Well, that changes now. If you do:

$ pip install frida

You will now only get our Python bindings. Nothing more. And this package has zero dependencies.

The CLI tools might still be useful to you, though, so to install those do:

$ pip install frida-tools

Convenience APIs in bindings

Something that seemed like a great idea at the time was having our language bindings provide some convenience APIs on the Session object. The thinking was that simple use-cases that only need to enumerate loaded modules and perhaps a few memory ranges, to then read or write memory, wouldn’t have to load their own agent. So both our Python and our Node.js bindings did this behind the scenes for you.

Back then it was somewhat tedious to communicate with an agent as the rpc API didn’t exist, but even so, it was a bad design decision. The JS APIs are numerous and not all can be exposed without introducing new layers of complexity. Another aspect is that every language binding would have to duplicate such convenience APIs, or we would have to add core APIs that bindings could expose. Both are terrible options, and cause confusion by blurring the lines, ultimately confusing people new to Frida. Granted, it did make things easier for some very simple use-cases, like memory dumping tools, but for everybody else it just added bloat and confusion.

These APIs are now finally gone from our Python and Node.js bindings. The other bindings are unaffected as they didn’t implement any such convenience APIs.

Node.js bindings

It’s been a few years since our Node.js bindings were written, and since then Node.js has evolved a lot. It now supports ES6 classes, async / await, arrow functions, Proxy objects, etc.

Just the Proxy support alone means we can simplify rpc use-cases like:

const api = await script.getExports();
const result = await api.add(2, 5);

to just:

const result = await script.exports.add(2, 5);

Some of you may also prefer writing your application in TypeScript, which is an awesome productivity boost compared to old-fashioned JavaScript. Not only do you get type checking, but if you’re using an editor like VS Code you also get type-aware refactoring and amazing code completion.

However, for type checking and editor features to really shine, it is crucial to have type definitions for your project’s dependencies. This is rarely ever an issue these days, except for those of you using Frida’s Node.js bindings. Up until now we didn’t provide any type definitions. This has finally been resolved. Rather than augmenting our bindings with type definitions, I decided to rewrite them in TypeScript instead. This means we also take advantage of modern language features like ES6 classes and async / await.

We could have stopped there, but those of you using our Node.js bindings from TypeScript would still find this a bit frustrating:'message', (message, data) => {

Here, the compiler knows nothing about which events exist on the Script object, and what the callback’s signature is supposed to be for this particular event. We’ve finally fixed this. The API now looks like this:

script.message.connect((message, data) => {

Voilà. Your editor can even tell you which events are supported and give you proper type checking for the code in your callback. Sweet!


Something that’s caused some confusion in the past is the observation that accessing this.context.pc from onEnter or onLeave would give you the return address, and not the address of the instruction that you put the hook on. This has finally been fixed. Also, this.context.sp now points at the return address on x86, instead of the first argument. The same goes for Stalker when using call probes.

As part of this refactoring breaking our backtracer implementations, I also improved our default backtracer on Windows.


You might have wondered why frida.get_usb_device() would give you a Device whose type was ‘tether’. It is now finally ‘usb’ as you’d expect. So our language bindings are finally consistent with our core API.

Changes in 12.0.1

  • core: fix argument access on 32-bit x86
  • core: update Stalker to the new CpuContext semantics
  • python: publish the correct README to PyPI
  • python: fix the Windows build system

Changes in 12.0.2

  • core: upgrade to Capstone’s next branch
  • core: fix the DbgHelp backtracer on Windows and update to latest DbgHelp
  • python: fix long description
  • java: fix hooking of java.lang.Class.getMethod() – thanks 0x3430D!

Changes in 12.0.3

  • core: fix the iOS build system broken by Capstone upgrade

Changes in 12.0.4

  • core: fix i/macOS libc++ initialization on early instrumentation – thanks mrmacete!
  • core: add support for memory search with mask – thanks mrmacete!
  • core: fix InvocationContext.get_return_address()
  • node: fix crash on RPC object return from an async function

Changes in 12.0.5

  • core: fix arm64 crashes caused by Capstone upgrade, where test-and-branch decoding logic failed to decode negative offsets – kudos to mrmacete for discovering and fixing this one!
  • core: fix MIPS regressions and one crasher – thanks r0ck3tAKATrashPanda!

Changes in 12.0.6

  • python: improve spawn() to support unicode aux options on Python 2.x
  • java: fix Java.registerClass() when cache dir is missing
  • java: make the temporary file naming configurable

Changes in 12.0.7

  • core: fix early instrumentation on iOS 11.3.1 through 11.4.1 – thanks mrmacete!

Changes in 12.0.8

  • core: fix launching of Android apps with custom process name – thanks giantpune!
  • java: fix ClassLinker field offset detection on Android 8.0


Frida 11.0 Released

It’s time to overhaul the spawn() API and fix some rough edges in the spawn- and child-gating APIs.


Say you’re using Frida’s Python bindings, you’d currently do:

pid = device.spawn(["/bin/cat", "/etc/passwd"])

Or to spawn an iOS app:

pid = device.spawn([""])

Well, that’s pretty much all you could do with that API really… except one thing that wasn’t exposed by the Python and Node.js bindings. We’ll get to that in a bit. Before we go there, let’s take a look at the underlying API in frida-core, which these bindings expose to different languages:

namespace Frida {
	public class Device : GLib.Object {
		public async uint spawn (string path,
			string[] argv, string[] envp)
			throws Frida.Error;
		public uint spawn_sync (string path,
			string[] argv, string[] envp)
			throws Frida.Error;

That’s Vala code by the way, which is the language that frida-core is written in. It’s a C#-like language that compiles to C, and it’s pretty awesome. But I digress. The first method, spawn() is asynchronous, allowing the calling thread to do other things while the call is in progress, whereas spawn_sync() blocks until the operation completes.

Those two methods compile down to the following three C functions:

void frida_device_spawn (FridaDevice * self,
    const gchar * path,
    gchar ** argv, int argv_length,
    gchar ** envp, int envp_length,
    GAsyncReadyCallback callback, gpointer user_data);
guint frida_device_spawn_finish (FridaDevice * self,
    GAsyncResult * result, GError ** error);
guint frida_device_spawn_sync (FridaDevice * self,
    const gchar * path,
    gchar ** argv, int argv_length,
    gchar ** envp, int envp_length,
    GError ** error);

The first two constitute spawn(), where you’d call the first giving it a callback, and once that callback gets called you’d call the second one, spawn_finish(), giving it the GAsyncResult your callback was given. The return value is the PID, or, in case it failed, the error out-argument explains what went wrong. This is the GIO async pattern in case you’re curious.

As for the third, spawn_sync(), this is what Frida’s Python bindings use. Our Node.js bindings actually use the first two, as those bindings are fully asynchronous. Someday it would be nice to also migrate our Python bindings to be fully async, by integrating with the async/await support introduced in Python 3.5.

Anyway, returning to the examples above, I mentioned there was something not exposed. If you look closely at the frida-core API you’ll notice that there’s the envp string-array. Peeking under the hood of the bindings, you’d realize we did indeed not expose this, and we actually did this:

  envp = g_get_environ ();
  envp_length = g_strv_length (envp);

So that means we passed along whatever the Python process’ environment happened to be. That’s definitely not good if the actual spawning happened on another system entirely, like on a connected iOS or Android device. What made this slightly less problematic was the fact that envp was ignored when spawning iOS and Android apps, and only used when spawning regular programs.

Another issue with this old API is that the declaration, string[] envp, means it isn’t nullable, which it would have been if the declaration had been: string[]? envp. That means there is no way to distinguish between wanting to spawn without any environment, which intuitively would mean “use defaults”, and an empty environment.

As I was about to fix this aspect of the API, I realized that it was time to also fix a few other long-standing issues with it, like being able to:

  • provide just a few extra environment variables on top of the defaults
  • set the working directory
  • customize stdio redirection
  • pass along platform-specific options

Up until this point we have always redirected stdio to our own pipes, and streamed any output through the output signal on Device. There was also Device.input() for writing to stdin. Those APIs are still the same, the only difference is that we no longer do such redirection by default. Most of you were probably not too bothered with this, though, as we didn’t implement such redirection for iOS and Android apps. Starting with this release we do however finally implement it for iOS apps.

By now you’re probably wondering what the new API looks like. Let’s have a look:

namespace Frida {
	public class Device : GLib.Object {
		public async uint spawn (string program,
			Frida.SpawnOptions? options = null)
			throws Frida.Error;
		public uint spawn_sync (string program,
			Frida.SpawnOptions? options = null)
			throws Frida.Error;
	public class SpawnOptions : GLib.Object {
		public string[]? argv { get; set; }
		public string[]? envp { get; set; }
		public string[]? env { get; set; }
		public string? cwd { get; set; }
		public Frida.Stdio stdio { get; set; }
		public GLib.VariantDict aux { get; }

		public SpawnOptions ();

So going back to the Python examples at the beginning, those still work without any changes. But, instead of:


You can now also do:


As the first argument is the program to spawn. You can still pass an argv here and that will be used to set the argv option, meaning that argv[0] will be used for the program argument. You can also do this:

device.spawn("/bin/busybox", argv=["/bin/cat", "/etc/passwd"])

And if you’d like to replace the entire environment instead of using defaults:

device.spawn("/bin/ls", envp={ "CLICOLOR": "1" })

Though in most cases you probably only want to add/override a few environment variables, which is now also possible:

device.spawn("/bin/ls", env={ "CLICOLOR": "1" })

You might also want to use a different working directory:

device.spawn("/bin/ls", cwd="/etc")

Or perhaps you’d like to redirect stdio:

device.spawn("/bin/ls", stdio="pipe")

The stdio default value is inherit, as mentioned earlier.

We have now covered all of the SpawnOptions, except the last of them: aux. This is a dictionary for platform-specific options. Setting such options is pretty simple with the Python bindings: any keyword-argument not recognized will end up in that dictionary.

For example, to launch Safari and tell it to open a specific URL:

device.spawn("", url="")

Or perhaps you’d like to spawn an i/macOS program with ASLR disabled:

device.spawn("/bin/ls", aslr="disable")

Another example is spawning an Android app with a specific activity:

spawn("", activity=".SecuritySettings")

And that’s actually all of the aux options we currently support – and what’s great is that we can add new ones without needing to update our bindings.

But before we move on, let’s take a quick look at what this new API would look like using our Node.js bindings:

const pid = await device.spawn('/bin/sh', {
  argv: ['/bin/sh', '-c', 'ls /'],
  env: {
    'BADGER': 'badger-badger-badger',
    'SNAKE': true,
    'MUSHROOM': 42,
  cwd: '/usr',
  stdio: 'pipe',
  aslr: 'auto'

So as you can see, the second argument is an object with options, and those not recognized end up in the aux dictionary.

Remaining changes in 11.0.0

Let’s just summarize the remaining changes, starting with the Device class:

  • enumerate_pending_spawns() is now enumerate_pending_spawn() to be grammatically correct.
  • The spawned signal has been renamed to spawn-added, and there is now also spawn-removed.
  • The delivered signal has been renamed to child-added, and there is now also child-removed.

The final change is that the Child class’ path, argv, and envp properties are now all nullable. This is to be able to discern e.g. “no envp provided” from “empty envp provided”.

Changes in 11.0.1

  • core: fix stack alignment of agent thread for 32-bit ARM processes
  • core: fix fragile SELinux rule patching

Changes in 11.0.2

  • core: plug Mach ports leaks in spawn()-logic on i/macOS (long-standing issue)

Changes in 11.0.3

  • core: fix crash on iPhone 8 and X caused by bad tls_base calculation on arm64

Changes in 11.0.4

  • python: update metadata and bump requirements

Changes in 11.0.5

  • core: fix compatibility issue with Electra’s Tweak Injector
  • core: fix process name truncation in enumerate_processes() on iOS
  • java: Java.registerClass() now supports overloads
  • packaging: every new release now comes with packages for Fedora and Ubuntu

Changes in 11.0.6

  • python: fix dependency spec so REPL works again when installed from PyPI

Changes in 11.0.7

  • core: better child gating API coverage on Windows
  • python: 2.7 binaries for Linux are no longer broken when installed from PyPI

Changes in 11.0.8

  • core: fix race resulting in crash on system session setup on some platforms
  • python: fix deadlock on interpreter shutdown

Changes in 11.0.9

  • java: fix issue preventing Java.registerClass() during early instrumentation

Changes in 11.0.10

  • core: fix crash when enumerating imports/exports of ELF modules with an empty DT_GNU_HASH

Changes in 11.0.11

  • java: fix type compatibility checking, which typically resulted in the wrong overload getting called, in turn causing the VM to abort()

Changes in 11.0.12

  • core: fix compatibility issues with the latest macOS Mojave and iOS 12 betas
  • core: improve the iOS Kernel API available in the system session, i.e. PID 0
  • frida-trace: fix crash when tracer scripts send() arbitrary data

Changes in 11.0.13

  • core: fix hang on teardown of script that was never load()ed


So that’s about it. If you didn’t read about the Frida 10.8 release that happened last week, make sure you go read about it here.


Frida 10.8 Released

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:

from __future__ import print_function
import frida
from frida.application import Reactor
import threading

class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda _:

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on("delivered", lambda child:
                lambda: self._on_delivered(child)))

    def run(self):
        self._reactor.schedule(lambda: self._start())

    def _start(self):
        argv = ["/bin/sh", "-c", "cat /etc/hosts"]
        print("✔ spawn(argv={})".format(argv))
        pid = self._device.spawn(argv)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:

    def _instrument(self, pid):
        print("✔ attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on("detached", lambda reason:
                self._on_detached(pid, session, reason)))
        print("✔ enable_child_gating()")
        print("✔ create_script()")
        script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, 'open'), {
  onEnter: function (args) {
      type: 'open',
      path: Memory.readUtf8String(args[0])
        script.on("message", lambda message, data:
                lambda: self._on_message(pid, message)))
        print("✔ load()")
        print("✔ resume(pid={})".format(pid))

    def _on_delivered(self, child):
        print("⚡ delivered: {}".format(child))

    def _on_detached(self, pid, session, reason):
        print("⚡ detached: pid={}, reason='{}'"
            .format(pid, reason))
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("⚡ message: pid={}, payload={}"
            .format(pid, message["payload"]))

app = Application()

And action:

$ python3
✔ spawn(argv=['/bin/sh', '-c', 'cat /etc/hosts'])
✔ attach(pid=42401)
✔ enable_child_gating()
✔ create_script()
✔ load()
✔ resume(pid=42401)
⚡ message: pid=42401,
↪payload={'type': 'open', 'path': '/dev/tty'}
⚡ detached: pid=42401, reason='process-replaced'
⚡ delivered: Child(pid=42401, parent_pid=42401,
↪path="/bin/cat", argv=['cat', '/etc/hosts'],
↪envp=['SHELL=/bin/bash', 'TERM=xterm-256color', …],
✔ attach(pid=42401)
✔ enable_child_gating()
✔ create_script()
✔ load()
✔ resume(pid=42401)
⚡ message: pid=42401,
↪payload={'type': 'open', 'path': '/etc/hosts'}
⚡ detached: pid=42401, reason='process-terminated'


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.:

rpc.exports = {
  dispose: function () {

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!

Frida 10.7 Released

iOS users rejoice: Frida is now compatible with the latest Electra jailbreak on iOS 11! At the time of this writing that means 1.0.4, but now that Electra seems to have stabilized it should be safe to assume that we’ll also be compatible with future updates. Just make sure you’re not running anything older than 1.0.4.

In other news, our Android support has improved significantly over the last releases, so if you’re even slightly behind: go grab the latest 10.7.x right away.


Frida 10.6 Released

It’s time for some big updates to Frida’s Gadget.

This component is really useful when dealing with jailed iOS and Android devices, but is now also able to cover a lot of other scenarios.

Its environment variables are now gone, and have been replaced by an optional configuration file. Because some apps may look for loaded libraries with “Frida” in their name as part of their “anti-debug” defenses, we now allow you to rename Gadget’s binary however you like. Along with this it now also supports three different interaction types.

It can listen on a TCP port, like before, and it can also load scripts from the filesystem and run fully autonomously. The latter part used to be really limited but is now really flexible, as you can even tell it to load scripts from a directory, where each script may have filters. This is pretty useful for system-wide tampering, and should allow for even more interesting use-cases.

So without further ado, I would urge you all to check out the brand new docs available here.


Frida 10.5 Released

The midnight oil has been burning and countless cups of coffee have been consumed here at NowSecure, and boy do we have news for you this time.

Continuing in the spirit of last release’ low-level bag of goodies, we’ll be moving one level up the stack this time. We are going to introduce a brand new way to use new CodeWriter APIs, enabling you to weave in your own instructions into the machine code executed by any thread of your choosing. We’re talking lazy dynamic recompilation on a per-thread basis, with precise control of the compilation process.

But first a little background. Most people using Frida are probably using the Interceptor API to perform inline hooking, and/or doing method swizzling or replacement through the ObjC and Java APIs. The idea is typically to modify some interesting API that you expect to be called, and be able to divert execution to your own code in order to observe, augment, or fully replace application behavior.

One drawback to such approaches is that code or data is modified, and such changes can be trivially detected. This is fine though, as being invisible to the hosting process’ own code is always going to be a cat and mouse game when doing in-process instrumentation.

These techniques are however quite limited when trying to answer the question of “behind this private API, which other APIs actually get called for a given input?”. Or, when doing reversing and fuzzing, you might want to know where execution diverges between two known inputs to a given function. Another example is measuring code coverage. You could use Interceptor’s support for instruction-level probes, first using a static analysis tool to find all the basic blocks and then using Frida to put single-shot probes all over the place.

Enter Stalker. It’s not a new API, but it’s been fairly limited in what it allowed you to do. Think of it as a per-thread code-tracer, where the thread’s original machine code is dynamically recompiled to new memory locations in order to weave in instrumentation between the original instructions.

It does this recompilation lazily, one basic-block at a time. Considering that a lot of self-modifying code exists, it is careful about caching compiled blocks in case the original code changes after the fact.

Stalker also goes to great lengths to recompile the code such that side-effects are identical. E.g. if the original instruction is a CALL it will make sure that the address of the original next instruction is what’s pushed on the stack, and not the address of the next recompiled instruction.

Anyway, Stalker has historically been like a pet project inside of a pet project. A lot of fun, but other parts of Frida received most of my attention over the years. There have been some awesome exceptions though. Me and @karltk did some fun pair-programming sessions many years ago when we sat down and decided to get Stalker working well on hostile code. At some later point I put together CryptoShark in order get people excited about its potential. Some time went by and suddenly Stalker received a critical bug-fix contributed by Eloi Vanderbeken. Early this year, Antonio Ken Iannillo jumped on board and ported it to arm64. Then, very recently, Erik Smit showed up and fixed a critical bug where we would produce invalid code for REP-prefixed JCC instructions. Yay!

Stalker’s API has so far been really limited. You can tell it to follow a thread, including the thread you’re in, which is useful in combination with inline hooking, i.e. Interceptor. The only two things you could do was:

  1. Tell it which events you’re interested in, e.g. call: true, which will produce one event per CALL instruction. This means Stalker will add some logging code before each such instruction, and that would log where the CALL happened, its target, and its stack depth. The other event types are very similar.
  2. Add your own call probes for specific targets, giving you a synchronous callback into JavaScript when a CALL is made to a specific target.

I’m super-excited to announce that we’ve just introduced a third thing you can do with this API, and this one is a game changer. You can now customize the recompilation process, and it’s really easy:

var appModule = Process.enumerateModulesSync()[0];
var appStart = appModule.base;
var appEnd = appStart.add(appModule.size);

Process.enumerateThreadsSync().forEach(function (thread) {
  console.log('Stalking ' +;

  Stalker.follow(, {
    transform: function (iterator) {
      var instruction =;

      var startAddress = instruction.address;
      var isAppCode = >= 0 &&
 === -1;

      do {
        if (isAppCode && instruction.mnemonic === 'ret') {
          iterator.putCmpRegI32('eax', 60);
          iterator.putJccShortLabel('jb', 'nope', 'no-hint');

          iterator.putCmpRegI32('eax', 90);
          iterator.putJccShortLabel('ja', 'nope', 'no-hint');



      } while ((instruction = !== null);

function onMatch (context) {
  console.log('Match! pc=' + context.pc +
      ' rax=' + context.rax.toInt32());

The transform callback gets called synchronously whenever a new basic block is about to be compiled. It gives you an iterator that you then use to drive the recompilation-process forward, one instruction at a time. The returned Instruction tells you what you need to know about the instruction that’s about to be recompiled. You then call keep() to allow Stalker to recompile it as it normally would. This means you can omit this call if you want to skip some instructions, e.g. because you’ve replaced them with your own code. The iterator also allows you to insert your own instructions, as it exposes the full CodeWriter API of the current architecture, e.g. X86Writer.

The example above determines where the application’s own code is in memory, and adds a few extra instructions before every RET instruction in any code belonging to the application itself. This code checks if eax contains a value between 60 and 90, and if it does, calls out to JavaScript to let it implement arbitrarily complex logic. This callback can read and modify registers as it pleases. What’s nice about this approach is that you can insert code into hot code-paths and selectively call into JavaScript, making it easy to do really fast checks in machine code but offload more complex tasks to a higher level language. You can also Memory.alloc() and have the generated code write directly there, without entering into JavaScript at all.

So that’s the big new thing in 10.5. Special thanks to @asabil who helped shape this new API.

In closing, the only other big change is that the Instruction API now exposes a lot more details of the underlying Capstone instruction. Stalker also uses a lot less memory on both x86 and arm64, and is also more reliable. Lastly, Process.setExceptionHandler() is now a documented API, along with our SQLite API.


Frida 10.4 Released

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.

Considering that Frida needs to generate and transform quite a bit of machine code for its own needs, e.g. to implement Interceptor and Stalker, it should come as no surprise that we already have C APIs to do these things across six different instruction set flavors. Initially these APIs were so barebones that I didn’t see much value in exposing them to JavaScript, but after many years of interesting internal use-cases they’ve evolved to the point where the essential bits are now covered pretty well.

So with 10.4 we are finally exposing all of these APIs to JavaScript. It’s also worth mentioning that these new bindings are auto-generated, so future additions will be effortless.

Let’s take a look at an example on x86:

var getLivesLeft = Module.findExportByName('',
var maxPatchSize = 64; // Do not write out of bounds, may be
                       // a temporary buffer!
Memory.patchCode(getLivesLeft, maxPatchSize, function (code) {
  var cw = new X86Writer(code, { pc: getLivesLeft });
  cw.putMovRegU32('eax', 9999);

Which means we replaced the beginning of our target function with simply:

mov eax, 9999

I.e. assuming the return type is int, we just replaced the function body with return 9999;.

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:

var multiply = new NativeCallback(function (a, b) {
  return a * b;
}, 'int', ['int', 'int']);

var impl = Memory.alloc(Process.pageSize);

Memory.patchCode(impl, 64, function (code) {
  var cw = new X86Writer(code, { pc: impl });

  cw.putMovRegU32('eax', 42);

  var stackAlignOffset = Process.pointerSize;
  cw.putSubRegImm('xsp', stackAlignOffset);

  cw.putCallAddressWithArguments(multiply, ['eax', 7]);

  cw.putAddRegImm('xsp', stackAlignOffset);


  cw.putMovRegU32('eax', 43);



var f = new NativeFunction(impl, 'int', []);

Though that’s quite a few hoops just to multiply 42 by 7, the idea is to illustrate how calling functions, even back into JavaScript, and jumping to labels, is actually quite easy.

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:

var impl = Memory.alloc(Process.pageSize);

Memory.patchCode(impl, Process.pageSize, function (code) {
  var cw = new X86Writer(code, { pc: impl });

  var libcPuts = Module.findExportByName(null, 'puts');
  var rl = new X86Relocator(libcPuts, cw);

  while (rl.readOne() !== 0) {
    console.log('Relocating: ' + rl.input.toString());


var puts = new NativeFunction(impl, 'int', ['pointer']);

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 available. E.g. xax resolves to eax vs rax depending on the kind of process you are in.


Frida 10.0 Released

This time we’re kicking it up a notch. We’re bringing you stability improvements and state of the art JavaScript support.

Let’s talk about the stability improvements first. We fixed a heap corruption affecting all Linux users. This one was particularly hard to track down, but rr saved the day. The other issue was a crash on unload in the Duktape runtime, affecting all OSes.

Dependencies were also upgraded, so as of Frida 10.0.0 you can now enjoy V8 6.0.124, released just days ago. We also upgraded Duktape to the latest 2.1.x. The Duktape upgrade resulted in slight changes to the bytecode semantics, which meant we had to break our API slightly. Instead of specifying a script’s name at load time, it is now specified when compiling it to bytecode, as this metadata is now included in the bytecode. This makes a lot more sense, so it was a welcome change.

Beside V8 and Duktape we’re also using the latest GLib, Vala compiler, etc. These upgrades also included JSON-GLib, which recently ditched autotools in favor of Meson. This is excellent news, as we’re also planning on moving to Meson down the road, so we’ve now done the necessary groundwork for making this happen.

So that’s about it. This upgrade should not require any changes to existing code – unless of course you are among the few using the bytecode API.


Frida 9.0 Released

Some big changes this time. We now use our Duktape-based JavaScript runtime by default on all platforms, iOS app launching no longer piggybacks on Cydia Substrate, and we are bringing some massive performance improvements. That, and some bugfixes.

Let’s talk about Duktape first. Frida’s first JS runtime was based on V8, and I’m really happy about that choice. It is however quite obvious that there are use-cases where it is a bad fit.

Some systems, e.g. iOS, don’t allow RWX memory1, and V8 won’t run without that. Another example is resource-constrained embedded systems where there just isn’t enough memory. And, as reported by users from time to time, some processes decide to configure their threads to have tiny stacks. V8 is however quite stack-hungry, so if you hook a function called by any of those threads, it won’t necessarily be able to enter V8, and your hooks appear to be ignored2.

Another aspect is that V8 is way more expensive than Duktape for the native ⇔ JS transitions, so if your Frida agent is all about API hooks, and your hooks are really small, you might actually be better off with Duktape. Garbage collection is also more predictable with Duktape, which is good for hooking time-sensitive code.

That said, if your agent is heavy on JavaScript, V8 will be way faster. It also comes with native ES6 support, although this isn’t too big a deal since non-trivial agents should be using frida-compile, which compiles your code to ES5.

So the V8 runtime is not going away, and it will remain a first-class citizen. The only thing that’s changing is that we pick Duktape by default, so that you are guaranteed to get the same runtime on all platforms, with a high probability that it’s going to work.

However, if your use-case is JS-heavy, all you have to do is call Session#enable_jit() before the first script is created, and V8 will be used. For our CLI tools you may pass –enable-jit to get the same effect.

That was Duktape. What’s the story about app launching and Substrate, then? Well, up until now our iOS app launching was piggybacking on Substrate. This was a pragmatic solution in order to avoid going into interoperability scenarios where Frida and Substrate would both hook posix_spawn() in launchd and xpcproxy, and step on each other.

It was however on my long-term TODO to fix this, as it added a lot of complexity in other areas. E.g. an out-of-band callback mechanism so our Substrate plugin could talk back to us at load time, having to manage temporary files, etc. In addition to that, it meant we were depending on a closed source third-party component, even though it was a soft-dependency only needed for iOS app launching. But still, it was the only part of Frida that indirectly required permanent modifications to the running system, and we really want to avoid that.

Let’s have a look at how the new app launching works. Imagine that you ran this on your host machine that’s got a jailbroken iOS device connected to it over USB:

$ frida-trace -U -f com.atebits.Tweetie2 -i open

We’re telling it to launch Twitter’s iOS app and trace functions named open. As a side-note, if you’re curious about the details, frida-trace is written in Python and is less than 900 lines of code, so it might be a good way to learn more about building your own tools on top of Frida. Or perhaps you’d like to improve frida-trace? Even better!

The first part that it does is that it gets hold of the first USB device and launches the Twitter app there. This boils down to:

import frida

device = frida.get_usb_device()
pid = device.spawn(["com.atebits.Tweetie2"])

What now happens behind the scenes is this:

  1. We inject our launchd.js agent into launchd (if not done already).
  2. Call the agent’s RPC-exported prepareForLaunch() giving it the identifier of the app we’re about to launch.
  3. Call SBSLaunchApplicationWithIdentifierAndLaunchOptions() so SpringBoard launches the app.
  4. Our launchd.js agent then intercept launchd’s __posix_spawn() and adds POSIX_SPAWN_START_SUSPENDED, and signals back the identifier and PID. This is the /usr/libexec/xpcproxy helper that will perform an exec()-style transition to become the app.
  5. We then inject our xpcproxy.js agent into this so it can hook __posix_spawn() and add POSIX_SPAWN_START_SUSPENDED just like our launchd agent did. This one will however also have POSIX_SPAWN_SETEXEC, so that means it will replace itself with the app to be launched.
  6. We resume() the xpcproxy process and wait for the exec to happen and the process to be suspended.

At this point we let the device.spawn() return with the PID of the app that was just launched. The app’s process has been created, and the main thread is suspended at dyld’s entrypoint. frida-trace will then want to attach to it so it can load its agent that hooks open. So it goes ahead and does something similar to this:

session = device.attach(pid)
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, 'open'), {
  onEnter: function () {

Now that it has applied the instrumentation, it will ask Frida to resume the process so the main thread can call main() and have some fun:


Note that I did skip over a few details here, as the attach() operation is actually a bit more complicated due to how uninitialized the process is, but you can read more about that here.

Finally, let’s talk about footprint and performance. First, let’s examine how much disk space is required when Frida is installed on an iOS device and is in a fully operational state:

That’s the 64-bit version, which is only 1.87 MB xz-compressed. The 32-bit version is obviously even smaller. Quite a few optimizations at play here:

  • We used to write the frida-helper binary out to a temporary file and spawn it. The meat of the frida-helper program is now statically linked into frida-server, and its entitlements have been boosted along with it. This binary is only necessary when Frida is used as a plugin in an unknown process, i.e. where we cannot make any guarantees about entitlements and code-signing. In the frida-server case, however, it is able to guarantee that all such constraints are met.
  • The library that we inject into processes to be instrumented, frida-agent.dylib, is no longer written out to a temporary file. We use our own out-of-process dynamic linker to map it from frida-server’s memory and directly into the address space of the target process. These mappings are made copy-on-write, so that means it is as memory-efficient as the old dlopen() approach was.
  • V8 was disabled for the iOS binaries as it’s only really usable on old jailbreaks where the kernel is patched to allow RWX pages. (If V8 is important to your use-case, you can build it like this: make server-ios FRIDA_DIET=no)
  • The iOS package has been split into two, “Frida” for 64-bit devices, and “Frida for 32-bit devices” for old devices.
  • Getting rid of the Substrate dependency for iOS app launching also meant we got rid of FridaLoader.dylib. This is however a very minor improvement.

Alright, so that’s disk footprint. How about memory usage?

Nice. How about performance? Let’s have a look:

Note that these measurements include the time spent communicating from the macOS host to the iOS device over USB.


1 Except if the process has an entitlement, although that’s limited to just one region.

2: It is technically possible to work around this by having a per-thread side-stack that we switch to before calling into V8. We did actually have this partially implemented in the past. Might be something we should revive in the longer term.

Frida 8.1 Released

It’s time for a release, and this time we have some big new things for those of you building Frida-based tools, plus a few additional goodies. Let’s start with the first part.

There’s no doubt that Frida’s JavaScript API is fairly low-level and only meant to provide low-level building blocks that don’t pertain to just one specific use-case. If your use-case involves grabbing screenshots on iOS, for example, this is not functionality one would expect to find in Frida itself.

You may then wonder how different tools with common features are supposed to share agent code with each other, and luckily the answer is not “copy paste”. We have a growing ecosystem of Frida-specific libraries, like frida-screenshot, frida-uikit, frida-trace, etc.

Perhaps some of you would be interested in APIs for instrumenting backend software written in Java, .NET, Python, Ruby, or Perl, or perhaps you would want to trace crypto APIs across different OSes and libraries, or some other cool idea. I would then highly recommend that you publish your module to npm, perhaps naming your module frida-$name to make it easy to discover.

Now you might be asking “but Frida does not support require(), how can I even split my agent code into multiple files in the first place?”. I’m glad you asked! This is where a handy little CLI tool called frida-compile enters the picture.

You give it a .js file as input and it will take care of bundling up any other files it depends on into just one file. But unlike a homegrown concatenation solution using cat, the final result also gets an embedded source map, which means filenames and line numbers in stack traces are meaningful. Modules are also separated into separate closures so variables are contained and never collide. You can also use the latest JavaScript syntax, like arrow functions, destructuring, and generator functions, as it compiles the code down to ES5 syntax for you. This means that your code also runs on our Duktape-based runtime, which you are forced to use if you use Frida on a jailed iOS device, or on a jailbroken iOS device running iOS >= 9.

In order to give you a short feedback loop while developing, frida-compile also provides a watch mode through -w, so you get instant incremental builds as you develop your agent.

Anyway, enough theory. Let’s look at how we can use an off-the-shelf web application framework from npm, and inject that into any process.

First, make sure you have the latest version of Node.js installed. Next, create an empty directory and paste this into a file named “package.json”:

  "name": "hello-frida",
  "version": "1.0.0",
  "scripts": {
    "prepublish": "npm run build",
    "build": "frida-compile agent -o _agent.js",
    "watch": "frida-compile agent -o _agent.js -w"
  "devDependencies": {
    "express": "^4.14.0",
    "frida-compile": "^2.0.6"

Then in agent.js, paste the following code:

const express = require('express');

const app = express();

  .get('/ranges', (req, res) => {
      protection: '---',
      coalesce: true
  .get('/modules', (req, res) => {
  .get('/modules/:name', (req, res) => {
    try {
    } catch (e) {
  .get('/modules/:name/exports', (req, res) => {
  .get('/modules/:name/imports', (req, res) => {
  .get('/objc/classes', (req, res) => {
    if (ObjC.available) {
    } else {
      res.status(404).send('Objective-C runtime not available in this process');
  .get('/threads', (req, res) => {


Install frida-compile and build your agent in one step:

$ npm install

Then load the generated _agent.js into a running process:

$ frida Spotify -l _agent.js

You can now hit it with HTTP requests:

$ curl
$ curl
$ curl
$ curl
$ curl
$ curl
$ curl

Sweet. We just built a process inspection REST API with 7 different endpoints in fewer than 50 lines of code. What’s pretty cool about this is that we used an off-the-shelf web application framework written for Node.js. You can actually use any existing modules that rely on Node.js’ built-in net and http modules. Like an FTP server, IRC client, or NSQ client.

So up until this release you could use Frida-specific modules like those mentioned earlier. You could also use thousands of other modules from npm, as most of them don’t do any I/O. Now with this release you also get access to all net and http based modules, which opens up Frida for even more cool use-cases.

In case you are curious how this was implemented, I added Socket.listen() and Socket.connect() to Frida. These are minimal wrappers on top of GIO’s SocketListener and SocketClient, which are already part of Frida’s technology stack and used by Frida for its own needs. So that means our footprint stays the same with no dependencies added. Because frida-compile uses browserify behind the scenes, all we had to do was plug in our own builtins for net and http. I simply ported the original net and http modules from Node.js itself.

This release also brings some other goodies. One long-standing limitation with NativeFunction is that calling a system API that requires you to read errno (UNIX) or call GetLastError() (Windows) would be tricky to deal with. The challenge is that Frida’s own code might clobber the current thread’s error state between your NativeFunction call and when you try to read out the error state.

Enter SystemFunction. It is exactly like NativeFunction, except that the call returns an object wrapping the returned value and the error state right afterwards. Here’s an example:

const open = new SystemFunction(
    Module.findExportByName(null, 'open'),
    ['pointer', 'int']);
const O_RDONLY = 0;

const path = Memory.allocUtf8String('/inexistent');
const result = open(path, O_RDONLY);
console.log(JSON.stringify(result, null, 2));
 * Which on Darwin typically results in the following output:
 * {
 *   "value": -1,
 *   "errno": 2
 * }
 * Where 2 is ENOENT.

This release also lets you read and modify this system error value from your NativeCallback passed to Interceptor.replace(), which might come handy if you are replacing system APIs. Note that you could already do this with Interceptor.attach(), but that’s not an option in cases where you don’t want the original function to get called.

Another big change worth mentioning is that our V8 runtime has been heavily refactored. The code is now easier to understand and it is way less work to add new features. Not just that, but our argument parsing is also handled by a single code-path. This means that all of our APIs are much more resilient to bad or missing arguments, so you get a JavaScript exception instead of having some APIs do fewer checks and happily crash the target process in case you forgot an argument.

Anyway, those are the highlights. Here’s a full summary of the changes:


  • core: add Socket.listen() and Socket.connect()
  • core: add setImmediate() and clearImmediate()
  • core: improve set{Timeout,Interval}() to support passing arguments
  • core: fix performance-related bug in Interceptor’s dirty state logic


  • core: add Script.nextTick()


  • core: teach Socket.listen() and Socket.connect() about UNIX sockets
  • core: fix handling of this.errno / this.lastError replacement functions
  • core: add SystemFunction API to get errno / lastError on return
  • core: fix crash on close() during I/O with the Stream APIs
  • core: fix and consolidate argument handling in the V8 runtime


  • core: temporarily disable Mapper on macOS in order to confirm whether this was the root cause of reported stability issues
  • core: add .call() and .apply() to NativeFunction
  • objc: fix parsing of opaque struct types


  • core: fix crash in the V8 runtime caused by invalid use of v8::Eternal
  • frida-repl: add batch mode support through -e and -q


  • node: generate prebuilds for 6.0 (LTS) and 7.0 only


  • node: generate prebuilds for 4.0 and 5.0 in addition to 6.0 and 7.0


  • objc: fix infinite recursion when proxying some proxies
  • objc: add support for proxying non-NSObject instances
  • python: fix removal of signal callbacks that are member functions


  • core: implement hooking of single-instruction ARM functions
  • core: plug leak in the handling of unhookable functions on some architectures
  • core: fix setImmediate() callback processing behavior
  • core: plug leak in setTimeout()
  • core: fix race condition in the handling of setTimeout(0) and setImmediate() in the Duktape runtime
  • core: fix crash when processing tick callbacks in the Duktape runtime
  • core: fix lifetime issue in the Duktape runtime
  • core: fix the reported module sizes on Linux
  • core: fix crash when launching apps on newer versions of Android
  • core: fix handling of attempts to launch Android apps not installed
  • core: improve compatibility with different versions and flavors of Android by detecting Dalvik and ART field offsets dynamically
  • core: fix unload issue on newer versions of Android, which resulted in only the first attach() succeeding and subsequent attempts all timing out
  • core: move ObjC and Java into their own modules published to npm, and use frida-compile to keep baking them into Frida’s built-in JS runtime
  • java: improve ART compatibility by detecting ArtMethod field offsets dynamically
  • node: update dependencies
  • node: fix unhandled Promise rejection issues


  • core: fix use-after-free caused by race condition on script unload


  • core: make ApiResolver and DebugSymbol APIs preemptible to avoid deadlocks


  • core: use a Mach exception handler on macOS and iOS, allowing us to reliably catch exceptions in apps that already have a Mach exception handler of their own
  • core: fix leak in InvocationContext copy-on-write logic in the Duktape runtime, used when storing data on this across onEnter and onLeave


  • core: fix Interceptor argument replacement issue in the V8 runtime, resulting in the argument only being replaced the first time


Frida 8.0 Released

It is time to level up to the next major version.

First off is the long-standing issue where multiple Frida clients attached to the same process were forced to coordinate so none of them would call detach() while one of the others was still using the session.

This was probably not a big deal for most users of Frida. However, we also had the same issue if one running frida-server was shared by multiple clients. You might have frida-trace running in one terminal while using the REPL in another, both attached to the same process, and you wouldn’t then expect one of them calling detach() to result in the other one getting kicked out.

Some of you may have tried this and observed that it works as expected, but this was due to some crazy logic in frida-server that would keep track of how many clients were interested in the same process, so it could ignore a detach() call if other clients were still subscribed to the same session. It also had some logic to clean up a certain client’s resources, e.g. scripts, if it suddenly disconnected.

Starting with 8.0 we have moved the session awareness into the agent, and kept the client-facing API the same, but with one little detail changed. Each call to attach() will now get its own Session, and the injected agent is aware of it. This means you can call detach() at any time, and only the scripts created in your session will be destroyed. Also, if your session is the last one alive, Frida will unload its agent from the target process.

That was the big change of this release, but we didn’t stop there.

One important feature of Frida’s scripts is that you can exchange messages with them. A script may call send(message[, data]) to send a JSON-serializable message, and optionally a binary blob of data next to it. The latter is so you don’t have to spend CPU-cycles turning your binary data into text that you include in the message.

It is also possible to communicate in the other direction, where the script would call recv(callback) to get a callback when you post_message() to it from your application. This allowed you to post a JSON-serializable message to your script, but there was no support for sending a binary blob of data next to it.

To address this shortcoming we renamed post_message() to post(), and gave it an optional second argument allowing you to send a binary blob of data next to it.

We also improved the C API by migrating from plain C arrays to GBytes, which means we are able to minimize how many times we copy the data as it flows through our APIs.

So in closing, let’s summarize the changes:


  • core: add support for multiple parallel sessions
  • core: rename Script’s post_message() to post() and add support for passing out-of-band binary data to the script
  • core: replace C arrays with GBytes to improve performance
  • core: fix heap corruption caused by use-after-free in libgee
  • core: fix multiple crashes
  • core: fix exports enumeration crash on macOS Sierra
  • core: add basic support for running on Valgrind
  • core: bump the macOS requirement to 10.9 so we can rely on libc++
  • node: update to the new 8.x API
  • python: update to the new 8.x API
  • swift: update to the new 8.x API
  • swift: upgrade to Swift 3
  • qml: update to the new 8.x API
  • clr: update to the new 8.x API
  • clr: plug leaks


  • node: fix Script#post()


  • core: fix deadlock when calling recv().wait() from our JS thread


  • core: reduce Interceptor base overhead by up to 65%
  • core: minimize Interceptor GC churn in our V8 runtime, using the same recycling and copy-on-write tricks as our Duktape runtime
  • core: speed up gum_process_get_current_thread_id() on macOS and iOS


Frida 7.3 Released

It’s finally release o’clock, and this time around the focus has been on improving quality. As it’s been a while since the last time we upgraded our third-party dependencies, and I found myself tracking down a memory-leak in GLib that had already been fixed upstream, I figured it was time to upgrade our dependencies. So with this release I’m happy to announce that we’re now packing the latest V8, GLib, Vala compiler, etc. Great care was also taken to eliminate resource leaks, so you can attach to long-running processes without worrying about memory allocations or OS handles piling up.

So in closing, let’s summarize the changes:


  • core: upgrade to the latest V8, GLib, Vala, Android NDK, etc.
  • core: plug resource leaks
  • core: fix thread enumeration on Linux/x86-32
  • core: (arm64) improve function hooking by adding support for relocating LDRPC with an FP/SIMD destination register


  • core: build Android binaries with PIE like we used to


  • core: add Script.setGlobalAccessHandler() for handling attempts to access undeclared global variables, which is useful for building REPLs


  • objc: convert Number to NSNumber when an object is expected
  • objc: add support for auto-converting to an array of objects, useful when calling e.g. +[NSArray arrayWithObjects:count:]


  • core: improve the unstable accessor API
  • core: fix the Duktape globals accessor logic so it’s only applied to reads


  • core: improve hexdump() to support any NativePointer-conforming object
  • objc: fix handling of the L type


  • core: fix regression in devkit header auto-generation logic


Frida 7.2 Released

Some of you may be aware that Frida has two JavaScript runtimes, one based on V8, and another one based on Duktape. We also used to have a runtime based on JavaScriptCore, but it got retired when our Duktape runtime proved better in all of the situations where V8 wasn’t a good fit, e.g. on tiny embedded systems and systems where RWX pages are forbidden.

Anyway, what is pretty neat is that Duktape has an API for compiling to bytecode, allowing you to cache the compiled code and save precious startup time when it’s time to instrument a new process. Starting with this release we now have brand new API for compiling your JavaScript to bytecode, and of course instantiating a script from it. This API is not yet supported in our V8 runtime, but we should be able to implement it there after our next V8 upgrade, by using the WebAssembly infrastructure that started appearing in the latest releases.

So without further ado, let’s take this new API for a spin with the Duktape runtime by forcing Frida to favor Duktape through Session.disable_jit().

From Node.js:

const co = require('co');
const frida = require('frida');

co(function* () {
  const systemSession = yield frida.attach(0);
  yield systemSession.disableJit();
  const bytecode = yield systemSession.compileScript(`
    rpc.exports = {
      listThreads: function () {
        return Process.enumerateThreadsSync();

  const session = yield frida.attach('Twitter');
  yield session.disableJit();
  const script = yield session.createScriptFromBytes(bytecode);
  yield script.load();

  const api = yield script.getExports();
  console.log('api.listThreads() =>', yield api.listThreads());

  yield script.unload();
.catch(err => {

And from Python:

from __future__ import print_function
import frida

system_session = frida.attach(0)
bytecode = system_session.compile_script("""
rpc.exports = {
  listThreads: function () {
    return Process.enumerateThreadsSync();

session = frida.attach("Twitter")
script = session.create_script_from_bytes(bytecode)

api = script.exports
print("api.list_threads() =>", api.list_threads())

Note that the same caveats as specified in the Duktape documentation apply here, so make sure the code you’re trying to load is well-formed and generated by the same version of Duktape. It may get upgraded when you upgrade to a future version of Frida, but will at least be architecture-neutral; i.e. you can compile to bytecode on a 64-bit x86 desktop and load it just fine in a 32-bit iOS app on ARM.

So that’s bytecode compilation through the API, but you probably want to use the frida-compile CLI tool for this instead:

$ npm install frida-compile
$ ./node_modules/.bin/frida-compile agent.js -o agent.bin -b

While developing you can also use it in watch mode by adding -w, which makes it watch the inputs and perform fast incremental builds whenever one of them changes.

Whether you’re using bytecode (-b) or not, frida-compile is highly recommended as it also comes with a number of other benefits, letting you:

  • Split your script into multiple .js files by using require().
  • Leverage thousands of existing modules from npm, including some that are Frida-specific. For example: frida-trace, frida-uikit, frida-screenshot, etc.
  • Use ES6 syntax and have your code compiled to ES5 so it’s compatible with the Duktape runtime.

So in closing, let’s summarize the changes:


  • core: add support for compiling and loading to/from bytecode
  • core: include error name and stack trace in RPC error replies
  • node: add support for the new bytecode APIs
  • node: augment RPC errors with name and stack when available
  • node: port examples to ES6
  • python: add support for the new bytecode APIs
  • python: update to the revised RPC protocol


  • objc: add support for resolving methods on minimal Objective-C proxies


  • objc: fix handling of methods returning structs and floating point values


  • objc: expose the raw handle of Objective-C methods


  • core: fix deadlock that was easily reproducible on iOS 9
  • java: improve Java.perform() robustness and handling of non-app processes


  • objc: fix handling of methods returning a struct in registers on x86-64


  • core: port Gum to MIPS
  • core: avoid swallowing exception when a Proxy object misbehaves
  • objc: add support for accessing Objective-C instance variables


  • core: port .so injector to MIPS
  • core: enhance MIPS fuzzy backtracer with more branch-and-link instructions
  • core: fix UnixInputStream and UnixOutputStream pollable behavior on TTYs, fixing hang on script unload
  • core: remove “0x” prefix from hexdump() offsets


  • objc: fix parsing of type hints
  • objc: add support for including type hints
  • objc: make ObjC.Block’s types field public
  • objc: add support for properly declaring void *
  • core: (MIPS) fix stack offset when getting/setting stack arguments


  • core: fix bug preventing registers from being written in the V8 runtime


  • core: add support for attaching to iOS Simulator processes
  • core: fix Android class-resolving regression introduced in 7.2.4


  • core: always kill iOS apps through SpringBoard


  • objc: unregister Objective-C classes on unload and GC


  • core: fix application kill logic on iOS 9


  • core: make the Duktape runtime preemptible like the V8 runtime
  • core: fix a few locking bugs in the V8 runtime


  • core: implement the Kernel API in the Duktape runtime also
  • core: remove the dangerous Kernel.enumerateThreads() API


  • core: improve robustness when quickly reattaching to the same process
  • core: fix deadlock when pending calls exist at detach time
  • core: fix hooking regression on 32-bit ARM
  • core: fix dlsym() deadlock in frida-gadget on Linux
  • core: fix Windows build regression
  • core: fix iOS 7 regression


  • core: fix session teardown regression


  • core: fix long-standing stability issue on iOS 9, where the injected bootstrap code was not pseudo-signed and caused processes to eventually lose their CS_VALID status
  • core: speed up app launching on iOS by eliminating unnecessary disk I/O
  • core: fix temporary directory clean-up on iOS


  • core: fix preemption-related lifetime-issue in the Duktape runtime


  • core: rework the V8 runtime to support fully asynchronous unloading
  • core: rework the Duktape runtime to support fully asynchronous unloading
  • core: make the Duktape runtime fully reentrant
  • core: add and Script.unpin() for extending a script’s lifetime in critical moments, e.g. for callbacks expected from external APIs out of one’s control
  • core: fix a timer-related leak in both the V8 and the Duktape runtime
  • objc: keep script alive until callback scheduled by ObjC.schedule() has been executed
  • objc: add a dealloc event to the ObjC proxy API


  • core: fix hang on detach()


  • core: fix hang on script unload
  • core: fix hang on abrupt connection loss during detach()


  • core: fix two low-probability crashes during script unload


  • core: fix use-after-free in the Duktape runtime
  • core: fix use-after-free bugs in ModuleApiResolver
  • core: improve unload-behavior when an exception handler is set


  • core: fix app launching on iOS 9.3.3
  • frida-server: fix “hang” on detach when another client is attached to the same process


Frida 7.1 Released

If you’ve ever used Frida to spawn programs making use of stdio you might have been frustrated by how the spawned process’ stdio state was rather undefined and left you with very little control. Starting with this release we have started addressing this, and programs are now always spawned with stdin, stdout and stderr redirected, and you can even input your own data to stdin and get the data being written to stdout and stderr. Frida’s CLI tools get this for free as this is wired up in the ConsoleApplication base-class. If you’re not using ConsoleApplication or you’re using a different language-binding, simply connect to the output signal of the Device object and your handler will get three arguments each time this signal is emitted: pid, fd, and data, in that order. Hit the input() method on the same class to write to stdin. That’s all there is to it.

Now that we have normalized the stdio behavior across platforms, we will later be able to add API to disable stdio redirection.

Beside this and lots of bug-fixes we have also massively improved the support for spawning plain programs on Darwin, on both Mac and iOS, where spawn() is now lightning fast on both and no longer messes up the code-signing status on iOS.

For those of you doing advanced instrumentation of Mac and iOS apps there’s now also brand new API for dynamically creating your own Objective-C protocols at runtime. We already supported creating new classes and proxy objects, and with this new API you can do even more.

So in closing, here’s a summary of the changes:


  • core: add Device.input() API for writing to stdin of spawned processes
  • core: add Device.output signal for propagating output from spawned processes
  • core: implement the new spawn() stdio behavior in the Windows, Darwin, and Linux backends
  • core: downgrade to Capstone 3.x for now due to non-trivial regressions in 4.x
  • node: add support for the new stdio API
  • node: add missing return to error-path
  • python: add support for the new stdio API


  • core: fix intermittent crash in spawn()


  • core: rework the spawn() implementation on Darwin, now much faster and reliable
  • core: add support for enumerating and looking up dynamic symbols on Darwin
  • core: fix page size computation in the Darwin Mach-O parser


  • core: revert temporary hack


  • python: fix ConsoleApplication crash on EOF
  • frida-trace: flush queued events before exiting


  • frida-repl: improve REPL autocompletion
  • objc: add ObjC.registerProtocol() for dynamic protocol creation
  • objc: fix handling of class name conflicts
  • objc: allow proxies to be named


  • python: fix download fallback


  • python: improve the download fallback


  • python: fix the local fallback and look in home directory instead


  • core: fix handling of overlapping requests to attach to the same pid
  • core: (Darwin) fix spawn() without attach()
  • core: (Darwin) fix crash when shutdown requests overlap
  • frida-server: always recycle the same temporary directory


  • core: (Windows) upgrade from VS2013 to VS2015
  • node: add prebuild for Node.js 6.x
  • python: fix handling of unicode command-line arguments on Python 2.x
  • qml: use libc++ instead of libstdc++ on Mac


  • core: provide a proper error message when the remote Frida is incompatible
  • core: ignore attempts to detach from the system session
  • core: guard against create_script() and detach() overlapping
  • core: fix setTimeout() so the delay is optional and defaults to 0
  • core: (V8 runtime) fix crash when closed File object gets GCed
  • core: (Darwin) fix intermittent crash on teardown
  • core: (QNX) fix implementation of gum_module_find_export_by_name()
  • core: (QNX) implement temporary TLS storage
  • frida-repl: monitor the loaded script and auto-reload on change
  • node: take level into account when handling log messages so console.warn() and console.error() go to stderr instead of stdout
  • node: do not let sessions keep the runtime alive


  • core: fix the return value of Memory.readByteArray() for size = 0


  • core: (Linux/Android) fix export address calculation for libraries with a preferred base
  • core: fix Java API not available error on Android 6.0
  • java: improve ART support by taking OS version and arch into account
  • frida-repl: add –no-pause to not pause spawned process at startup


Frida 7.0 Released

It’s been a while since our last major release bump. This time we’re addressing the long-standing issue where 64-bit integers were represented as JavaScript Number values. This meant that values beyond 53 bits were problematic due to the fact that the underlying representation is a double.

The 64-bit types in the Memory, NativeFunction, and NativeCallback APIs are now properly represented by the newly introduced Int64 and UInt64 types, and their APIs are almost identical to NativePointer.

Now let’s cross our fingers that int64/uint64 make it into ES7.

So in closing, here’s a summary of the changes:


  • core: rework handling of 64-bit integers
  • core: improve strictness of constructors
  • core: improve QNX support
  • frida-repl: update the logo


  • core: fix Int64/UInt64 field capacity on 32-bit architectures


  • core: allow Int64 and UInt64 to be passed as-is to all relevant APIs
  • core: fix handling of $protocols on ObjC instances


  • core: fix race-condition where listener gets destroyed mid-call
  • core: fix handling of nested native exception scopes
  • core: improve QNX support
  • frida-repl: tweak the startup message


  • core: massively improve the function hooking success-rate on 32-bit ARM
  • core: improve the function hooking success-rate on 64-bit ARM
  • core: fix the sp value exposed by Interceptor on 32-bit ARM


  • core: spin the main CFRunLoop while waiting for Device#resume() when spawning iOS apps, allowing thread-sensitive early instrumentation to be applied from the main thread


  • core: fix hooking of half-word aligned functions on 32-bit ARM
  • core: fix thread enumeration on Linux
  • core: add simple hexdump() API to the Script runtimes
  • core: make the Duktape runtime’s CpuContext serializable to JSON


  • core: allow passing a NativePointer to hexdump()


  • core: fix handling of wrapper objects in retval.replace()
  • core: fix behavior of Memory.readUtf8String() when a size is specified
  • core: add support for the new task_for_pid(0) method on the iOS 9.1 JB
  • core: don’t use cbnz which is not available in ARM mode on some processors
  • core: implement enumerate_threads() and modify_thread() for QNX


  • core: fix early crash in FridaGadget.dylib on iOS when running with ios-deploy and other environments where we are loaded before CoreFoundation
  • core: run a CFRunLoop in the main thread of frida-helper on Darwin, allowing system session scripts to make use of even more Apple APIs
  • core: add stream APIs for working with GIO streams, for now only exposed through UnixInputStream and UnixOutputStream (UNIX), and Win32InputStream and Win32OutputStream (Windows)


  • core: fix deadlock on script unload when I/O operations are pending


  • core: spin the main CFRunLoop while FridaGadget.dylib is blocking waiting for Device#resume(), allowing thread-sensitive early instrumentation to be applied from the main thread
  • java: fix method type sanity-check


Frida 6.2 Released

It’s release o’clock, and this time we’re bringing you massive performance improvements on all platforms, a brand new API for looking up functions, and major stability improvements on iOS 9.

Let’s talk about the latter subject first. Some of you may have noticed weird bugs and deadlocks when using Frida on iOS 9. The root cause was simply that our inline hooking was causing the process to lose the CS_VALID bit of its code-signing status. This was not a problem on iOS 8 and older as jailbreaks were always able to patch the kernel to loosen up on its code-signing requirements. Starting with this release we have implemented some tricks to be able to do inline hooking without breaking the code-signing status. For the technically curious this means that we dynamically generate a .dylib as a temporary file, write out the new versions of the memory pages that we’d like to modify, e.g. the libc memory page containing open(), then pseudo-sign this binary, ask the kernel to F_ADDFILESIGS to it, and finally mmap() from this file on top of the original memory pages.

This brings us to the next topic: performance. The tricks that I just talked about do actually add quite a bit of extra overhead just to hook one single function. It is also a very different approach from what we can do on systems with support for read-write-execute memory-pages and relaxed code-signing requirements, so this obviously meant that major architectural changes were needed. I had also been thinking for a while about being able to apply a whole batch of hooks in one go, allowing us to be more efficient and have more control on exactly when hooks are activated.

Starting with this release, our Interceptor API now supports transactions. Simply call begin_transaction(), hook all the functions, and make them all active in one go by calling end_transaction(). This results in a massive performance boost, and you get all of this for free without any changes to your existing code. This is because we implicitly begin a transaction whenever we’re entering the JavaScript runtime, and end it when we’re leaving it (and just before we send() a message or return from an RPC method). So unless you’re attaching your hooks from timers or asynchronous APIs like Memory.scan(), they will all be batched into a single transaction and get a performance boost.

Here’s how we stack up to CydiaSubstrate in terms of performance:

Note that if you’re using our instrumentation engine from C or C++ you will have to call begin_transaction() and end_transaction() yourself to get this boost, but your code will still work even if you don’t, because every operation will implicitly contain a transaction, and the API allows nesting those calls.

That was function hooking performance, but we didn’t stop there. If you’ve ever used frida-trace to trace Objective-C APIs, or glob for functions across all loaded libraries, you may have noticed that it could take quite a while to resolve all the functions. If you combined this with early instrumentation it could even take so long that we exceeded the system’s launch timeout. All of this has now been optimized, and to give you an idea of the speed-up, a typical Objective-C case that used to take seconds is now completing in a few milliseconds.

Now to the final part of the news. Considering that dynamically discovering functions to hook is such a common use-case, and not just something that frida-trace does, we now have a brand new API for just that:

ApiResolver #1

ApiResolver #2

So in closing, here’s a summary of the changes:


  • core: improve Interceptor to avoid breaking dynamic code-signing on iOS 9
  • core: move to a transaction-based Interceptor API for improved performance
  • core: fix crash when scheduled callbacks are freed late (V8 and Duktape)
  • frida-trace: improve performance by removing setTimeout() logic, allowing many hooks to be applied in the same transaction
  • frida-trace: batch log events in 50 ms chunks to improve performance


  • core: add ApiResolver API
  • frida-trace: improve performance by using the new ApiResolver API


  • core: fix oops that prevented injection into Windows Store/Universal apps
  • core: fix crash on teardown on 32-bit ARM
  • core: add frida-inject, a tool to inject an agent into a running process with similar semantics to frida-gadget
  • core: (Linux) prevent libdl from unloading to work around TLS destructor bug
  • core: (Linux) fix race-condition on rapid uninject


  • core: fix source-map handling for eval code, which manifested itself as unhandled exceptions getting swallowed, e.g. when running frida-trace
  • core: fix Python 3.x build system regression
  • frida-trace: fix path escaping issue
  • frida-trace: improve error-handling for bad handlers


  • frida-trace: monitor handlers instead of polling them


  • core: add support for hooking arbitrary instructions by calling Interceptor.attach() with a function instead of a callbacks object
  • core: add support for detaching individual listeners added by Interceptor.attach(), even synchronously from their callbacks
  • core: add Memory.scanSync()
  • core: fix clobber by improving Interceptor to preserve r12 aka IP on ARM
  • core: expose r8 through r12 to the JavaScript runtimes
  • core: fix crash on architectures where unaligned word access is not supported
  • frida-repl: simplify logic by using the RPC feature
  • node: upgrade to prebuild 3.x


  • core: fix regression on non-jailbroken iOS systems
  • core: fix Interceptor regression in the Duktape runtime
  • core: fix module name of resolved imports
  • core: add API for specifying which host to connect to
  • core: improve QNX support and fix build regressions
  • core: fix the frida-inject build system on Mac
  • core: (Windows) fix crash when USB device location retrieval fails
  • frida-server: allow overriding the default listen address
  • frida-node: add addRemoteDevice() and removeRemoteDevice() to DeviceManager
  • frida-python: add -H switch for specifying the host to connect to
  • frida-python: add add_remote_device() and remove_remote_device() to DeviceManager
  • frida-python: fix compatibility issues with the Duktape runtime
  • frida-python: canonicalize the requested RPC method name


Frida 6.1 Released

Some time ago @s1341 ported Frida to QNX, and just a few weeks back he was running into memory footprint issues when using Frida on embedded ARM devices. This was right after he contributed pull-requests porting Frida to linux-arm. We started realizing that it might be time for a new JavaScript runtime, and agreed that Duktape seemed like a great fit for our needs.

This runtime has now landed, all tests are passing, and it even beats our V8 runtime on the measured overhead for a call to a hooked function with an empty onEnter/onLeave callback. To give you an idea:

…/interceptor_on_enter_performance: V8 min=2 max=31 avg=2 OK
…/interceptor_on_enter_performance: DUK min=1 max=2 avg=1 OK

(Numbers are in microseconds, measured on a 4 GHz i7 running OS X 10.11.2.)

Anyway, even if that comparison isn’t entirely fair, as we do some clever recycling and copy-on-write tricks that we don’t yet do in our V8 runtime, this new runtime is already quite impressive. It also allows us to run on really tiny devices, and the performance difference between a roaring JIT-powered monster like V8 and a pure interpreter might not really matter for most users of Frida.

So starting with this release we are also including this brand new runtime in all of our prebuilt binaries so you can try it out and tell us how it works for you. It only adds a few hundred kilobytes of footprint, which is nothing compared to the 6 MB that V8 adds per architecture slice. Please try it out by passing --disable-jit to the CLI tools, or calling session.disable_jit() before the first call to session.create_script().

Considering that this new runtime also solves some issues that would require a lot of work to fix in our JavaScriptCore runtime, like ignoring calls from background threads and avoid poisoning the app’s heap, we decided to get rid of that runtime and switch to this new Duktape-based runtime on OSes where V8 cannot currently run, like on iOS 9. We feature-detect this at runtime, so you still get to use V8 on iOS 8 like before – unless you explicitly --disable-jit as just mentioned.

So in closing, here’s a summary of the changes:


  • core: replace the JavaScriptCore runtime with its successor built on Duktape
  • core: add disable_jit() to allow users to try out the new Duktape engine
  • core: fix crash on Linux when injecting into processes where pthread_create has never been called/bound yet
  • core: add support for linux-armhf (e.g. Raspberry Pi)
  • python: add disable_jit() to Session
  • node: add disableJit() to Session
  • CLI tools: add –disable-jit switch
  • frida-repl: upgrade to latest prompt-toolkit
  • frida-trace: fix crash when attempting to trace partially resolved imports
  • frida-trace: stick to ES5 in the generated handlers for Duktape compatibility


  • core: fix synchronization logic and error-handling bugs in the Duktape runtime


  • core: fix Android regression resulting in crash on inject
  • core: fix Python 3.x build regression
  • clr: add DisableJit() to Session


  • core: give the iOS frida-helper all the entitlements that the Preferences app has, so system session scripts can read and write system configuration
  • core: changes to support AppContainer ACL on temporary directory/files within
  • node: fix pid check so it allows attaching to the system session


  • core: implement spawn() for console binaries on iOS
  • core: improve support for hooking low-level OS APIs
  • core: fix mapper issues preventing us from injecting into Mac processes where libraries frida-agent depends are not yet loaded
  • core: make InvocationContext available to replaced functions also


  • core: add support for generator functions in scripts generated by frida-load
  • frida-repl: fix race condition resulting in hang
  • frida-repl: fix spurious error message on exit


Frida 6.0 Released

Epic release this time, with brand new iOS 9 support and improvements all over the place. For some more background, check out my blog posts here and here.

There’s a lot of ground to cover here, but the summary is basically:


  • core: add support for OS X El Capitan
  • core: add support for iOS 9
  • core: fix launchd plist permissions in Cydia package
  • core: disable our dynamic linker on iOS for now
  • core: add new JavaScript runtime based on JavaScriptCore, as we cannot use V8 on iOS 9 with the current jailbreak
  • core: add brand new system session when attaching to pid=0
  • core: improve arm hooking, including support for early TBZ/TBNZ/IT/B.cond, and avoid relocating instructions that a later instruction loops back to
  • core: fix relocation of LDR.W instructions on arm64
  • core: abort when we’re stuck in an exception loop
  • core: fix AutoIgnorer-related deadlocks
  • core: drop our . prefix so temporary files are easier to discover
  • python: add support for running without ES6 support
  • python: tweak to allow offline installation
  • python: lock the prompt-toolkit version to 0.38 for now
  • frida-repl: fix display of raw buffers as returned by Memory.readByteArray()
  • frida-repl: fix crash in completion on error
  • node: add support for DeviceManager’s added and removed signals
  • node: add example showing how to watch available devices
  • node: use prebuild instead of node-pre-gyp
  • node: Babelify the source code read by frida.load()
  • node: remove frida.load() as it’s now in the frida-load module


  • python: stop providing 3.4 binaries and move to 3.5 instead
  • node: fix Linux linking issue where we fail to pick up our libffi
  • node: also produce prebuild for Node.js LTS


  • core: provide FridaGadget.dylib for instrumenting iOS apps without jailbreak
  • core: add support for the iOS Simulator
  • core: improve MemoryAccessMonitor to allow monitoring any combination of R, W or X operations on a page
  • python: fix UTF-8 fields being accidentally exposed as str on Python 2.x


  • core: fix spawn() on OS X


  • core: add partial support for using the gadget standalone
  • CLI tools: fix crash when the stdout encoding cannot represent all characters
  • frida-trace: always treat handler scripts as UTF-8


  • core: add logical shift right and left operations to NativePointer
  • core: improve Interceptor to support attaching to a replaced function
  • core: add support for hooking tiny functions on 32-bit ARM
  • core: emulate {Get/Set}LastErrror and TLS key access on Windows, allowing us to hook more low-level APIs


  • core: fix launchd / Jetsam issue on iOS 9
  • core: fix iOS 9 code signing issue
  • core: update security attributes on named pipe to allow us to inject into more Windows apps


  • core: add support for injecting into processes on linux-arm
  • core: fix crashes related to the DebugSymbol API on Mac and iOS
  • frida-trace: improve manpage parser


  • core: fix Linux compatibility issue caused by failing to link libstdc++ statically


  • core: add support for running frida-gadget standalone
  • core: add a temporary workaround for Windows compatibility regression
  • core: port the Fruity backend to Linux, allowing direct access to connected iOS devices
  • core: expose the InvocationContext context read-write in the JavaScriptCore runtime also
  • core: fix issue with InvocationContext’s CpuContext getting GCed prematurely


Re-release of 6.0.9 with a Windows build regression fix.


  • core: prevent stale HostSession objects in case of network errors
  • CLI tools: assume UTF-8 when the stdout encoding is unknown
  • node: fix double free caused by using the wrong Nan API


  • core: update security attributes on named pipe on Windows
  • core: add CreateProcessW flags to prevent IFEO loop on Windows
  • core: fix hooking of recursive functions on arm and arm64
  • python: fix Python 3 line endings regression
  • node: update prebuild dependency


Frida 5.0 Released

Wow, another major release! We decided to change the Device API to give you persistent IDs so you can easily tell different devices apart as they’re hotplugged.

But that’s just the beginning of it, we’re also bringing a ton of other improvements this time:


  • core: change to represent individual devices across reconnects
  • core: add new Droidy backend for interfacing with connected Android devices
  • core: adjust confusing iPhone 5+ device name on Darwin
  • core: normalize the fallback iOS device name for consistency with Android
  • core: upgrade V8 to
  • objc: include both class and instance methods in $methods and $ownMethods
  • python: add -D switch for specifying the device id to connect to
  • python: add frida-ls-devices CLI tool for listing devices
  • python: update to the new API
  • python: add get_local_device() and improve API consistency with frida-node
  • node: update to the new API
  • node: improve the top-level facade API
  • qml: update to the new API
  • clr: update to the new API
  • frida-ps: improve the output formatting


  • core: add support for source maps
  • node: add frida.load() for turning a CommonJS module into a script
  • node: upgrade Nan


  • core: add console.warn() and console.error()
  • core: add Module.enumerateImports() and implement on Darwin, Linux, and Windows
  • core: allow null module name when calling Module.findExportByName()
  • core: move Darwin.Module and Darwin.Mapper from frida-core to frida-gum, allowing easy Mach-O parsing and out-of-process dynamic linking
  • core: better handling of temporary files
  • frida-trace: add support for conveniently tracing imported functions
  • frida-trace: blacklist dyld_stub_binder from being traced
  • python: avoid logging getting overwritten by the status message changing


  • core: improve arm64 hooking, including support for hooking short functions


  • core: improve arm64 hooking, also taking care to avoid relocating instructions that other instructions depend on, including the next instruction after a BL/BLR/SVC instruction
  • core: port Arm64Writer and Arm64Relocator to Capstone


  • core: fix crash on teardown by using new API provided by our GLib patch
  • core: fix module name resolving on Linux
  • core: improve ELF handling to also consider ET_EXEC images as valid modules
  • core: improve arm64 hooking
  • core: port {Arm,Thumb}Writer and {Arm,Thumb}Relocator to Capstone
  • python: fix tests on OS X 10.11
  • node: fix tests on OS X 10.11


  • core: turn NativeFunction invocation crash into a JS exception when possible
  • core: add Process.setExceptionHandler() for handling native exceptions from JS
  • core: install a default exception handler that emits error messages
  • core: prevent apps from overriding our exception handler if we install ours early in the process life-time
  • core: gracefully handle it if we cannot replace native functions
  • core: allow RPC exports to return ArrayBuffer values
  • python: add support for rpc methods returning ArrayBuffer objects
  • node: add support for rpc methods returning ArrayBuffer objects


  • core: don’t install a default exception handler for now


Re-release of 5.0.7 due to build machine issues.


  • python: update to match new build server configuration


  • core: fix instrumentation of arm64 functions with early usage of IP registers


Frida 4.5 Released

Time for another packed release. This time we’re bringing a brand new spawn gating API that lets you catch processes spawned by the system, and tons of Android improvements and improvements all over the place.

So without further ado, the list of changes:


  • core: add Process.pageSize constant
  • core: let Memory.alloc() allocate raw pages when size >= page size
  • core: fix NativeFunction’s handling of small return types
  • core: fix PC alignment when rewriting BLX instructions
  • core: add spawn gating API
  • core: implement get_frontmost_application() on Android
  • core: implement enumerate_applications() on Android
  • core: add support for spawning Android apps
  • core: add support for injecting into arm64 processes on Android
  • core: add support for Android M
  • core: patch the kernel’s live SELinux policy
  • core: integrate with SuperSU to work around restrictions on Samsung kernels
  • core: work around broken sigsetjmp on Android, and many other Android fixes
  • core: fix crash when enumerating modules on Linux
  • core: optimize exports enumeration for remote processes on Darwin
  • dalvik: port to ART and deprecate Dalvik name, now known as Java
  • java: add Java.openClassFile() to allow loading classes at runtime
  • java: fixes for array conversions and field setters
  • python: add support for the new spawn gating API
  • python: allow script source and name to be unicode on Python 2.x also
  • python: fix error-propagation in Python 3.x
  • python: fix the Linux download URL computation
  • node: add support for the new spawn gating API
  • node: port to Nan 2.x


  • core: fix ensure_host_session() error propagation


Frida 4.4 Released

With 4.4 out the door, we can now offer you a brand new RPC API that makes it super-easy to communicate with your scripts and have them expose services to your application. We also got some amazing contributions from Adam Brady, who just ported frida-node to Nan, making it easy to build it for multiple versions of Node.js.

So to summarize this release:

  • core: add new RPC API
  • python: add support for calling RPC exports
  • node: add support for calling RPC exports
  • node: allow posted message value to be anything serializable to JSON
  • node: port to Nan


Frida 4.3 Released

It’s release o’clock, and this time we have a slew of improvements all over the place. In brief:


  • core: add support for getting details about the frontmost application, initially only for iOS
  • python: add Device.get_frontmost_application()
  • node: add Device.getFrontmostApplication()


  • core: add support for relocating PC-relative CBZ on arm64
  • frida-repl: fix crash and loading of script on Py3k


  • core: add support for launching an iOS app with a URL
  • dalvik: fix bug in field caching
  • frida-trace: color and indent events based on thread ID and depth
  • frida-ps: fix application listing on Py3k


  • core: re-enable the Darwin mapper after accidentally disabling it


  • core: gracefully handle attempts to replace functions
  • core: throw an exception when Interceptor’s attach() and replace() fail
  • core: fix clean-up of agent sessions
  • core: fix assertion logging and log to CFLog on Darwin
  • dalvik: add Dalvik.synchronized(), Dalvik.scheduleOnMainThread() and Dalvik.isMainThread()
  • dalvik: port Dalvik.androidVersion and Dalvik.choose() to Android 4.2.2
  • python: fix the PyPI download URL for windows-i386
  • frida-trace: handle attach() failures gracefully


  • frida-server: better resource tracking


  • core: fix for arm64 function hooking
  • dalvik: fix for Dalvik.enumerateLoadedClasses()


  • objc: add ObjC.Block for implementing and interacting with blocks


Frida 4.2 Released

The Frida co-conspirators have been cracking away on several fronts, so much lately that I figured it was worth jotting this down to get the word out.

In Dalvik land, @marc1006 contributed a really neat new feature – the ability to do object carving, essentially scanning the heap for objects of a certain type. Check this out:

const strings = [];
Dalvik.choose('java.lang.String', {
  onMatch: function (str) {
  onComplete: function () {
    console.log('Found ' + strings.length + ' strings!');

Meanwhile, @Tyilo has been rocking out adding the same feature for Objective-C:

const strings = [];
ObjC.choose(ObjC.classes.NSString, {
  onMatch: function (str) {
  onComplete: function () {
    console.log('Found ' + strings.length + ' strings!');

In other mobile news, @pancake added support for enumerating applications on Firefox OS. Sweet!

While all of this was going on, @s1341 has been hard at work stabilizing the QNX port, and it’s reportedly working really well now.

On my end I have been applying Frida to interesting challenges at NowSecure, and ran into quite a few bugs and limitations in the Objective-C integration. There’s now support for overriding methods that deal with struct types passed by value, e.g. -[UIView drawRect:], which means NativeFunction and NativeCallback also support these; so for declaring a struct simply start an array with the fields’ types specified sequentially. You can even nest them. So for the - drawRect: case where a struct is passed by value, and that struct is made out of two other structs, you’d declare it like this:

const f = new NativeFunction(ptr('0x1234'), 'void',
    [[['double', 'double'], ['double', 'double']]]);

Another thing worth mentioning is that a long-standing issue especially visible when instrumenting 32-bit iOS apps, but affecting all platforms, has finally been fixed.

So let’s run quickly through all the changes:


  • core: add support for enumerating applications on Firefox OS
  • core: add NativePointer.toMatchPattern() for use with Memory.scan()
  • core: fix QNX injector race condition
  • objc: massively improved handling of types
  • objc: fix implicit conversion from JS string to NSString
  • objc: fix crash during registration of second proxy or unnamed class
  • objc: new ObjC.Object properties: $className and $super
  • dalvik: add Dalvik.choose() for object carving


  • core: NativeFunction and NativeCallback now support functions passing struct types by value
  • core: fix accidental case-sensitivity in Process.getModuleByName()
  • dalvik: new object property: $className


  • core: add this.returnAddress to Interceptor’s onEnter and onLeave callbacks
  • objc: add ObjC.choose() for object carving


  • core: fix exports enumeration of stripped libraries on QNX
  • objc: new ObjC.Object property: $kind, a string that is either instance, class or meta-class
  • objc: fix the $class property so it also does the right thing for classes
  • objc: fix crash when looking up inexistent method
  • python: ensure graceful teardown of the reactor thread
  • frida-discover: fix regression
  • frida-repl: fix hang when target crashes during evaluation of expression


  • core: fix exception handling weirdness; very visible on ios-arm
  • core: QNX stability improvements
  • objc: add ObjC.api for direct access to the Objective-C runtime’s API
  • objc: new ObjC.Object properties: equals, $superClass and $methods
  • objc: fix iOS 7 compatibility
  • objc: fix toJSON() of ObjC.classes and ObjC.protocols
  • dalvik: fix handling of java.lang.CharSequence
  • frida-repl: add %time command for easy profiling


  • core: fix crash when handling exceptions without a message object
  • core: fix the life-time of CpuContext JS wrappers
  • core: expose the file mapping info to Process.enumerateRanges()
  • core: make it possible to coalesce neighboring ranges when enumerating
  • core: add convenience API for looking up modules and ranges
  • core: make the QNX mprotect read in a loop instead of just the once
  • dalvik: avoid crashing the process if a type conversion fails
  • dalvik: allow null as call parameter
  • objc: fix conversion of structs with simple field types
  • objc: speed up implicit string conversion by caching wrapper object


  • objc: fix crash when interacting with not-yet-realized classes


  • core: optimize Interceptor callback logic and make it twice as fast when onEnter and onLeave aren’t both specified
  • core: fix return-address seen by the invocation-context on arm64
  • core: add a fuzzy backtracer for arm64


  • core: fix access to arguments 4 through 7 on arm64
  • core: add Memory.readFloat(), Memory.writeFloat(), Memory.readDouble() and Memory.writeDouble()
  • dalvik: improved type checking
  • qnx: implement side-stack for calling onEnter()/onLeave() with the stack-hungry V8 engine


  • core: Darwin backend bug-fixes
  • core: optimize handling of the send() data payload
  • core: add APIs for interacting with the iOS kernel through task_for_pid(0), only available in the attach(pid=0) session
  • core: side-stack support for replaced functions on QNX
  • objc: add getOwnPropertyNames() to ObjC.classes
  • frida-repl: improved completion


  • python: fix Py3k regression


  • objc: add $ownMethods to ObjC.Object
  • dalvik: add support for primitive arrays and object arrays
  • python: improve compatibility between Python 2 and 3
  • frida-repl: better magic commands


  • core: fix Interceptor vector register clobbering issue on arm64
  • core: improve temporary directory handling on Android


  • dalvik: add support for accessing instance and static fields
  • dalvik: type conversion improvements
  • python: resolve python runtime lazily on Mac to allow our binaries to work with multiple Python distributions
  • python: pip support


  • python: fix Py3k regressions

That’s all for now. Please help spread the word by sharing this post across the inter-webs. We’re still quite small as an open source project, so word-of-mouth marketing means a lot to us.


Frida 4.1 Released

It’s release o’clock, and this time we’re taking the iOS support to the next level while also bringing some solid quality improvements. I’m also really excited to announce that I’ve recently joined NowSecure, and the awesomeness of this release is no conincidence.

Let’s start with a brand new iOS feature. It’s now possible to list installed apps, which frida-ps can do for you:

$ frida-ps -U -a
10582 Facebook    com.facebook.Facebook
11066 IRCCloud    com.irccloud.IRCCloud
  451 Mail
10339 Mailbox     com.orchestra.v2
 6866 Messages
10626 Messenger   com.facebook.Messenger
11043 Settings
10542 Skype
11218 Slack       com.tinyspeck.chatlyio
11052 Snapchat    com.toyopagroup.picaboo

Add the -i switch and it will also include all installed applications, and not just those of them that are currently running.

This is also available from your language binding of choice, e.g. from Python:

>>> import frida
>>> iphone = frida.get_usb_device()
>>> print("\n".join(map(repr, iphone.enumerate_applications())))
Application(identifier="", name="YouTube")
Application(identifier="com.toyopagroup.picaboo", name="Snapchat")
Application(identifier="", name="Skype", pid=10542)


That’s cool, but wouldn’t you like to do early instrumentation of those apps? Now you can do that too, by just asking us to spawn an app identifier:

$ frida-trace -U -f com.toyopagroup.picaboo -I "libcommonCrypto*"

Or at the API level:

>>> import frida
>>> iphone = frida.get_usb_device()
>>> pid = iphone.spawn(["com.toyopagroup.picaboo"])
>>> snapchat = iphone.attach(pid)
>>> apply instrumentation
>>> iphone.resume(pid)

Note that we piggy-back on Cydia Substrate for the early launch part in order to maximize interoperability; after all it’s not too good if multiple frameworks all inject code into launchd and risk stepping on each others’ toes. This dependency is however a soft one, so we’ll throw an exception if Substrate isn’t installed when trying to call spawn() with an app identifier.

So, early instrumentation of iOS apps is pretty cool. But, those applications are typically consuming tons of Objective-C APIs, and if we want to instrument them we often find ourselves having to create new Objective-C classes in order to create delegates to insert between the application and the API. Wouldn’t it be nice if such Objective-C classes could be created in pure JavaScript? Now they can:

const MyConnectionDelegateProxy = ObjC.registerClass({
  name: 'MyConnectionDelegateProxy',
  super: ObjC.classes.NSObject,
  protocols: [ObjC.protocols.NSURLConnectionDataDelegate],
  methods: {
    '- init': function () {
      const self = this.super.init();
      if (self !== null) {
        ObjC.bind(self, {
          foo: 1234
      return self;
    '- dealloc': function () {
    '- connection:didReceiveResponse:': function (conn, resp) {
      /* === 1234 */
     * But those previous methods are declared assuming that
     * either the super-class or a protocol we conform to has
     * the same method so we can grab its type information.
     * However, if that's not the case, you would write it
     * like this:
    '- connection:didReceiveResponse:': {
      retType: 'void',
      argTypes: ['object', 'object'],
      implementation: function (conn, resp) {
    /* Or grab it from an existing class: */
    '- connection:didReceiveResponse:': {
      types: ObjC.classes
          .Foo['- connection:didReceiveResponse:'].types,
      implementation: function (conn, resp) {
    /* Or from an existing protocol: */
    '- connection:didReceiveResponse:': {
      types: ObjC.protocols.NSURLConnectionDataDelegate
          .methods['- connection:didReceiveResponse:'].types,
      implementation: function (conn, resp) {
    /* Or write the signature by hand if you really want to: */
    '- connection:didReceiveResponse:': {
      types: '[email protected]:[email protected]@24',
      implementation: function (conn, resp) {

const proxy = MyConnectionDelegateProxy.alloc().init();
/* use `proxy`, and later: */

Though most of the time you’d like to build a proxy object where you pass on everything and only do some logging for the few methods you actually care about. Check this out:

const MyConnectionDelegateProxy = ObjC.registerProxy({
  protocols: [ObjC.protocols.NSURLConnectionDataDelegate],
  methods: {
    '- connection:didReceiveResponse:': function (conn, resp) {
      /* fancy logging code here */
      /* === 1234 */
          .connection_didReceiveResponse_(conn, resp);
    '- connection:didReceiveData:': function (conn, data) {
      /* other logging code here */
          .connection_didReceiveData_(conn, data);
  events: {
    forward: function (name) {
      console.log('*** forwarding: ' + name);

const method = ObjC.classes.NSURLConnection[
    '- initWithRequest:delegate:startImmediately:'];
Interceptor.attach(method.implementation, {
  onEnter: function (args) {
    args[3] = new MyConnectionDelegateProxy(args[3], {
      foo: 1234

So that’s Objective-C. The Dalvik integration also got some sweet new API for enumerating loaded classes thanks to @marc1006, who also fixed our handling of static methods and being able to return booleans from overriden implementations.

We also got lots of awesome improvements from @Tyilo who helped improve the ObjC integration, beat the REPL into better shape, added APIs for enumerating malloc ranges, and added some convenience APIs to NativePointer.

While all of this was going on, @s1341 has been hard at work doing an amazing job porting Frida to QNX, which is now really close to working like a charm.

Let’s run through the remaining changes:


  • objc: support for more types
  • frida-trace: fix ObjC tracing regression


  • frida-node: fix encoding of the pixels property


  • frida-repl: fix Windows regression


  • objc: support for more types and better type checking
  • objc: arm64 now working properly
  • frida-repl: allow variables to be created


  • platform: support passing a plain array of data to send()
  • arm: support for relocating cbz/cbnz instructions


  • platform: fix spawning of child processes that write to stdout
  • platform: fix NativeCallback’s handling of bool/int8/uint8 return values (this was preventing Dalvik method overrides from being able to return false).
  • platform: allow Memory.readByteArray() with length < 1
  • arm: support for relocating the ldrpc t2 instruction
  • arm: improved redirect resolver
  • arm64: fix relocation of the adrp instruction
  • arm64: support for relocating PC-relative ldr instruction
  • dalvik: add Dalvik.enumerateLoadedClasses()
  • dalvik: fix handling of static methods
  • python: fix console.log() on Windows
  • frida-repl: bugfixes and improvements
  • frida-trace: glob support for tracing ObjC methods


  • platform: add missing pid field in enumerate_applications()


  • objc: class and proxy creation APIs
  • objc: new ObjC.protocols API for enumerating protocols


  • platform: improved concurrency by releasing V8 lock while calling NativeFunction
  • platform: add Process.getModuleByName(name)
  • platform: faster and more robust detach
  • python: stability improvements in CLI tools
  • frida-repl: replace readline with prompt-toolkit


  • platform: faster and more robust teardown
  • frida-server: clean up on SIGINT and SIGTERM


  • frida-ps: add support for listing applications


  • platform: fix crash on spawn on Mac, iOS and Linux
  • platform: add and NativePointer.equals()
  • platform: add Process.enumerateMallocRanges{,Sync}()
  • frida-trace: switch from Enter to Ctrl+C for stopping
  • frida-trace: fix spawning of iOS apps
  • frida-repl: add prototype names to autocomplete


  • python: CLI tools stability improvements

That’s all for now. Please help spread the word by sharing this post across the inter-webs. We’re still quite small as an open source project, so word-of-mouth marketing means a lot to us.


Frida 4.0.0 Released

It’s time for an insane release with tons of improvements.

Let’s start with a user-facing change. The CLI tool called frida-repl has been renamed to just frida, and now does tab completion! This and some other awesome REPL goodies were contributed by @fitblip.

There is also integrated support for launching scripts straight from the shell:

$ frida Calculator -l calc.js
    |   |    Frida 4.0.0 - A world-class dynamic
    |   |                  instrumentation framework
    |   |    Commands:
    |   |        help      -> Displays the help system
    |   |        object?   -> Display information about 'object'
    |   |        exit/quit -> Exit
    |   |
    |   |    More info at

# The code in calc.js has now been loaded and executed
# Reload it from file at any time
[Local::ProcName::Calculator]-> %reload

Or, perhaps you’re tired of console.log() and would like to set some breakpoints in your scripts to help you understand what’s going on? Now you can, because Frida just got an integrated Node.js-compatible debugger.

(Cue “Yo Dawg” meme here.)

Yep yep, but it is actually quite useful, and all of the CLI tools provide the --debug switch to enable it:

# Connect Frida to a locally-running
# and load calc.js with the debugger enabled
$ frida Calculator -l calc.js --debug
    |   |    Frida 4.0.0 - A world-class dynamic
    |   |                  instrumentation framework
    |   |    Commands:
    |   |        help      -> Displays the help system
    |   |        object?   -> Display information about 'object'
    |   |        exit/quit -> Exit
    |   |
    |   |    More info at

Debugger listening on port 5858
# We can now run node-inspector and start debugging calc.js

Here’s what it looks like:

Frida Debugger Session

Ever found yourself wanting to frida-trace Objective-C APIs straight from the shell? Thanks to @Tyilo you now can:

# Trace ObjC method calls in Safari
$ frida-trace -m '-[NSView drawRect:]' Safari

There are also other goodies, like brand new support for generating backtraces and using debug symbols to symbolicate addresses:

var f = Module.findExportByName("libcommonCrypto.dylib",
Interceptor.attach(f, {
    onEnter: function (args) {
        console.log("CCCryptorCreate called from:\n" +
            Thread.backtrace(this.context, Backtracer.ACCURATE)
            .map(DebugSymbol.fromAddress).join("\n") + "\n");

Or perhaps you’re on Windows and trying to figure out who’s accessing certain memory regions? Yeah? Well check out the brand new MemoryAccessMonitor. Technically this code isn’t new, but it just hasn’t been exposed to the JavaScript API until now.

Another nice feature is that starting with this release it is no longer necessary to forward multiple TCP ports when using frida-server running on another device, e.g. Android.

There is now also much better error feedback propagated all the way from a remote process to different exceptions in for example Python. With the previous release attaching to an inexistent pid on Mac would give you:

SystemError: GDBus.Error:org.gtk.GDBus.UnmappedGError.Quark._g_2
dio_2derror_2dquark.Code0: task_for_pid() for remote pid failed w
hile trying to make pipe endpoints: (os/kern) failure (5)

Whoah, madness. This is now simply:

frida.ProcessNotFoundError: unable to find process with pid 1234

That’s better. Let’s talk about performance. Perhaps you used frida-trace and wondered why it spent so much time “Resolving functions…”? On a typical iOS app resolving just one function would typically take about 8 seconds. This is now down to ~1 second. While there were some optimizations possible, I quickly realized that no matter how fast we make the enumeration of function exports, we would still need to transfer the data, and the transfer time alone could be unreasonable. Solution? Just move the logic to the target process and transfer the logic instead of the data. Simple. Also, the Dalvik and ObjC interfaces have been optimized so seconds have been reduced to milliseconds. The short story here is further laziness in when we interrogate the language runtimes. We took this quite far in the ObjC interface, where we now use ES6 proxies to provide a more idiomatic and efficient API.

That brings us to the next topic. The ObjC interface has changed a bit. Essentially:

var NSString = ObjC.use("NSString");

is now:

var NSString = ObjC.classes.NSString;

You still use ObjC.classes for enumerating the currently loaded classes, but this is now behaving like an object mapping class name to a JavaScript ObjC binding.

Also, there’s no more casting, so instead of:

var NSSound = ObjC.use('NSSound');
var sound = ObjC.cast(ptr("0x1234"), NSSound);

You just go:

var sound = new ObjC.Object(ptr("0x1234"));

Yep, no more class hierarchies trying to mimic the ObjC one. Just a fully dynamic wrapper where method wrappers are built on the first access, and the list of methods isn’t fetched unless you try to enumerate the object’s properties.

Anyway, this is getting long, so let’s summarize the other key changes:

  • The Dalvik interface now handles varargs methods. Thanks to @dmchell for reporting and helping track this down.
  • NativePointer also provides .and(), .or() and .xor() thanks to @Tyilo.
  • The Interceptor’s onEnter/onLeave callbacks used to expose the CPU registers through this.registers, which has been renamed to this.context, and now allows you to write to the registers as well.
  • Process.enumerateThreads()’s thread objects got their CPU context field renamed from registers to context for consistency.
  • Synchronous versions of enumerateFoo() API available as enumerateFooSync() methods that simply return an array with all of the items.
  • Memory.readCString() is now available for reading ASCII C strings.
  • Frida.version can be interrogated to check which version you’re running, and this is also provided on the frida-core end, which for example is exposed by frida-python through frida.__version__.
  • Stalker now supports the jecxz and jrcxz instructions. This is good news for CryptoShark, which should soon provide some updated binaries to bundle the latest version of Frida.
  • V8 has been updated to 4.3.62, and a lot of ES6 features have been enabled.
  • We’re now using a development version of the upcoming Capstone 4.0.
  • All third-party dependencies have been updated to the latest and greatest.
  • Windows XP is now supported. This is not a joke. I realized that we didn’t actually use any post-XP APIs, and as I had to rebuild the dependencies on Windows I figured we might as well just lower our OS requirements to help those of you still instrumenting software on XP.


Frida 3.0.0 Released

You may have wondered:

Why a Python API, but JavaScript debugging logic?

Well, you can now do this:

$ npm install frida

We just brought you brand new Node.js bindings, and they are fully asynchronous:

Check out the examples to get an idea what the API looks like. It’s pretty much a 1:1 mapping of the API provided by the Python bindings, but following Node.js / JavaScript conventions like camelCased method-names, methods returning ES6 Promise objects instead of blocking, etc.

Now, combine this with NW.js and you can build your own desktop apps with HTML, CSS, and JavaScript all the way through.

So, brand new Node.js bindings; awesome! We did not stop there, however. But first, a few words about the future. I am excited to announce that I have just started a company with the goal of sponsoring part-time development of Frida. By offering reverse-engineering and software development expertise, the goal is to generate enough revenue to pay my bills and leave some time to work on Frida. Longer term I’m hoping there will also be demand for help adding features or integrating Frida into third-party products. In the meantime, however, if you know someone looking for reverse-engineering or software development expertise, I would really appreciate it if you could kindly refer them to get in touch. Please see my CV for details.

That aside, let’s get back to the release. Next up: 32-bit Linux support! Even Stalker has been ported. Not just that, the Linux backend can even do cross-architecture injection like we do on the other platforms. This means a 64-bit Frida process, e.g. your Python interpreter, can inject into a 32-bit process. The other direction works too.

Another awesome update is that Tyilo contributed improvements to frida-trace so it now uses man-pages for auto-generating the log handlers. Awesome, huh? But there’s even more goodies:

  • frida-server ports are now recycled, so if you’re using Frida on Android you won’t have to keep forwarding ports unless you’re actually attaching to multiple processes at the same time.
  • Linux and Android spawn() support has been improved to also support PIE binaries.
  • Android stability and compatibility improvements.
  • Mac and Linux build system have been revamped, and make it easy to build just the parts that you care about; and maybe even some components you didn’t even know were there that were previously not built by default.
  • Python bindings have a minor simplification so instead of frida.attach(pid).session.create_script() it’s simply just frida.attach(pid).create_script(). This is just like in the brand new Node.js bindings, and the reason we had to bump the major version.

That’s the gist of it. Please help spread the word by sharing this post across the inter-webs. We’re still quite small as an open source project, so word-of-mouth marketing means a lot to us.


Frida 2.0.2 Released

Thanks to your excellent feedback we just eliminated a crasher when using Frida on Windows with certain iOS device configurations. As this is a very important use-case we decided to do a hotfix release without any other changes.

Please keep the bug-reports coming!

Frida 2.0.1 Released

Just a quick bug-fix release to remedy an iOS issue that slipped through the final testing of 2.0.0. Enjoy!

Frida 2.0.0 Released

It’s time for a new and exciting release! Key changes include:

  • No more kernel panics on Mac and iOS! Read the full story here.
  • Mac and iOS injector performs manual mapping of Frida’s dylib. This means we’re able to attach to heavily sandboxed processes.
  • The CLI tools like frida-trace, frida-repl, etc., have brand new support for spawning processes:
$ frida-trace -i 'open*' -i 'read*' /bin/cat /etc/resolv.conf
    27 ms	open$NOCANCEL()
    28 ms	read$NOCANCEL()
    28 ms	read$NOCANCEL()
    28 ms	read$NOCANCEL()
Target process terminated.
  • Usability improvements in frida-repl and frida-discover.
  • First call to DeviceManager.enumerate_devices() does a better job and also gives you the currently connected iOS devices, so for simple applications or scripts you no longer have to subscribe to updates if you require the device to already be present.
  • The python API now provides you with frida.get_usb_device(timeout = 0) and frida.get_remote_device() for easy access to iOS and remote/Android devices.
  • The onEnter and onLeave callbacks passed to Interceptor.attach() may access this.registers to inspect CPU registers, which is really useful when dealing with custom calling conventions.
  • console.log() logs to the console on your application’s side instead of the target process. This change is actually why we had to bump the major version for this release.
  • Android 5.0 compatibility, modulo ART support.
  • Brand new support for Android/x86. Everything works except the Dalvik integration; please get in touch if you’d like to help out with a pull-request to fix that!

Want to help out? Have a look at our GSoC 2015 Ideas Page to get an overview of where we’d like to go next.


Update 2am: An iOS issue slipped through the final testing, so we just pushed 2.0.1 to remedy this.

Update 11pm: Thanks to your excellent feedback we found a critical bug when using Frida on Windows with certain iOS device configurations. Please upgrade to 2.0.2 and let us know if you run into any issues.

Frida 1.8.0 Released

This release introduced a serious regression on iOS and was quickly pulled from our Cydia repo, though it was available for Mac, Linux and Android while waiting to be replaced by 2.0.0.

Frida 1.6.7 Released

Tired of waiting for Frida to attach to 32-bit processes on 64-bit Mac or iOS systems? Or perhaps frida-trace takes a while to resolve functions? If any of the above, or none of it, then this release is for you!

Attaching to 32-bit processes on Mac/iOS hosts has been optimized, and instead of seconds this is now a matter of milliseconds. That’s however specific to Darwin OSes; this release also speeds up enumeration of module exports on all OSes. This is now 75% faster, and should be very noticable when using frida-trace and waiting for it to resolve functions.

Also, as an added bonus, teardown while attached to multiple processes no longer crashes on Darwin and Linux.


Frida 1.6.5 Released

It’s release o’clock, and time for some bug fixes:

  • iOS 8.1 is now supported, and the ARM64 support is better than ever.
  • The iOS USB transport no longer disconnects when sending a burst of data to the device. This would typically happen when using frida-trace and tracing a bunch of functions, resulting in a burst of data being sent over the wire. This was actually a generic networking issue affecting Mac and iOS, but was very reproducible when using Frida with a tethered iOS device.
  • Eliminated crashes on shutdown of the Python interpreter.
  • The onEnter and onLeave callbacks in frida-trace scripts are now called with this bound to the correct object, which means that it’s bound to an object specific to that thread and invocation, and not an object shared by all threads and invocations.

Frida 1.6.4 Released

It’s time for a bug-fix release!

Stalker improvements:

  • The engine no longer pre-allocates a fixed chunk of 256 MB per thread being traced, and now grows this dynamically in a reentrancy-safe manner.
  • Eliminated a bug in the cache lookup logic where certain blocks would always result in a cache miss. Those blocks thus got recompiled every time they were about to get executed, slowing down execution and clogging up the cache with more and more entries, and eventually running out of memory.
  • Relocation of RIP-relative cmpxchg instruction is now handled correctly.

Better Dalvik integration (Android):

  • App’s own classes can now be loaded.
  • Several marshalling bugs have been fixed.

Script runtime:

  • More than one NativeFunction with the same target address no longer results in use-after-free.

Also, CryptoShark 0.1.2 is out, with an upgraded Frida engine and lots of performance improvements so the GUI is able to keep up with the Stalker. Go get it while it’s hot!

Frida 1.6.3 Released

This latest release includes a bunch of enhancements and bug fixes. Some of the highlights:

  • The remainder of Frida’s internals have been migrated from udis86 to Capstone, which means that our Stalker is now able to trace binaries with very recent x86 instructions. Part of this work also included battle-testing it on 32- and 64-bit binaries on Windows and Mac, and all known issues have now been resolved.

  • Memory.protect() has been added to the JavaScript API, allowing you to easily change page protections. For example:

Memory.protect(ptr("0x1234"), 4096, 'rw-');
  • Process.enumerateThreads() omits Frida’s own threads so you don’t have to worry about them.

  • Python 3 binaries are now built against Python 3.4.

So with this release out, let’s talk about CryptoShark:

Grab a pre-built Windows binary here, or build it from source if you’d like to try it out on Mac or Linux.


Frida 1.6.2 Released

It’s release o’clock, and this time we’re bringing you more than just bugfixes. Meet Instruction.parse():

var a = Instruction.parse(ptr("0x1234"));
var b = Instruction.parse(;


push rbp
mov rbp, rsp

How is this implemented you ask? That’s the cool part. Frida already uses the amazing Capstone disassembly framework behind the scenes, and thus it makes perfect sense to make it available to the JavaScript runtime. Have a look at the JavaScript API Reference for all the details.


Frida 1.6.1 Released

It’s time for a bugfix release. Highlights:

  • Compatibility with the Pangu iOS jailbreak on ARM64. The issue is that RWX pages are not available like they used to be with the evad3rs jailbreak.
  • Fix occasional target process crash when detaching.
  • Fix crash when trying to attach to a process the second time after failing to establish the first time. This primarily affected Android users, but could happen on any OS when using frida-server.
  • Faster and more reliable injection on Linux/x86-64 and Android/ARM.
  • Fix issues preventing hooking of HeapFree and friends on Windows.
  • Upgraded GLib, libgee, json-glib and Vala dependencies for improved performance and bugfixes.
  • No more resource leaks. Please report if you find any.

Also new since 1.6.0, as covered in my blog post, there is now a full- featured binding for Qml. This should be of interest to those of you building graphical cross-platform tools.

Frida 1.6.0 Released

As some of you may have noticed, Frida recently got brand new Android support, allowing you to easily instrument code just like on Windows, Mac, Linux and iOS. This may sound cool and all, but Android does run a lot of Java code, which means you’d only be able to observe the native side-effects of whatever that code was doing. You could of course use Frida’s FFI API to poke your way into the VM, but hey, shouldn’t Frida just do that dirty plumbing for you? Of course it should!

Here’s what it looks like in action:

Dalvik.perform(function () {
    var Activity = Dalvik.use("");
    Activity.onResume.implementation = function () {
        send("onResume() got called! Let's call the original implementation");

The Dalvik.perform() call takes care of attaching your thread to the VM, and isn’t necessary in callbacks from Java. Also, the first time you call Dalvik.use() with a given class name, Frida will interrogate the VM and build a JavaScript wrapper on-the-fly. Above we ask for the Activity class and replace its implementation of onResume with our own version, and proceed to calling the original implementation after sending a message to the debugger (running on your Windows, Mac or Linux machine). You could of course choose to not call the original implementation at all, and emulate its behavior. Or, perhaps you’d like to simulate an error scenario:

Dalvik.perform(function () {
    var Activity = Dalvik.use("");
    var Exception = Dalvik.use("java.lang.Exception");
    Activity.onResume.implementation = function () {
        throw Exception.$new("Oh noes!");

So there you just instantiated a Java Exception and threw it straight from your JavaScript implementation of Activity.onResume.

This release also comes with some other runtime goodies:

  • Memory.copy(dst, src, n): just like memcpy
  • Memory.dup(mem, size): short-hand for Memory.alloc() followed by Memory.copy()
  • Memory.writeXXX(): the missing counterparts: S8, S16, U16, S32, U32, S64, U64, ByteArray, Utf16String and AnsiString
  • Process.pointerSize to make your scripts more portable
  • NativePointer instances now have a convenient isNull() method
  • NULL constant so you don’t have to do ptr("0") all over the place
  • WeakRef.bind(value, fn) and WeakRef.unbind(id) for the hardcore: The former monitors value so fn gets called as soon as value has been garbage-collected, or the script is about to get unloaded. It returns an id that you can pass to unbind() for explicit cleanup. This API is useful if you’re building a language-binding, where you need to free native resources when a JS value is no longer needed.


Frida 1.4.2 Released

Just a quick bugfix release to squash this annoying bug, which is reproducible on all supported x86 OSes. Thanks for Guillaume for tracking this one down.

As a bonus, frida-repl now also works on Windows. Happy REPLing!

Frida 1.4.1 Released

Interested in spawning processes on Windows or Linux, and not just on Mac? Or maybe you’ve been bitten by the Linux injector crashing your processes instead of letting you inject into them? Or maybe you had a function name that was so long that frida-trace overflowed the max filename length on Windows? Well, if any of the above, or none of it, then Frida 1.4.1 is for you!

Thanks to Guillaume and Pedro for making this release awesome. Keep those pull- requests and bug reports coming!

Frida 1.4.0 Released

Did anyone say Android? Frida 1.4.0 is out, with brand new Android support! Have a look at the documentation here to get started. Also new in this release is that Frida is now powered by the amazing Capstone Disassembly Engine, which means our cross-platform code instrumentation is even more powerful. It also paves the way for taking the x86-only stealth tracing to new architectures in future releases.


Frida 1.2.1 Released

Had some fun tracing Apple’s crypto APIs, which lead to the discovery of a few bugs. So here’s 1.2.1 bringing some critical ARM-related bugfixes:

  • ARM32: Fix crashes caused by register clobber issue in V8 on ARM32 due to an ABI difference regarding r9 in Apple’s ABI compared to AAPCS.
  • ARM32: Fix ARM32/Thumb relocator branch rewriting for immediate same-mode branches.
  • ARM64: Improve ARM64 relocator to support rewriting b and bl.

Frida 1.2.0 Released

It’s release-o-clock, and Frida 1.2.0 is finally out! Bugfixes aside, this release introduces brand new ARM64 support, which is quite useful for those of you using Frida on your iPhone 5S or iPad Air. You can now inject into both 64- and 32-bit processes, just like on Mac and Windows.

This release also improves stability on ARM32, where attaching to short functions used to result in undefined behavior.


Frida 1.0.11 Released

Some of you experienced issues injecting into processes on Windows, as well as crashes on iOS. Here’s a new release bringing some serious improvements to Frida’s internals:

  • V8 has been upgraded to 3.25 in order to fix the iOS stability issues. This also means new features (like ECMAScript 6) and improved performance on all platforms. Another nice aspect is that Frida now depends on a V8 version that runs on 64-bit ARM, which paves the way for porting Frida itself to AArch64.
  • The Windows injector has learned some new tricks and will get you into even more processes. A configuration error was also discovered in the Windows build system, which explains why some of you were unable to inject into some processes.
  • For those of you building Frida on Windows, the build system there now depends on VS2013. This means XP is no longer supported, though it is still possible to build with the v120_xp toolchain if any of you still depend on that, so let me know if this is a deal-breaker for you.
  • The recently added support for this.lastError (Windows) is now working correctly.

That’s all for now. Let us know what you think, and if you like Frida, please help spread the word! :)

Frida 1.0.10 Released

This release brings a few improvements:

  • Interceptor is now compatible with a lot more functions on iOS/ARM.
  • A new CLI tool called frida-repl provides you with a basic REPL to experiment with the JavaScript API from inside a target process.
  • onLeave callback passed to Interceptor.attach() is now able to replace the return value by calling retval.replace().
  • Both onEnter and onLeave callbacks passed to Interceptor.attach() can access this.errno (UNIX) or this.lastError (Windows) to inspect or manipulate the current thread’s last system error.

Here’s how you can combine the latter three to simulate network conditions for a specific process running on your Mac:

~ $ frida-repl TargetApp

Then paste in:

callbacks = { \
    onEnter: function onEnter(args) { \
        args[0] = ptr(-1); // Avoid side-effects on socket \
    }, \
    onLeave: function onLeave(retval) { \
        var ECONNREFUSED = 61; \
        this.errno = ECONNREFUSED; \
        retval.replace(-1); \
    } \
}; \
Module.enumerateExports("libsystem_kernel.dylib", { \
    onMatch: function (exp) { \
        if ("connect") === 0 &&"connectx") !== 0) { \
            Interceptor.attach(exp.address, callbacks); \
        } \
    }, \
    onComplete: function () {} \


Frida 1.0.9 Released

Another release — this time with some new features:

  • Objective-C integration for Mac and iOS. Here’s an example to whet your appetite:
var UIAlertView = ObjC.use('UIAlertView'); /* iOS */
ObjC.schedule(ObjC.mainQueue, function () {
    var view = UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_(
        "Hello from Frida",
  • Module.enumerateExports() now also enumerates exported variables and not just functions. The onMatch callback receives an exp object where the type field is either function or variable.

To get the full scoop on the ObjC integration, have a look at the JavaScript API reference.

Frida 1.0.8 Released

We just rolled out a bugfix release:

  • Support injection into Mac App Store apps
  • Eliminate iOS daemon auto-start issues
  • No more iOS crashes shortly after injecting

Frida 1.0.7 Released

This release brings USB device support in the command-line tools, and adds frida-ps for enumerating processes both locally and remotely.

For example to enumerate processes on your tethered iOS device:

$ frida-ps -U

The -U switch is also accepted by frida-trace and frida-discover.

Docs how to set this up on your iOS device will soon be added to the website.

However, that’s not the most exciting part. Starting with this release, Frida got its first contribution since the HN launch. Pete Morici dived in and contributed support for specifying module-relative functions in frida-trace:

$ frida-trace -a 'kernel32.dll+0x1234'


Frida 1.0.6 Released

This release simplifies the licensing and fixes bugs reported by the community since the HN launch.


  • Relicense remaining GPLv3+ Frida components to LGPLv2.1+ (same as frida-gum).
  • Tracer works on 64-bit with function addresses in the upper range
  • Linux build links with Frida’s own libraries instead of the build machine’s corresponding libraries.

Frida 1.0.5 Released

This release improves frida-trace with support for auto-generating, loading and reloading function handlers from scripts on the filesystem. Have a look at our Quick-start guide for a walkthrough. Also new in this release is Py3k support, which is available from PyPI on all platforms.