JavaScript API
Frida JavaScript API
Source: frida/frida-website (_i18n/en/_docs/javascript-api.md) Runtime: QuickJS (default) or V8 (
Script.runtime) TypeScript bindings:npm install @types/frida-gum+ clone frida-agent-example
Runtime Information
Frida
| Property | Type | Description |
|---|---|---|
Frida.version | string | Current Frida version |
Frida.heapSize | number | Current size of Frida’s private heap (bytes), shared across all scripts |
Script
| API | Returns | Description |
|---|---|---|
Script.runtime | string | "QJS" or "V8" |
Script.evaluate(name, source) | any | Evaluate JS string in global scope; name is a UNIX-style virtual path used in stack traces. Supports source maps via Script.registerSourceMap(). |
Script.load(name, source) | Promise<namespace> | Compile and evaluate as ES module. Parent script can export values importable by the child. Requires frida-compile’s ES module bundle format. |
Script.registerSourceMap(name, json) | void | Register source map JSON for a script path. Call before loading the script for accurate stack traces. |
Script.nextTick(func[, ...params]) | void | Run func on next tick (when current native thread exits JS runtime) |
Script.pin() | void | Prevent script unload (reference-counted; must pair with unpin()) |
Script.unpin() | void | Reverse a previous pin() |
Script.bindWeak(value, fn) | id | Call fn when value is GC’d or script unloads. Returns ID for unbindWeak(). |
Script.unbindWeak(id) | void | Stop monitoring value and call fn immediately |
Script.setGlobalAccessHandler(handler|null) | void | Install handler for undefined global access. Handler object: { enumerate(): string[], get(property): any } |
Process, Thread, Module, Memory
Process
| API | Returns | Description |
|---|---|---|
Process.id | number | PID |
Process.arch | string | "ia32" | "x64" | "arm" | "arm64" |
Process.platform | string | "windows" | "darwin" | "linux" | "freebsd" | "qnx" | "barebone" |
Process.pageSize | number | Virtual memory page size in bytes |
Process.pointerSize | number | Pointer size in bytes |
Process.codeSigningPolicy | string | "optional" or "required". "required" means Interceptor API is off-limits and unsigned code cannot run (set via Gadget config). |
Process.mainModule | Module | Main executable module |
Process.getCurrentDir() | string | Current working directory path |
Process.getHomeDir() | string | Current user home directory path |
Process.getTmpDir() | string | Temp directory path |
Process.isDebuggerAttached() | boolean | Whether a debugger is attached |
Process.getCurrentThreadId() | number | Current thread OS ID |
Process.enumerateThreads() | Thread[] | All running threads |
Process.attachThreadObserver(callbacks) | observer | Watch thread add/remove/rename events. callbacks: { onAdded(thread), onRemoved(thread), onRenamed(thread, previousName) }. Call .detach() on returned observer. |
Process.runOnThread(id, callback) | Promise<any> | Run JS function on specific thread. Use with caution — may interrupt non-reentrant code. |
Process.findModuleByAddress(address) | Module|null | Find module by address (null if not found) |
Process.getModuleByAddress(address) | Module | Get module by address (throws if not found) |
Process.findModuleByName(name) | Module|null | Find module by name |
Process.getModuleByName(name) | Module | Get module by name (throws if not found) |
Process.enumerateModules() | Module[] | All loaded modules |
Process.attachModuleObserver(callbacks) | observer | Watch module load/unload. callbacks: { onAdded(module), onRemoved(module) }. onAdded fires before app can use the module — ideal time to apply instrumentation. |
Process.findRangeByAddress(address) | range|null | Memory range containing address |
Process.getRangeByAddress(address) | range | Memory range (throws if not found) |
Process.enumerateRanges(protection|specifier) | range[] | Memory ranges matching protection string (e.g. "rw-"). Specifier: { protection, coalesce: bool }. Range fields: base (NativePointer), size, protection, file? ({ path, offset, size }). |
Process.enumerateMallocRanges() | range[] | Individual heap allocations |
Process.setExceptionHandler(callback) | void | Process-wide native exception handler. callback(details): { type, address, memory?, context, nativeContext }. Return true to resume, else forward to OS. |
Thread
Objects returned by Process.enumerateThreads():
| Property/Method | Type | Description |
|---|---|---|
id | number | OS thread ID |
name | string|undefined | Thread name if available |
state | string | "running" | "stopped" | "waiting" | "uninterruptible" | "halted" |
context | object | CPU registers snapshot: pc, sp, arch-specific (eax, rax, r0, x0, etc.) |
entrypoint | object|undefined | { routine: NativePointer, parameter?: NativePointer } |
setHardwareBreakpoint(id, address) | void | Set hardware breakpoint. Use with Process.setExceptionHandler(). |
unsetHardwareBreakpoint(id) | void | Remove hardware breakpoint |
setHardwareWatchpoint(id, address, size, conditions) | void | Watch memory region. conditions: "r" | "w" | "rw" |
unsetHardwareWatchpoint(id) | void | Remove hardware watchpoint |
Thread.backtrace([context, backtracer]) | NativePointer[] | Generate backtrace. Pass this.context from Interceptor callbacks for accuracy. backtracer: Backtracer.ACCURATE (default, needs debug info) or Backtracer.FUZZY (stack forensics, false positives). Limit: 16 frames. |
Thread.sleep(delay) | void | Sleep for delay seconds (e.g. 0.05 = 50ms) |
1// Backtrace example in Interceptor
2Interceptor.attach(Module.getGlobalExportByName('open'), {
3 onEnter(args) {
4 console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
5 .map(DebugSymbol.fromAddress).join('\n'));
6 }
7});
Module
Objects returned by Module.load() and Process.enumerateModules():
| Property/Method | Returns | Description |
|---|---|---|
name | string | Canonical module name |
base | NativePointer | Base address |
size | number | Size in bytes |
path | string | Full filesystem path |
ensureInitialized() | void | Run module initializers. Required during early instrumentation before interacting with APIs (e.g. ObjC classes). |
enumerateImports() | object[] | { type, name, module, address, slot }. Only name is guaranteed. |
enumerateExports() | object[] | { type, name, address } |
enumerateSymbols() | object[] | { isGlobal, type, section?, name, address, size? }. Only on i/macOS and Linux. |
enumerateRanges(protection) | range[] | Module-scoped memory ranges |
enumerateSections() | object[] | { id, name, address, size } |
enumerateDependencies() | object[] | { name, type }. Types: regular, weak, reexport, upward |
findExportByName(name) | NativePointer|null | Export address (null if not found) |
getExportByName(name) | NativePointer | Export address (throws if not found) |
findSymbolByName(name) | NativePointer|null | Symbol address |
getSymbolByName(name) | NativePointer | Symbol address (throws) |
Module.load(path) | Module | Load module from filesystem path |
Module.findGlobalExportByName(name) | NativePointer|null | Search all modules for export (expensive) |
Module.getGlobalExportByName(name) | NativePointer | Same but throws |
Symbol type values:
- Mach-O:
unknown,section,undefined,absolute,prebound-undefined,indirect - ELF:
unknown,section,object,function,file,common,tls
ModuleMap
1const map = new ModuleMap([filter]); // filter: (Module) => boolean
| Method | Returns | Description |
|---|---|---|
has(address) | boolean | Whether address belongs to any module in map |
find(address) | Module|null | Module containing address |
get(address) | Module | Module containing address (throws) |
findName(address) / getName(address) | string|null | Just the name field |
findPath(address) / getPath(address) | string|null | Just the path field |
update() | void | Refresh after module load/unload |
values() | Module[] | Deep copy of current modules (unaffected by future update()) |
Memory
| API | Returns | Description |
|---|---|---|
Memory.scan(address, size, pattern, callbacks) | void | Scan memory range for pattern. Pattern format: "13 37 ?? ff" (hex bytes, ?? = wildcard). r2-style mask: "13 37 : 1f ff". Nibble wildcards: "?3 37". Callbacks: { onMatch(address, size), onError(reason), onComplete() }. Return "stop" from onMatch to cancel. |
Memory.scanSync(address, size, pattern) | {address, size}[] | Synchronous scan, returns array of matches |
Memory.alloc(size[, options]) | NativePointer | Allocate heap memory. If size is multiple of Process.pageSize, allocates raw OS pages. Options: { near: address, maxDistance: bytes }. Memory released when all JS handles gone — keep reference alive! |
Memory.copy(dst, src, n) | void | memcpy equivalent |
Memory.dup(address, size) | NativePointer | alloc + copy |
Memory.protect(address, size, protection) | boolean | Update memory protection (e.g. "rw-") |
Memory.queryProtection(address) | string | Get protection string at address |
Memory.patchCode(address, size, apply) | void | Safely modify code. apply(writable_ptr) — write to the writable pointer, not address directly (iOS may use a temp buffer). |
Memory.allocUtf8String(str) | NativePointer | Allocate and write UTF-8 string (NUL-terminated) |
Memory.allocUtf16String(str) | NativePointer | Allocate and write UTF-16 string |
Memory.allocAnsiString(str) | NativePointer | Allocate and write ANSI string |
1// patchCode example: make get_lives_left() return 9000
2const getLivesLeft = Process.getModuleByName('game-engine.so').getExportByName('get_lives_left');
3Memory.patchCode(getLivesLeft, 64, code => {
4 const cw = new X86Writer(code, { pc: getLivesLeft });
5 cw.putMovRegU32('eax', 9000);
6 cw.putRet();
7 cw.flush();
8});
MemoryAccessMonitor
| API | Description |
|---|---|
MemoryAccessMonitor.enable(ranges, callbacks) | Monitor memory ranges for first access per page. ranges: { base, size } or array. callbacks.onAccess(details): { threadId, operation, from, address, rangeIndex, pageIndex, pagesCompleted, pagesTotal, context } |
MemoryAccessMonitor.disable() | Stop monitoring |
CModule
Compile C source code to machine code in memory. Useful for hot Interceptor/Stalker callbacks.
1new CModule(code[, symbols, options])
code: C source string or ArrayBuffer (precompiled shared library)symbols: object mapping names to NativePointer values (memory blocks, NativeCallbacks)options.toolchain:"internal"(TinyCC, always available, unoptimized) |"external"(system toolchain) |"any"(default)- Global C functions exported as NativePointer properties
- Writable globals must be
externand passed viasymbols - Lifecycle hooks:
void init(void),void finalize(void) dispose(): eagerly unmap
1const cm = new CModule(`
2#include <stdio.h>
3void hello(void) { printf("Hello from CModule\\n"); }
4`);
5const hello = new NativeFunction(cm.hello, 'void', []);
6hello();
CModule.builtins: object with defines and headers available during compilation (arch/version-specific).
RustModule
1new RustModule(code[, symbols, options])
code: Rust source stringsymbols: extern C symbols exposed to Rustoptions.dependencies: array of Cargo dependency strings (e.g.['base64 = "0.22.1"'])- Public
#[no_mangle] extern "C"functions exported as NativePointer properties
ApiResolver
1const resolver = new ApiResolver(type);
2// type: "module" | "swift" | "objc"
"module": always available; resolves exports, imports, sections"swift": requires Swift runtime; checkSwift.available"objc": requires ObjC runtime; checkObjC.available
Lazy-loads data on creation. Reuse instance for batch queries; recreate for fresh data.
1resolver.enumerateMatches(query) // returns [{name, address, size?}]
Query syntax:
- Module:
"exports:*!open*","imports:libc.so!*","sections:*!*text*" - ObjC:
"-[NSURL* *HTTP*]" - Swift:
"functions:*CoreDevice!*RemoteDevice*"
Suffix /i for case-insensitive matching.
DebugSymbol
| API | Returns | Description |
|---|---|---|
DebugSymbol.fromAddress(address) | symbol | { address, name, moduleName, fileName, lineNumber }. Supports toString(). |
DebugSymbol.fromName(name) | symbol | Same |
DebugSymbol.getFunctionByName(name) | NativePointer | First matching function address (throws if not found) |
DebugSymbol.findFunctionsNamed(name) | NativePointer[] | All matching function addresses |
DebugSymbol.findFunctionsMatching(glob) | NativePointer[] | Functions matching glob pattern |
DebugSymbol.load(path) | void | Load debug symbols for a module |
Kernel
Requires Kernel.available === true before use.
| API | Returns | Description |
|---|---|---|
Kernel.available | boolean | Whether Kernel API is available |
Kernel.base | UInt64 | Kernel base address |
Kernel.pageSize | number | Kernel page size |
Kernel.enumerateModules() | {name, base, size}[] | Kernel modules |
Kernel.enumerateRanges(protection|specifier) | range[] | Kernel memory ranges |
Kernel.enumerateModuleRanges(name, protection) | range[] | Ranges for a kernel module (use null for kernel itself) |
Kernel.alloc(size) | UInt64 | Allocate kernel memory (rounded to page size) |
Kernel.protect(address, size, protection) | void | Update kernel memory protection |
Kernel.readByteArray(address, length) | ArrayBuffer | Read from kernel memory |
Kernel.writeByteArray(address, bytes) | void | Write to kernel memory |
Kernel.scan(address, size, pattern, callbacks) | void | Scan kernel memory |
Kernel.scanSync(address, size, pattern) | match[] | Synchronous kernel memory scan |
Data Types, Function and Callback
Int64 / UInt64
1const v = new Int64(v) // or int64(v)
2const v = new UInt64(v) // or uint64(v)
3// v: number or decimal/hex string ("0x1234")
| Method | Description |
|---|---|
add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs) | Arithmetic/bitwise, returns new Int64/UInt64 |
shr(n), shl(n) | Bit shift, returns new value |
compare(rhs) | Like String#localeCompare |
toNumber() | Cast to JS number |
toString([radix=10]) | Convert to string |
NativePointer
1const p = new NativePointer(s) // or ptr(s)
2// s: decimal or "0x..." hex string
3NULL // shorthand for ptr("0")
Arithmetic:
| Method | Description |
|---|---|
add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs) | Returns new NativePointer |
shr(n), shl(n) | Bit shift |
not() | Bitwise NOT |
isNull() | Returns boolean |
equals(rhs) | Equality check |
compare(rhs) | Like String#localeCompare |
toInt32() | Cast to signed 32-bit int |
toString([radix=16]) | Default hex string |
toMatchPattern() | Returns Memory.scan()-compatible pattern string |
Pointer Authentication (AArch64 PAC):
| Method | Description |
|---|---|
sign([key, data]) | Add PAC bits. Keys: "ia" (default), "ib", "da", "db". No-op if PAC not supported. |
strip([key]) | Remove PAC bits. Defaults to "ia". |
blend(smallInteger) | Blend constant into pointer (use as data for sign()) |
Read/Write:
| Method | Description |
|---|---|
readPointer() / writePointer(ptr) | Read/write pointer-sized value |
readS8() … readDouble() | Read signed/unsigned integers and floats |
writeS8(v) … writeDouble(v) | Write values |
readS64(), readU64(), readLong(), readULong() | Returns Int64/UInt64 |
writeS64(v), writeU64(v), writeLong(v), writeULong(v) | Write 64-bit values |
readByteArray(length) | Returns ArrayBuffer |
writeByteArray(bytes) | bytes: ArrayBuffer or [0x13, 0x37, ...] |
readVolatile(length) / writeVolatile(bytes) | Safe read/write via syscall — use when memory may be inaccessible or threads are running |
readCString([size=-1]) | Read ASCII/NUL-terminated |
readUtf8String([size=-1]) | Read UTF-8 |
readUtf16String([length=-1]) | Read UTF-16 |
readAnsiString([size=-1]) | Read ANSI (Windows only) |
writeUtf8String(str) / writeUtf16String(str) / writeAnsiString(str) | Write encoded string with NUL terminator |
All read/write methods throw JS exception on inaccessible memory (except readVolatile/writeVolatile).
ArrayBuffer
| API | Description |
|---|---|
ArrayBuffer.wrap(address, size) | Create ArrayBuffer backed by existing memory. No validation — bad pointer crashes process. |
buffer.unwrap() | Returns NativePointer to backing store. Caller must keep buffer alive. |
NativeFunction
1new NativeFunction(address, returnType, argTypes[, abi])
2new NativeFunction(address, returnType, argTypes[, options])
Supported types: void, pointer, int, uint, long, ulong, char, uchar, size_t, ssize_t, float, double, int8–int64, uint8–uint64, bool
Structs by value: use array of field types: ['int', 'int', 'int']. Nestable. C++ classes with vtables: first field is pointer to vtable.
Supported ABIs:
| Platform | Values |
|---|---|
| Default | "default" |
| Win32 | "sysv", "stdcall", "thiscall", "fastcall", "mscdecl" |
| Win64 | "win64" |
| UNIX x86 | "sysv", "unix64" |
| UNIX ARM | "sysv", "vfp" |
Options object:
| Key | Values | Description |
|---|---|---|
abi | see above | ABI selection |
scheduling | "cooperative" (default), "exclusive" | Whether to release JS lock during call |
exceptions | "steal" (default), "propagate" | How to handle native exceptions |
traps | "default", "none", "all" | Interceptor/Stalker activation during call. "all" enables Stalker for coverage/step-into. |
Variadic functions: add '...' between fixed and variadic args in argTypes.
NativeCallback
1new NativeCallback(func, returnType, argTypes[, abi])
Creates a native-callable JS function. Returns a NativePointer. When used with Interceptor.replace(), func receives this bound to the same invocation object as Interceptor.attach().
SystemFunction
1new SystemFunction(address, returnType, argTypes[, abi|options])
Like NativeFunction but return value is { value, errno } (UNIX) or { value, lastError } (Windows).
Network
Socket
| API | Returns | Description |
|---|---|---|
Socket.listen([options]) | Promise<SocketListener> | Open TCP or UNIX listening socket. Options: family ("unix","ipv4","ipv6"), host, port, type (UNIX: "path","abstract","abstract-padded","anonymous"), path, backlog (default 10) |
Socket.connect(options) | Promise<SocketConnection> | Connect to TCP or UNIX server. Options: family, host (default "localhost"), port, type, path |
Socket.type(handle) | string|null | OS socket type: "tcp","udp","tcp6","udp6","unix:stream","unix:dgram" |
Socket.localAddress(handle) | {ip?,port?,path?}|null | Local address |
Socket.peerAddress(handle) | {ip?,port?,path?}|null | Peer address |
SocketListener
All methods async (return Promise).
| Property/Method | Description |
|---|---|
path | UNIX socket path |
port | IP port |
close() | Release resources |
accept() | Wait for next client → Promise<SocketConnection> |
SocketConnection
Inherits from IOStream. All methods async.
| Method | Description |
|---|---|
setNoDelay(noDelay) | Disable Nagle algorithm when true |
File and Stream
File
| API | Description |
|---|---|
File.readAllBytes(path) | Synchronously read all bytes → ArrayBuffer |
File.readAllText(path) | Synchronously read all text (UTF-8) → string |
File.writeAllBytes(path, data) | Synchronously write ArrayBuffer |
File.writeAllText(path, text) | Synchronously write UTF-8 string |
new File(filePath, mode) | Open file. mode like C fopen() (e.g. "wb") |
tell() | Current file pointer position |
seek(offset[, whence]) | Move pointer. whence: File.SEEK_SET, File.SEEK_CUR, File.SEEK_END |
readBytes([size]) | Read bytes → ArrayBuffer (reads to EOF if no size) |
readText([size]) | Read UTF-8 text → string |
readLine() | Read next line (no newline in result) |
write(data) | Write string or ArrayBuffer |
flush() | Flush buffered data |
close() | Close file |
IOStream / InputStream / OutputStream
All methods async (return Promise).
IOStream:
input: InputStreamoutput: OutputStreamclose(): close both streams
InputStream:
close(),read(size)→ ArrayBuffer (empty = EOF),readAll(size)→ ArrayBuffer (exact size, rejects on premature EOF withpartialData)
OutputStream:
close(),write(data)→ number of bytes written,writeAll(data)(rejects withpartialSize),writeMemoryRegion(address, size)→ bytes written
Platform-specific Streams
| Class | Platform | Constructor |
|---|---|---|
UnixInputStream(fd[, options]) | UNIX | options.autoClose: close fd on stream release |
UnixOutputStream(fd[, options]) | UNIX | Same |
Win32InputStream(handle[, options]) | Windows | handle is Windows HANDLE |
Win32OutputStream(handle[, options]) | Windows | Same |
Database
SqliteDatabase
| API | Description |
|---|---|
SqliteDatabase.open(path[, options]) | Open SQLite v3 database. options.flags: array of "readonly","readwrite","create" |
SqliteDatabase.openInline(encodedContents) | Open in-memory DB from Base64-encoded (optionally gzipped) content. Read-write, never touches filesystem. |
close() | Close database |
exec(sql) | Execute raw SQL (result ignored; use for DDL) |
prepare(sql) | Compile SQL → SqliteStatement |
dump() | Dump to gzip+Base64 string (for use with openInline) |
SqliteStatement
| Method | Description |
|---|---|
bindInteger(index, value) | Bind integer |
bindFloat(index, value) | Bind float |
bindText(index, value) | Bind text |
bindBlob(index, bytes) | Bind blob (ArrayBuffer, byte array, or string) |
bindNull(index) | Bind null |
step() | Advance and return row as array, or null at end |
reset() | Reset for re-use |
1const db = SqliteDatabase.open('/path/to/people.db');
2const smt = db.prepare('SELECT name FROM people WHERE age = ?');
3smt.bindInteger(1, 42);
4let row;
5while ((row = smt.step()) !== null) console.log(row[0]);
6smt.reset();
Instrumentation
Interceptor
| API | Description |
|---|---|
Interceptor.attach(target, callbacks[, data]) | Hook function at target (NativePointer). On 32-bit ARM: LSB=0 for ARM, LSB=1 for Thumb — Frida handles this automatically for addresses from its own APIs. Returns listener with .detach(). |
Interceptor.detachAll() | Remove all hooks |
Interceptor.replace(target, replacement[, data]) | Replace function implementation. Use NativeCallback for JS replacement; CModule NativePointer for C replacement. data accessible via gum_invocation_context_get_listener_function_data(). |
Interceptor.replaceFast(target, replacement) | Like replace() but patches target directly (less overhead). Returns pointer to original implementation. |
Interceptor.revert(target) | Revert replacement |
Interceptor.flush() | Commit pending changes to memory. Call after attach/replace before calling the function via NativeFunction. Auto-flushed on thread exit from JS runtime, send(), RPC return, console.*. |
Interceptor.breakpointKind | "soft" (default) or "hard". Barebone backend only. |
callbacks object:
| Callback | Signature | Description |
|---|---|---|
onEnter(args) | args: NativePointer array | Read/write arguments. args[0], args[1], etc. |
onLeave(retval) | retval: NativePointer-derived | Read/replace return value. retval.replace(1337) or retval.replace(ptr("0x1234")). Don’t store retval — it’s recycled. Copy with ptr(retval.toString()). |
Both callbacks can also be NativePointer values pointing to C functions (from CModule) with signatures:
void onEnter(GumInvocationContext *ic)void onLeave(GumInvocationContext *ic)
this inside callbacks:
| Property | Description |
|---|---|
returnAddress | NativePointer |
context | CPU registers: pc, sp, arch-specific (eax, rax, r0, x0, etc.). Writable. |
errno | (UNIX) current errno, writable |
lastError | (Windows) OS error, writable |
threadId | OS thread ID |
depth | Call depth relative to other invocations |
this is thread-local per invocation — use it to pass data between onEnter and onLeave.
Performance notes:
- Omit unused callbacks (
onEnter/onLeave) — each adds overhead (~6µs foronEnteralone, ~11µs for both on iPhone 5S) - For hot functions, implement callbacks in C via CModule
- Batch multiple values into a single
send()call for high-frequency hooks
1// Basic attach
2Interceptor.attach(Module.getGlobalExportByName('read'), {
3 onEnter(args) {
4 this.fd = args[0].toInt32();
5 this.buf = args[1];
6 this.count = args[2].toInt32();
7 },
8 onLeave(retval) {
9 const n = retval.toInt32();
10 if (n > 0) console.log(hexdump(this.buf, { length: n }));
11 }
12});
13
14// Replace function
15const openPtr = Module.getGlobalExportByName('open');
16const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
17Interceptor.replace(openPtr, new NativeCallback((pathPtr, flags) => {
18 console.log('Opening:', pathPtr.readUtf8String());
19 return open(pathPtr, flags);
20}, 'int', ['pointer', 'int']));
Stalker
Per-thread code tracing engine. Instruments basic blocks by copying them to RWX slabs.
| API | Description |
|---|---|
Stalker.follow([threadId, options]) | Start stalking thread (or current thread if omitted) |
Stalker.unfollow([threadId]) | Stop stalking |
Stalker.exclude(range) | Exclude memory range from tracing ({ base, size }) |
Stalker.parse(events[, options]) | Parse GumEvent binary blob. Options: { annotate: bool, stringify: bool } |
Stalker.flush() | Flush buffered events immediately |
Stalker.garbageCollect() | Free memory after unfollow() at a safe point |
Stalker.invalidate(address) | Invalidate current thread’s translated code for a basic block |
Stalker.invalidate(threadId, address) | Invalidate specific thread’s translated code |
Stalker.addCallProbe(address, callback[, data]) | Call callback synchronously on every call to address. Returns probe ID. C signature: void onCall(GumCallSite *site, gpointer user_data) |
Stalker.removeCallProbe(id) | Remove call probe |
Stalker.trustThreshold | int: -1 (never trust), 0 (always trust), N (trust after N executions). Default: 1 |
Stalker.queueCapacity | Event queue size in events. Default: 16384 |
Stalker.queueDrainInterval | Drain interval in ms. Default: 250. Set to 0 to disable periodic drain (call flush() manually). |
follow() options:
1Stalker.follow(threadId, {
2 events: {
3 call: true, // CALL instructions
4 ret: false, // RET instructions
5 exec: false, // all instructions (high volume)
6 block: false, // block executed (coarse)
7 compile: false // block compiled (coverage)
8 },
9 // Choose ONE of:
10 onReceive(events) { /* raw GumEvent binary blob */ },
11 onCallSummary(summary) { /* { address: callCount } */ },
12
13 // Optional transformer (rewrite basic blocks):
14 transform(iterator) {
15 let insn;
16 while ((insn = iterator.next()) !== null) {
17 // inspect insn.mnemonic, insn.address, etc.
18 iterator.keep(); // emit instruction (drop by not calling keep())
19 }
20 },
21 // C transformer for performance:
22 // transform: cm.transform,
23 // onEvent: cm.process,
24 // data: ptr(1337)
25});
Performance: Use onCallSummary instead of onReceive when only call counts are needed. Use CModule for transform/onEvent callbacks in hot paths.
ObjC
Frida 17+: No longer bundled in GumJS. Install:
npm install frida-objc-bridgeandimport ObjC from 'frida-objc-bridge'. REPL and frida-trace still include it automatically.
Prerequisite: check ObjC.available before using any ObjC API.
| API | Description |
|---|---|
ObjC.available | boolean — ObjC runtime loaded |
ObjC.api | Object mapping ObjC runtime function names to NativeFunction instances |
ObjC.classes | Object mapping class names to ObjC.Object wrappers. Method call: replace : with _: NSString.stringWithString_("Hello") |
ObjC.protocols | Object mapping protocol names to ObjC.Protocol wrappers |
ObjC.mainQueue | GCD main queue |
ObjC.schedule(queue, work) | Schedule JS function on GCD queue (auto NSAutoreleasePool) |
new ObjC.Object(handle[, protocol]) | Wrap existing ObjC instance at NativePointer handle |
new ObjC.Protocol(handle) | Wrap existing protocol |
new ObjC.Block(target[, options]) | Wrap or define a block. For new block: { implementation, types } or { implementation, retType, argTypes }. Call declare(signature) if existing block lacks metadata. |
ObjC.implement(method, fn) | Create NativeCallback compatible with method signature |
ObjC.registerProxy(properties) | Create proxy class: { protocols?, methods?, events: { dealloc(), forward(name) } } |
ObjC.registerClass(properties) | Create ObjC class: { name?, super?, protocols?, methods } |
ObjC.registerProtocol(properties) | Create ObjC protocol: { name?, protocols?, methods } |
ObjC.bind(obj, data) | Bind JS data to ObjC instance |
ObjC.unbind(obj) | Unbind JS data |
ObjC.getBoundData(obj) | Look up bound data |
ObjC.enumerateLoadedClasses([options,] callbacks) | Enumerate classes. options.ownedBy: ModuleMap filter. Callbacks: { onMatch(name, owner), onComplete() } |
ObjC.enumerateLoadedClassesSync([options]) | Sync version → { modulePath: [className] } |
ObjC.choose(specifier, callbacks) | Scan heap for live instances. specifier: ObjC.Object class or { class, subclasses: bool } |
ObjC.chooseSync(specifier) | Sync version → instance[] |
ObjC.selector(name) | JS string → selector |
ObjC.selectorAsString(sel) | selector → JS string |
ObjC.Object special properties:
| Property | Description |
|---|---|
$kind | "instance" | "class" | "meta-class" |
$super | ObjC.Object for chaining super calls |
$superClass | Super-class as ObjC.Object |
$class | Class of this object |
$className | Class name string |
$moduleName | Module path string |
$protocols | { name: ObjC.Protocol } |
$methods | Native method names (including inherited) |
$ownMethods | Native method names (own class only) |
$ivars | Instance variables (read/write via assignment) |
equals(other) | Reference equality check |
clone(options) | New method wrapper with custom NativeFunction options |
1// Hook ObjC method
2const { NSSound } = ObjC.classes;
3Interceptor.attach(NSSound.play.implementation, {
4 onEnter() { send('[NSSound play]'); }
5});
6
7// Replace implementation
8const oldImpl = NSSound.play.implementation;
9NSSound.play.implementation = ObjC.implement(NSSound.play, (handle, sel) => {
10 return oldImpl(handle, sel);
11});
12
13// Intercept block argument
14Interceptor.attach(someMethod, {
15 onEnter(args) {
16 const block = new ObjC.Block(args[4]);
17 const orig = block.implementation;
18 block.implementation = (err, val) => {
19 console.log('block called', err, val);
20 return orig(err, val);
21 };
22 }
23});
Java
Frida 17+: No longer bundled. Install:
npm install frida-java-bridgeandimport Java from 'frida-java-bridge'. REPL and frida-trace still include it automatically.
Prerequisite: check Java.available before using any Java API.
| API | Description |
|---|---|
Java.available | boolean — Dalvik/ART VM loaded |
Java.androidVersion | Android version string |
Java.perform(fn) | Ensure thread attached to VM, call fn. Defers if app class loader not ready. |
Java.performNow(fn) | Like perform() but doesn’t wait for class loader |
Java.scheduleOnMainThread(fn) | Run on VM main thread |
Java.use(className) | Get class wrapper. $new() = constructor, $dispose() = cleanup. Method replacement: Activity.onResume.implementation = function() {...} |
Java.cast(handle, klass) | Wrap raw pointer as class instance. Has .class and .$className properties. |
Java.array(type, elements) | Create Java array from JS array. Pass by reference to Java APIs. |
Java.openClassFile(filePath) | Open .dex: { load(), getClassNames() } |
Java.choose(className, callbacks) | Scan heap for live instances: { onMatch(instance), onComplete() } |
Java.retain(obj) | Duplicate wrapper for use outside replacement method |
Java.enumerateLoadedClasses(callbacks) | { onMatch(name, handle), onComplete() } |
Java.enumerateLoadedClassesSync() | Returns class name array |
Java.enumerateClassLoaders(callbacks) | { onMatch(loader), onComplete() } |
Java.enumerateClassLoadersSync() | Returns loader array |
Java.enumerateMethods(query) | Query: "class!method" with globs. Modifiers: /i (case-insensitive), /s (include signatures), /u (user classes only) |
Java.backtrace([options]) | options.limit (default 16). Returns { id, frames: [{signature, origin, className, methodName, methodFlags, fileName, lineNumber}] } |
Java.isMainThread() | boolean |
Java.registerClass(spec) | Create Java class: { name, superClass?, implements?, fields?, methods } |
Java.deoptimizeEverything() | Force interpreter execution (enables ART instrumentation) |
Java.deoptimizeBootImage() | Deoptimize boot image only |
Java.vm | { perform(fn), getEnv(), tryGetEnv() } |
Java.classFactory | Default factory (app main class loader) |
Java.ClassFactory.get(classLoader) | Factory for specific class loader |
Method flag constants: ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, etc.
1// Hook Android method
2Java.perform(() => {
3 const Activity = Java.use('android.app.Activity');
4 Activity.onResume.implementation = function() {
5 send('onResume called');
6 this.onResume();
7 };
8});
9
10// SSL pinning bypass pattern
11Java.perform(() => {
12 const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
13 const MyTM = Java.registerClass({
14 name: 'com.example.MyTM',
15 implements: [X509TrustManager],
16 methods: {
17 checkClientTrusted(chain, authType) {},
18 checkServerTrusted(chain, authType) {},
19 getAcceptedIssuers() { return []; }
20 }
21 });
22});
CPU Instruction
Instruction
1const insn = Instruction.parse(target); // target: NativePointer
Fields: address, next (NativePointer to next insn), size, mnemonic, opStr, operands, regsRead, regsWritten, groups, toString()
On 32-bit ARM: LSB=0 for ARM, 1 for Thumb.
Writer Classes
All writers share this interface:
| Property/Method | Description |
|---|---|
new XxxWriter(codeAddress[, {pc}]) | Create writer. pc option needed when writing to scratch buffer (e.g. Memory.patchCode() on iOS). |
reset(codeAddress[, {pc}]) | Recycle instance |
dispose() | Eagerly free memory |
flush() | Resolve labels and write pending data. Always call when done. |
base | NativePointer to first byte of output |
code | NativePointer to next byte of output |
pc | Program counter at next output byte |
offset | Current offset as number |
putLabel(id) | Place label for forward/backward references |
putBytes(data) | Write raw ArrayBuffer |
putNop() | NOP |
putBreakpoint() | OS/arch-specific breakpoint |
X86Writer — also: putCallAddressWithArguments, putCallReg, putJmpAddress, putJmpReg, putJccShortLabel, putJccNearLabel, putMovRegReg, putMovRegU32, putMovRegU64, putAddRegImm, putSubRegImm, putPushReg, putPopReg, putRet, putLeave, putCmpRegI32, putTestRegReg, putXchgRegRegPtr, putLockXaddRegPtrReg, putFxsaveRegPtr, putU8, putS8
ArmWriter — also: putBImm, putBLabel, putBlImm, putBlxImm, putBxReg, putLdrRegAddress, putLdrRegU32, putStrRegReg, putMovRegReg, putAddRegRegImm, putSubRegRegImm, putVpushRange, putVpopRange, putInstruction
ThumbWriter — also: putBImm, putBlImm, putBlxImm, putBxReg, putCbzRegLabel, putCbnzRegLabel, putPushRegs, putPopRegs, putLdrRegAddress, putStrRegReg, putMovRegReg, putAddRegImm, putSubRegImm, putInstruction, putInstructionWide, commitLabel
Arm64Writer — also: putBImm, putBLabel, putBCondLabel, putBlImm, putBlrReg, putBrReg, putBrRegNoAuth, putBlrRegNoAuth, putRetReg, putCbzRegLabel, putCbnzRegLabel, putTbzRegImmLabel, putLdrRegAddress, putLdrRegU64, putLdrRegRef/putLdrRegValue, putAdrpRegAddress, putStrRegReg, putLdpRegRegRegOffset, putStpRegRegRegOffset, putMovRegReg, putAddRegRegImm, putSubRegRegImm, putXpaciReg, sign(value), putMrs
MipsWriter — also: putJAddress, putJrReg, putJalAddress, putBeqRegRegLabel, putLaRegAddress, putLwRegRegOffset, putSwRegRegOffset, putMoveRegReg, putAdduRegRegReg, putInstruction
Relocator Classes
1new XxxRelocator(inputCode, output)
2// inputCode: NativePointer; output: corresponding XxxWriter
| Method | Description |
|---|---|
reset(inputCode, output) | Recycle |
dispose() | Free memory |
input | Latest Instruction read (null initially) |
eob | End-of-block reached (branch/call/ret) |
eoi | End-of-input reached |
readOne() | Buffer next instruction, returns bytes read so far (0 = EOI) |
peekNextWriteInsn() | Peek next Instruction to write |
peekNextWriteSource() | Peek next source address |
skipOne() | Skip next instruction |
writeOne() | Write next buffered instruction |
writeAll() | Write all buffered instructions |
ThumbRelocator also has copyOne().
Enum Types
x86 Register: xax xcx xdx xbx xsp xbp xsi xdi eax ecx … rax rcx … r8-r15 eip rip
x86 InstructionId: jo jno jb jae je jne jbe ja js jns jp jnp jl jge jle jg jcxz jecxz jrcxz
x86 BranchHint: no-hint likely unlikely
x86 PointerTarget: byte dword qword
ARM Register: r0-r15 sp lr sb sl fp ip pc s0-s31 d0-d31 q0-q15
ARM ConditionCode: eq ne hs lo mi pl vs vc hi ls ge lt gt le al
ARM Shifter: asr lsl lsr ror rrx asr-reg lsl-reg lsr-reg ror-reg rrx-reg
AArch64 Register: x0-x30 w0-w30 sp lr fp wsp wzr xzr nzcv ip0 ip1 s0-s31 d0-d31 q0-q31
AArch64 ConditionCode: eq ne hs lo mi pl vs vc hi ls ge lt gt le al nv
AArch64 IndexMode: post-adjust signed-offset pre-adjust
MIPS Register: v0 v1 a0-a3 t0-t9 s0-s8 k0 k1 gp sp fp ra hi lo zero at 0-31
Others
Console
1console.log(line) // stdout
2console.warn(line) // stderr
3console.error(line) // stderr
ArrayBuffer arguments are auto-formatted via hexdump().
Hexdump
1hexdump(target[, options])
2// target: ArrayBuffer or NativePointer
3// options: { offset: 0, length: 64, header: true, ansi: false, address: ptr('0x...') }
Shorthand
| Shorthand | Equivalent |
|---|---|
int64(v) | new Int64(v) |
uint64(v) | new UInt64(v) |
ptr(s) | new NativePointer(s) |
NULL | ptr("0") |
Communication (send/recv/rpc)
| API | Description |
|---|---|
send(message[, data]) | Send JSON-serializable object to host. Optional data: ArrayBuffer or byte array. Async but not optimized for high frequency — batch values. |
recv([type,] callback) | Register one-shot callback for next message from host. Optionally filter by type field. Call again to receive next. Callback: (message, data|null) |
rpc.exports | Assign object to expose RPC methods. Values can return plain values or Promises. |
1// Agent side
2rpc.exports = {
3 add(a, b) { return a + b; },
4 sub(a, b) { return new Promise(r => setTimeout(() => r(a - b), 100)); }
5};
6
7// Host side (Python)
8print(script.exports.add(2, 3)) # 5
9print(script.exports.sub(5, 3)) # 2
Timing Events
| API | Description |
|---|---|
setTimeout(func, delay[, ...params]) | Call after delay ms. Returns ID. |
clearTimeout(id) | Cancel |
setInterval(func, delay[, ...params]) | Call every delay ms. Returns ID. |
clearInterval(id) | Cancel |
setImmediate(func[, ...params]) | Schedule on next tick. Returns ID. |
clearImmediate(id) | Cancel |
Garbage Collection
1gc() // Force GC. Useful for testing Script.bindWeak() logic.
Worker
Isolated JS heap/lock for background processing.
1const w = new Worker(url[, { onMessage }]);
2w.post(message[, data]); // send to worker (use recv() inside)
3w.exports.method(); // call rpc.exports defined by worker → Promise
4w.terminate();
Cloak
Hide Frida’s resources from introspection APIs (Process.enumerateThreads(), etc.). Frida’s own resources are cloaked automatically.
| API | Description |
|---|---|
Cloak.addThread(id) / removeThread(id) | Hide/show thread |
Cloak.hasCurrentThread() / hasThread(id) | Check if cloaked |
Cloak.addRange(range) / removeRange(range) | Hide/show memory range ({ base, size }) |
Cloak.hasRangeContaining(address) | Check if address in cloaked range |
Cloak.clipRange(range) | Returns visible parts of range as array (empty = all cloaked, null = all visible) |
Cloak.addFileDescriptor(fd) / removeFileDescriptor(fd) | Hide/show fd |
Cloak.hasFileDescriptor(fd) | Check if fd is cloaked |
Profiler
Worst-case profiler built on Interceptor. Tracks maximum time per function call.
1const profiler = new Profiler();
2profiler.instrument(functionAddress, sampler[, { describe(args) }]);
3// describe: called on new worst-case; return string describing args
4const xml = profiler.generateReport();
Samplers
| Class | Description |
|---|---|
new CycleSampler() | CPU cycles (e.g. RDTSC on x86) |
new BusyCycleSampler() | CPU cycles for current thread only (e.g. QueryThreadCycleTime on Windows) |
new WallClockSampler() | Wall clock time |
new UserTimeSampler([threadId]) | User-space time for thread (default: current) |
new MallocCountSampler() | Count malloc/calloc/realloc calls |
new CallCountSampler(functions) | Count calls to specified NativePointer functions |
All samplers have sample() → bigint.