Modes

Overview

Frida instruments programs via Gum (C core) + GumJS (JavaScript runtime wrapping Gum). Instrumentation scripts run inside the target process with full access to Gum APIs: function hooking, library/export enumeration, memory read/write, memory pattern scanning.

Three modes address different deployment constraints:

ModeUse CaseKey Component
InjectedSpawn/attach to running processes; most commonfrida-core + frida-server
EmbeddedJailed iOS/Android; restricted environmentsfrida-gadget (shared library)
PreloadedAutonomous script execution; no external toolingfrida-gadget (autonomous config)

Mode 1: Injected

When to use: Default mode. Target process is accessible (not jailed). You want to spawn, attach, or hijack a process at runtime.

How it works:

Additional capabilities via frida-core:

Remote device support (iOS / Android):

Tooling: All standard Frida CLI tools (frida, frida-trace, frida-ps, etc.) use this mode by default.


Mode 2: Embedded (Gadget)

When to use: Injected mode is unavailable — e.g., jailed iOS, non-debuggable Android, or environments without a running frida-server.

How it works:

Ways to embed Gadget:

Naming:

Platform-specific notes:

PlatformNotes
iOS (Xcode)Xcode may place .dylib in Frameworks/ subdirectory; Gadget also searches the parent directory for .config in this case
Android (non-debuggable)Package manager only copies files from /lib if name starts with lib, ends with .so, or is gdbserver. Config file must follow the same pattern (e.g., libgadget.config.so)

Config file format: UTF-8 JSON with root object. Top-level keys:

KeyTypeDefaultDescription
interactionobjectlistenWhich interaction type to use
teardownstring"minimal""minimal" or "full" — cleanup on unload. Use "full" if Gadget is unloaded mid-process.
runtimestring"default"Override JS runtime: "default", "qjs", or "v8"
code_signingstring"optional""optional" or "required". Set "required" for jailed iOS without debugger. Note: "required" disables the Interceptor API unless a debugger was attached before Gadget loaded.

Gadget Interaction Types

Listen (default)

Gadget exposes a frida-server-compatible TCP interface. Process list contains only the single Gadget process (re.frida.Gadget / name Gadget).

Constructor blocks until attach() or resume() is called (enabling early instrumentation). Override with "on_load": "resume" to skip blocking.

Default config:

1{
2  "interaction": {
3    "type": "listen",
4    "address": "127.0.0.1",
5    "port": 27042,
6    "on_port_conflict": "fail",
7    "on_load": "wait"
8  }
9}

Config keys:

KeyTypeDefaultDescription
addressstring"127.0.0.1"Interface to listen on. "0.0.0.0" = all IPv4; "::" = all IPv6
portnumber27042TCP port
certificatestringPEM public+private key (inline or path) to enable TLS
tokenstringSecret token for authentication
on_port_conflictstring"fail""fail" or "pick-next" (try consecutive ports)
on_loadstring"wait""wait" (block until connected) or "resume" (start immediately)
originstringRequired “Origin” header value for cross-origin protection
asset_rootstringDirectory to serve static files over HTTP/HTTPS

Connect

Gadget connects outward to a running frida-portal and joins its cluster. Useful for managed/fleet scenarios. Constructor blocks only if spawn-gating is enabled (Device.enable_spawn_gating()).

Default config:

1{
2  "interaction": {
3    "type": "connect",
4    "address": "127.0.0.1",
5    "port": 27052
6  }
7}

Config keys:

KeyTypeDefaultDescription
addressstring"127.0.0.1"Portal’s cluster interface host
portnumber27052Portal’s cluster interface TCP port
certificatestringPEM CA public key for TLS verification (required if Portal has TLS)
tokenstringAuth token for Portal’s cluster interface
aclarray of stringsAccess control list: which controller tags can interact with this process

Note: For custom authentication, per-node ACLs, or application-specific protocol messages, instantiate PortalService programmatically instead of using the frida-portal CLI.

Script

Fully autonomous mode. Loads and executes a single JavaScript file from the filesystem before the program’s entrypoint runs. No external tooling required.

Minimal config:

1{
2  "interaction": {
3    "type": "script",
4    "path": "/home/user/explore.js"
5  }
6}

Script skeleton with optional lifecycle hooks:

 1rpc.exports = {
 2  init(stage, parameters) {
 3    // stage: "early" (first load) or "late" (reload)
 4    // parameters: object from config, or {}
 5    console.log('[init]', stage, JSON.stringify(parameters));
 6
 7    Interceptor.attach(Module.getGlobalExportByName('open'), {
 8      onEnter(args) {
 9        const path = args[0].readUtf8String();
10        console.log('open("' + path + '")');
11      }
12    });
13  },
14  dispose() {
15    // Called on script unload (process exit, Gadget unload, or reload)
16    console.log('[dispose]');
17  }
18};

Lifecycle details:

path resolution rules:

Config keys:

KeyTypeDefaultDescription
pathstringrequiredFilesystem path to the script
parametersobject{}Arbitrary data passed to init() as second argument
on_changestring"ignore""ignore" or "reload" — watch file and reload on change (recommended during development)

ScriptDirectory

Loads multiple scripts from a directory. Useful for system-wide instrumentation or plugin-style tweaks. Each script can optionally have a .config sidecar for filtering and parameters.

Minimal config:

1{
2  "interaction": {
3    "type": "script-directory",
4    "path": "/usr/local/frida/scripts"
5  }
6}

Config keys:

KeyTypeDefaultDescription
pathstringrequiredDirectory containing .js scripts
on_changestring"ignore""ignore" or "rescan" — rescan directory on change (recommended during development)

Per-script sidecar config (<scriptname>.config):

KeyTypeDescription
filterobjectLoad criteria (any one match triggers load). Keys: executables (array), bundles (array), objc_classes (array)
parametersobjectPassed to init() as second argument
on_changestring"ignore" or "reload"

Example: twitter.js + twitter.config for macOS Twitter app:

1{
2  "filter": {
3    "executables": ["Twitter"],
4    "bundles": ["com.twitter.twitter-mac"],
5    "objc_classes": ["Twitter"]
6  }
7}

Filter is OR-logic: script loads if any criterion matches.


Mode 3: Preloaded

Concept: Analogous to LD_PRELOAD (Linux) / DYLD_INSERT_LIBRARIES (macOS) — but for JavaScript. Uses frida-gadget in autonomous (Script or ScriptDirectory) mode loaded via the dynamic linker.

Implementation: Same as Embedded mode, but specifically configured to use the script or script-directory interaction with no external controller. Gadget is injected via LD_PRELOAD/DYLD_INSERT_LIBRARIES and runs scripts from the filesystem automatically.

Key difference from Embedded Listen/Connect: No frida-server or frida-portal is needed at runtime.


Decision Guide

Is the target process accessible (not jailed, can attach/spawn)?
├── Yes → Use Injected mode (frida-server + standard tools)
└── No → Need to embed frida-gadget
    ├── Want remote interactive control?
    │   ├── Direct connection → Listen interaction (Gadget as server)
    │   └── Fleet/portal scenario → Connect interaction (Gadget connects to portal)
    └── Want fully autonomous (no external tooling)?
        ├── Single script → Script interaction
        └── Multiple scripts / plugin system → ScriptDirectory interaction