Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Daten und Forschung · SKILL.md

frida

frida

Use this skill whenever writing, debugging, or reviewing Frida instrumentation scripts targeting iOS ARM64 or Android. Triggers include: any mention of 'Frida', 'dynamic instrumentation', 'hooking', 'Interceptor', 'Stalker', 'ObjC bridge', 'Java bridge', 'frida-server', 'frida-gadget', 'NativeFunction', or requests to hook/trace/patch native or managed func…

Independent GitHub snapshot · Automated classification · Original documentation governs

Aas0ler / skillsApache-2.0

Skill-Dokumentation

GitHub

name: frida description: "Use this skill whenever writing, debugging, or reviewing Frida instrumentation scripts targeting iOS ARM64 or Android. Triggers include: any mention of 'Frida', 'dynamic instrumentation', 'hooking', 'Interceptor', 'Stalker', 'ObjC bridge', 'Java bridge', 'frida-server', 'frida-gadget', 'NativeFunction', or requests to hook/trace/patch native or managed functions at runtime. Also use when bypassing SSL pinning, root detection, anti-cheat systems, or Frida self-detection. Apply for both JavaScript and TypeScript agents targeting frida-tools 14+ and Frida 17.x. Do NOT use for static binary analysis (use radare2/Ghidra skills instead), build-time patching, or non-Frida instrumentation frameworks."

Frida 17 JavaScript API Reference

Version: Frida 17.x (latest: 17.6.2, Feb 2026) · frida-tools 14+ Runtimes: QuickJS (default), V8 (opt-in) · TypeScript supported via frida-compile Script templates: See EXAMPLES.md for ready-to-use iOS ARM64 & Android scripts.


Table of Contents

  1. Setup & Tooling
  2. Migration Notes from Frida 16.x
  3. Global / Script Scope
  4. Process
  5. Module
  6. Memory
  7. NativePointer
  8. NativeFunction & NativeCallback
  9. Interceptor
  10. Stalker
  11. Thread
  12. DebugSymbol
  13. CModule
  14. ObjC Bridge (iOS/macOS)
  15. Java Bridge (Android)
  16. Socket
  17. File & I/O
  18. Hexdump & Utilities
  19. Script IPC (send / recv)
  20. Frida.Compiler
  21. Arm64Writer / X86Writer / ThumbWriter
  22. Platform Tips: iOS ARM64
  23. Platform Tips: Android
  24. Quick Reference Table

1. Setup & Tooling

Install Frida and launch an agent against a target process over USB, TCP, or locally.

# Minimal working setup
pip install frida frida-tools
frida -U -f com.example.App -l script.js   # spawn via USB
frida -U -F -l script.js                   # attach to frontmost iOS app
# Full tooling reference
pip install frida frida-tools

# Spawn and inject
frida -U -f com.example.App -l script.js          # USB, spawn
frida -U -F -l script.js                           # USB, frontmost app (iOS)
frida -H 192.168.1.10:27042 -f com.example.App -l script.js  # remote TCP

# Interactive REPL (ObjC/Java bridges auto-loaded in frida-tools 14+)
frida -U -F

# Compile TypeScript agent
frida-compile agent.ts -o agent.js
frida-compile agent.ts -o agent.js -w   # watch mode

# Auto-trace functions by name or glob pattern
frida-trace -U -F -i "open" -I "NSURLSession*"

# Push frida-server to Android device
adb push frida-server /data/local/tmp/
adb shell "chmod +x /data/local/tmp/frida-server && /data/local/tmp/frida-server &"

# iOS (jailbroken): install via Cydia/Sileo — package: re.frida.server

2. Migration Notes from Frida 16.x

Frida 17 removed three categories of long-deprecated APIs — update existing scripts before running them against a 17.x agent.

// Quick migration checklist — if your script uses any of these patterns, update it:
// ❌ enumerateModulesSync() or callback-style enumerate  → ✅ returns array directly
// ❌ Memory.readU32(ptr)                                 → ✅ ptr.readU32()
// ❌ Module.getExportByName('lib', 'fn')                 → ✅ Process.getModuleByName('lib').getExportByName('fn')
// ❌ ObjC/Java globals without import in TS agents       → ✅ import "frida-objc-bridge"
// ── Enumeration APIs ──────────────────────────────────────────────────────
// ❌ OLD
Process.enumerateModules({ onMatch(m) { ... }, onComplete() { } });
Process.enumerateModulesSync();
// ✅ NEW — returns array directly
for (const mod of Process.enumerateModules()) { console.log(mod.name); }

// ── Memory read/write ─────────────────────────────────────────────────────
// ❌ OLD
Memory.readU32(ptr('0x1234'));
Memory.writeU32(ptr('0x1234'), 100);
// ✅ NEW — methods on NativePointer, chainable
ptr('0x1234').readU32();
ptr('0x1234').writeU32(100);
ptr('0x1234').add(4).writeU32(10).add(4).writeU16(20);

// ── Static Module helpers ─────────────────────────────────────────────────
// ❌ OLD
Module.getBaseAddress('libc.so');
Module.getExportByName('libc.so', 'open');
Module.findExportByName('libc.so', 'open');
Module.ensureInitialized('libc.so');
// ✅ NEW
Process.getModuleByName('libc.so').base;
Process.getModuleByName('libc.so').getExportByName('open');
Process.getModuleByName('libc.so').findExportByName('open');
Module.getGlobalExportByName('open');   // replaces getExportByName(null, 'open')

// ── Runtime bridges ───────────────────────────────────────────────────────
// ObjC / Java / Swift no longer bundled in Frida core.
// frida-tools 14+ REPL auto-loads them. TypeScript agents must import explicitly:
import "frida-objc-bridge";   // provides ObjC global
import "frida-java-bridge";   // provides Java global

3. Global / Script Scope

Top-level globals and built-ins available in every Frida agent without any import.

// Typical agent preamble
const base     = Process.mainModule.base;
const libc     = Process.getModuleByName('libc.so');
const openAddr = libc.getExportByName('open');
send({ type: 'ready', base: base.toString() });
// Full global reference
ptr(val)           // Create NativePointer from hex string or number
NULL               // NativePointer(0x0)
Process            // Current process introspection
Memory             // Memory utilities (alloc, scan, patch, protect)
Interceptor        // Function hook engine
Stalker            // Code tracing engine
Thread             // Thread enumeration + backtrace
DebugSymbol        // Address ↔ symbol resolution
CModule            // Inline C compilation
Module             // Static module helpers (getGlobalExportByName, load)
ObjC               // Objective-C runtime bridge (iOS/macOS)
Java               // Android ART/JVM bridge
console            // console.log / console.warn / console.error
hexdump(buf)       // Pretty hex dump to string
send(msg, data?)   // Send message + optional binary to Python host
recv(type, cb)     // Register handler for message from Python host
rpc.exports        // Expose JS functions as RPC callable from host

// Script lifecycle
Script.pin();      // Prevent GC of this script object
Script.unpin();

// Top-level await is supported
const result = await SomeAsyncOperation();

4. Process

Provides identity, module lookup, memory range enumeration, thread listing, and exception handling for the instrumented process.

// Log process info and look up a module
console.log(Process.id, Process.arch, Process.platform);
const libc = Process.getModuleByName('libc.so');
console.log('libc base:', libc.base, 'size:', libc.size);
// ── Identity ──────────────────────────────────────────────────────────────
Process.id                  // PID (number)
Process.arch                // 'arm64' | 'x64' | 'arm' | 'ia32'
Process.platform            // 'darwin' | 'linux' | 'windows' | 'freebsd'
Process.pageSize            // page size in bytes
Process.pointerSize         // 4 or 8
Process.codeSigningPolicy   // 'optional' | 'required'
Process.mainModule          // Module object for the main executable
Process.currentDir          // working directory string

// ── Module lookup ─────────────────────────────────────────────────────────
Process.enumerateModules()                   // → Module[]
Process.getModuleByName('libc.so')           // throws if not found
Process.findModuleByName('libc.so')          // → Module | null
Process.getModuleByAddress(ptr('0x1234'))
Process.findModuleByAddress(ptr('0x1234'))

// ── Memory ranges ─────────────────────────────────────────────────────────
Process.enumerateRanges('r-x')                             // → RangeDetails[]
Process.enumerateRanges({ protection: 'r-x', coalesce: true })
Process.enumerateMallocRanges()                            // heap chunks

// RangeDetails: { base: NativePointer, size: number, protection: string,
//                 file?: { path, offset, size } }

// ── Threads ───────────────────────────────────────────────────────────────
Process.enumerateThreads()          // → ThreadDetails[] (Frida threads hidden)
Process.getCurrentThreadId()        // → number

// ── Exception handler ─────────────────────────────────────────────────────
Process.setExceptionHandler((details) => {
  console.log('Exception:', details.type, details.address);
  return false; // false = propagate to OS
});

5. Module

Represents a loaded binary (.so, .dylib, or executable) and exposes its exports, symbols, imports, and sections.

// Look up a module and find two exports without redundant lookups
const libc   = Process.getModuleByName('libc.so');
const openFn = libc.getExportByName('open');
const readFn = libc.getExportByName('read');
console.log('open @', openFn, 'read @', readFn);
// ── Module object properties ──────────────────────────────────────────────
mod.name    // 'libgame.so'
mod.path    // full filesystem path
mod.base    // NativePointer — load address
mod.size    // size in bytes

// ── Instance methods (Frida 17+) ──────────────────────────────────────────
mod.enumerateImports()        // → ImportDetails[]
mod.enumerateExports()        // → ExportDetails[]
mod.enumerateSymbols()        // → SymbolDetails[]
mod.enumerateSections()       // → SectionDetails[]
mod.enumerateDependencies()   // → string[]

mod.findExportByName('fn')    // → NativePointer | null
mod.getExportByName('fn')     // → NativePointer (throws if missing)
mod.findSymbolByName('_ZN…')  // → NativePointer | null
mod.getSymbolByName('_ZN…')   // → NativePointer

// ExportDetails: { type: 'function'|'variable', name: string, address: NativePointer }

// ── Static global helpers ─────────────────────────────────────────────────
Module.getGlobalExportByName('open')    // search all loaded modules; throws if missing
Module.findGlobalExportByName('open')   // → NativePointer | null
Module.load('/path/to/lib.so')          // dlopen and return Module

6. Memory

Allocates, copies, protects, patches, and scans process memory.

// Allocate a C string and scan for its bytes
const buf = Memory.allocUtf8String('FIND_ME');
Memory.scan(buf, 8, '46 49 4e 44', {
  onMatch(addr) { console.log('found at', addr); },
  onError() {},
  onComplete() {}
});
// ── Allocation ────────────────────────────────────────────────────────────
Memory.alloc(256)                            // zeroed buffer → NativePointer
Memory.allocUtf8String('hello')              // null-terminated UTF-8
Memory.allocUtf16String('hello')             // null-terminated UTF-16
Memory.allocByteArray(new Uint8Array([0x90, 0x90]))

// ── Copy / duplicate ──────────────────────────────────────────────────────
Memory.copy(dst, src, n)       // copy n bytes src → dst
Memory.dup(src, n)             // allocate + copy → new NativePointer

// ── Protection ────────────────────────────────────────────────────────────
Memory.protect(addr, size, 'rwx')          // change page protection
Memory.queryProtection(addr)               // → 'r-x' | 'rwx' | etc.

// ── Code patching (W^X safe) ──────────────────────────────────────────────
Memory.patchCode(addr, size, (code) => {
  const w = new Arm64Writer(code, { pc: addr });
  w.putNop();
  w.flush();
});

// ── Scanning (async, callback-based — remains async in Frida 17) ──────────
// Pattern format: 'AA BB ?? DD'  — ?? is a wildcard byte
Memory.scan(base, size, 'DE AD BE EF', {
  onMatch(address, size)  { console.log('found @', address); },
  onError(reason)         { /* skip unreadable pages */ },
  onComplete()            { }
});

// NOTE: Memory.readXxx() / Memory.writeXxx() static methods REMOVED in 17.
// Use NativePointer instance methods instead — see §7.

7. NativePointer

The universal address type in Frida — every memory address is a NativePointer with built-in arithmetic, read, and write methods.

// Read a simple struct: vtable pointer + two 32-bit fields
const obj  = ptr('0x200000');
const vtbl = obj.readPointer();
const hp   = obj.add(8).readU32();
const mp   = obj.add(12).readU32();
console.log('vtable:', vtbl, 'hp:', hp, 'mp:', mp);
const p = ptr('0x100400000');

// ── Arithmetic (returns new NativePointer) ────────────────────────────────
p.add(0x10)   p.sub(4)
p.and(0xFFF)  p.or(1)   p.xor(0xFF)
p.shl(2)      p.shr(1)  p.not()

// ── Comparison ────────────────────────────────────────────────────────────
p.equals(q)          // boolean
p.compare(q)         // -1 | 0 | 1
p.isNull()

// ── Conversion ────────────────────────────────────────────────────────────
p.toInt32()   p.toUInt32()
p.toString()  p.toString(16)
p.toMatchPattern()   // → 'AA BB CC DD' byte string for use in Memory.scan

// ── Read ──────────────────────────────────────────────────────────────────
p.readPointer()      // → NativePointer
p.readS8()   p.readU8()
p.readS16()  p.readU16()
p.readS32()  p.readU32()
p.readS64()  p.readU64()
p.readFloat()   p.readDouble()
p.readByteArray(n)    // → ArrayBuffer
p.readCString()       // null-terminated ASCII/UTF-8
p.readUtf8String(len?)
p.readUtf16String(len?)
p.readAnsiString(len?)  // Windows only

// ── Write (returns same NativePointer for chaining) ───────────────────────
p.writePointer(q)
p.writeS8(v)   p.writeU8(v)
p.writeS16(v)  p.writeU16(v)
p.writeS32(v)  p.writeU32(v)
p.writeS64(v)  p.writeU64(v)
p.writeFloat(v)   p.writeDouble(v)
p.writeByteArray(arrayBuffer)
p.writeUtf8String('hello')
p.writeUtf16String('hello')

// ── Chaining ──────────────────────────────────────────────────────────────
ptr('0x5000')
  .add(8).writeU32(100)
  .add(4).writeU16(42)
  .add(2).writeU8(1);

8. NativeFunction & NativeCallback

Wraps a native C function so it can be called directly from JavaScript, and creates a native-callable stub backed by a JavaScript function.

// Call strlen directly from JS
const strlen = new NativeFunction(
  Module.getGlobalExportByName('strlen'), 'int', ['pointer']
);
console.log('length:', strlen(Memory.allocUtf8String('hello'))); // → 5
// ── NativeFunction: call any native function from JS ─────────────────────
const open = new NativeFunction(
  Process.getModuleByName('libc.so').getExportByName('open'),
  'int',               // return type
  ['pointer', 'int'],  // argument types
  { abi: 'default' }   // optional: 'win64' | 'sysv' | 'stdcall' | 'fastcall'
);
const fd = open(Memory.allocUtf8String('/etc/hosts'), 0 /* O_RDONLY */);

// Supported type strings:
// 'void' 'bool' 'char' 'uchar' 'short' 'ushort' 'int' 'uint'
// 'long' 'ulong' 'longlong' 'ulonglong' 'float' 'double'
// 'pointer' 'size_t' 'ssize_t' 'int8_t' … 'uint64_t'

// ── NativeCallback: expose a JS function as a native C function ───────────
const myHook = new NativeCallback(
  (a, b) => {
    console.log('called with:', a, b);
    return a + b;
  },
  'int',          // return type
  ['int', 'int']  // argument types
);
// myHook is a NativePointer — pass it to Interceptor.replace() or any C API

9. Interceptor

The primary hooking engine — attaches onEnter/onLeave callbacks to observe any native function, or replaces it entirely with a new implementation.

// Log every call to open() and override the return value
Interceptor.attach(Module.getGlobalExportByName('open'), {
  onEnter(args) {
    console.log('[open]', args[0].readCString(), 'flags:', args[1].toInt32());
  },
  onLeave(retval) {
    console.log('[open] fd =', retval.toInt32());
  }
});
// ── attach: observe without replacing ─────────────────────────────────────
const hook = Interceptor.attach(targetPtr, {
  onEnter(args) {
    // args[n]       → NativePointer (n-th argument)
    // this.context  → CpuContext: pc, sp, fp, x0–x28 (ARM64) / rax… (x64)
    // this.threadId, this.returnAddress, this.depth, this.errno
    this.savedArg = args[0].readUtf8String();  // stash state for onLeave
  },
  onLeave(retval) {
    retval.replace(ptr(1));   // override return value
  }
});
hook.detach();   // remove this specific hook

// ── replace: swap implementation entirely ────────────────────────────────
Interceptor.replace(targetPtr,
  new NativeCallback((arg0) => {
    console.log('replaced, arg0 =', arg0);
    // To call original: new NativeFunction(targetPtr, 'void', ['pointer'])(arg0)
  }, 'void', ['pointer'])
);

// ── replaceFast: lower overhead, no InvocationContext, returns trampoline ─
const origPtr = Interceptor.replaceFast(targetPtr,
  new NativeCallback((arg0) => {
    return new NativeFunction(origPtr, 'int', ['pointer'])(arg0);
  }, 'int', ['pointer'])
);

// ── Housekeeping ──────────────────────────────────────────────────────────
Interceptor.revert(targetPtr);   // undo a replace / replaceFast
Interceptor.flush();              // apply all pending patches immediately

10. Stalker

A dynamic code tracing engine that follows thread execution at the basic-block or instruction level, enabling coverage collection, call tracing, and inline code transformation.

// Collect basic-block addresses while a function executes
Interceptor.attach(targetFn, {
  onEnter() {
    Stalker.follow(this.threadId, {
      events: { block: true },
      onReceive(events) {
        for (const [, start] of Stalker.parse(events))
          console.log('block @', start);
      }
    });
  },
  onLeave() {
    Stalker.unfollow(this.threadId);
    Stalker.flush();
    Stalker.garbageCollect();
  }
});
// ── Follow a thread ───────────────────────────────────────────────────────
Stalker.follow(threadId, {
  events: {
    call:    true,   // CALL instructions
    ret:     false,  // RET instructions
    exec:    false,  // every instruction (very high overhead)
    block:   true,   // basic block start addresses
    compile: false   // each time a block is JIT-compiled
  },
  // transform: inline instrumentation mode (alternative to onReceive)
  transform(iterator) {
    let instr = iterator.next();
    while (instr !== null) {
      iterator.putCallout((ctx) => { /* called at this instruction */ });
      iterator.keep();
      instr = iterator.next();
    }
  },
  onReceive(events) {
    // events: raw binary buffer
    const parsed = Stalker.parse(events, { annotate: true, stringify: false });
    // → [[type, from, to], …] or [[type, start, end], …] for blocks
  },
  onCallSummary(summary) {
    // summary: { [addressHex]: hitCount }
  }
});

Stalker.unfollow(threadId)
Stalker.flush()                               // drain buffered events
Stalker.garbageCollect()                      // free compiled code for unfollowed threads
Stalker.isFollowing(threadId)                 // → boolean
Stalker.exclude({ base: mod.base, size: mod.size })  // skip a memory range

Stalker.queueCapacity = 16384                 // event ring buffer size (entries)
Stalker.queueDrainInterval = 250              // drain interval in ms

11. Thread

Enumerates process threads, generates symbolicated backtraces, and provides sleep control for the current thread.

// Print a symbolicated backtrace from inside a hook
Interceptor.attach(targetFn, {
  onEnter(args) {
    Thread.backtrace(this.context, Backtracer.ACCURATE)
      .map(DebugSymbol.fromAddress)
      .forEach(s => console.log(' ', s.toString()));
  }
});
// ── Enumeration ───────────────────────────────────────────────────────────
// Frida's own threads are automatically hidden from results
Process.enumerateThreads()
// → [{ id: number, name?: string, state: string, context: CpuContext }, …]
// state: 'running' | 'stopped' | 'waiting' | 'uninterruptible' | 'halted'

Process.getCurrentThreadId()   // → number

// ── Timing ────────────────────────────────────────────────────────────────
Thread.sleep(0.5)   // sleep current thread for 0.5 seconds (float)

// ── Backtrace ─────────────────────────────────────────────────────────────
Thread.backtrace(context, Backtracer.ACCURATE)  // → NativePointer[]
Thread.backtrace(context, Backtracer.FUZZY)     // faster but less precise
// ACCURATE uses libunwind; FUZZY uses a heuristic frame scan

12. DebugSymbol

Resolves memory addresses to human-readable symbol names and source locations, and looks up addresses by symbol name.

// Annotate a backtrace with symbol names
Thread.backtrace(this.context, Backtracer.FUZZY)
  .forEach(addr => console.log(DebugSymbol.fromAddress(addr).toString()));
// Example output: "0x100403f10 libgame!GameManager::isCheatEnabled()"
// ── Address → symbol ──────────────────────────────────────────────────────
const sym = DebugSymbol.fromAddress(ptr('0x100403f10'));
sym.address      // NativePointer
sym.name         // mangled name string, or null if stripped
sym.moduleName   // 'libgame.so'
sym.fileName     // source file path (only if DWARF info is present)
sym.lineNumber   // number
sym.toString()   // one-line human-readable string

// ── Name → address ────────────────────────────────────────────────────────
const sym2 = DebugSymbol.fromName('_ZN7UObjectL13ProcessEventEPS_P9UFunctionPv');
// Use sym2.address to get the NativePointer

// ── Load external debug info ──────────────────────────────────────────────
DebugSymbol.load(Process.getModuleByName('libgame.so').path);
// Forces DWARF parsing for modules not yet fully analysed

13. CModule

Compiles and links a snippet of C source code into the process at runtime, producing native callbacks with zero JavaScript overhead — ideal for hot hooks and performance-critical instrumentation.

// Count hook hits with no JS round-trip overhead
const cm = new CModule(`
  #include <stdint.h>
  static uint32_t hits = 0;
  void on_enter(void) { hits++; }
  uint32_t get_hits(void) { return hits; }
`);
Interceptor.attach(targetFn, { onEnter: cm.on_enter });
// Read the counter from JS:
const getHits = new NativeFunction(cm.get_hits, 'uint32', []);
console.log('hits:', getHits());
// ── Construction ──────────────────────────────────────────────────────────
const cm = new CModule(`
  #include <gum/guminterceptor.h>   // GumInvocationContext etc.
  #include <stdint.h>
  // Available: GumStalker, GumInterceptor, gum_* APIs, standard C headers

  extern void imported_fn(void);    // symbol imported from JS

  static uint32_t call_count = 0;
  void on_enter(GumInvocationContext * ic) { call_count++; }
  uint32_t get_count(void) { return call_count; }
`,
  // Map C extern names to JS NativePointers
  { imported_fn: someNativeFuncPtr }
);

// ── Access exported C symbols (each is a NativePointer) ──────────────────
cm.on_enter    // → NativePointer
cm.get_count   // → NativePointer

// Use as a native Interceptor callback (no JS round-trip)
Interceptor.attach(target, { onEnter: cm.on_enter });

// Call the C function from JS
const getCount = new NativeFunction(cm.get_count, 'uint32', []);
console.log('calls:', getCount());

cm.dispose();  // unmap the module when no longer needed

// NOTE: CModule is unavailable on unsupported architectures — wrap in try/catch

14. ObjC Bridge (iOS/macOS)

Exposes the Objective-C runtime, letting you look up classes and protocols, hook instance and class methods, intercept and replace blocks, and schedule work on the main queue.

// Hook NSURLSession and log every request URL
Interceptor.attach(
  ObjC.classes.NSURLSession['- dataTaskWithRequest:completionHandler:'].implementation,
  {
    onEnter(args) {
      const req = new ObjC.Object(args[2]);
      console.log('[NSURLSession] URL:', req.URL().absoluteString().toString());
    }
  }
);
// ── Availability ──────────────────────────────────────────────────────────
ObjC.available   // boolean — false on Android / non-ObjC processes

// frida-tools 14+ REPL auto-loads ObjC.
// TypeScript agent: import "frida-objc-bridge";

// ── Class access ──────────────────────────────────────────────────────────
const NSString = ObjC.classes.NSString;
NSString['+ stringWithUTF8String:']    // class method wrapper
NSString['- length']                   // instance method wrapper

// ── ObjC.Object ───────────────────────────────────────────────────────────
const obj = new ObjC.Object(ptr);
obj.$className      // class name string
obj.$methods        // all methods including inherited
obj.$ownMethods     // own methods only
obj.description()   // call -description → ObjC.Object
obj.retain()  obj.release()  obj.autorelease()

// ── Enumerate classes / protocols ─────────────────────────────────────────
ObjC.enumerateLoadedClasses()   // → string[]
const proto = ObjC.protocols.NSURLSessionDelegate;
proto.methods   // → { methodName: { types, read, write } }

// ── Blocks ────────────────────────────────────────────────────────────────
// Create a new ObjC block from JS
const block = new ObjC.Block({
  retType: 'void',
  argTypes: ['pointer'],
  implementation(arg) { console.log('block called', arg); }
});
// Intercept an existing block passed as a function argument
const existing = new ObjC.Block(args[3]);
existing.implementation = (arg) => { /* replace body */ };

// ── Selectors ─────────────────────────────────────────────────────────────
ObjC.selector('viewDidLoad')         // JS string → SEL (NativePointer)
ObjC.selectorAsString(selPtr)        // SEL → JS string

// ── Main queue scheduling ─────────────────────────────────────────────────
ObjC.schedule(ObjC.mainQueue, () => {
  const pool = ObjC.classes.NSAutoreleasePool.alloc().init();
  // ObjC code here runs on the main thread
  pool.release();
});

15. Java Bridge (Android)

Exposes the Android ART/JVM runtime, enabling you to hook Java methods, access static fields, instantiate classes, enumerate live objects on the heap, and work with custom class loaders.

// Force isUserPremium() to always return true
Java.perform(() => {
  const GameManager = Java.use('com.example.game.GameManager');
  GameManager.isUserPremium.implementation = function() {
    console.log('[+] isUserPremium hooked → true');
    return true;
  };
});
// ── Availability ──────────────────────────────────────────────────────────
Java.available   // boolean

// frida-tools 14+ REPL auto-loads Java.
// TypeScript agent: import "frida-java-bridge";

// ── All Java API work must be wrapped in Java.perform() ───────────────────
Java.perform(() => {

  // Get a class wrapper
  const Activity = Java.use('android.app.Activity');

  // Hook instance method (simple overwrite)
  Activity.onResume.implementation = function() {
    console.log('[+] onResume:', this.getClass().getName());
    this.onResume();   // call original
  };

  // Hook overloaded method (specify full signature)
  Activity.startActivity
    .overload('android.content.Intent')
    .implementation = function(intent) {
      console.log('[+] intent:', intent.toString());
      this.startActivity(intent);
    };

  // Read / write a static field
  const Build = Java.use('android.os.Build');
  console.log('Model:', Build.MODEL.value);
  Build.MODEL.value = 'Pixel 9 Pro';

  // Instantiate a class
  const Intent = Java.use('android.content.Intent');
  const i = Intent.$new('android.intent.action.VIEW');

  // Cast a raw pointer to a Java type
  const Runnable = Java.use('java.lang.Runnable');
  const r = Java.cast(somePtr, Runnable);

  // Find all live instances of a class on the heap
  Java.choose('com.example.game.GameManager', {
    onMatch(instance) { console.log('[+] found:', instance); },
    onComplete() {}
  });

  // Prevent GC of a Java object across callbacks
  const retained = Java.retain(instance);
  // Java.release(retained) when done

  // Run code on the ART main thread
  Java.scheduleOnMainThread(() => { /* UI-safe code here */ });

  // Iterate all class loaders (useful for obfuscated / split APKs)
  Java.enumerateClassLoaders({
    onMatch(loader) {
      try { console.log(loader.loadClass('com.hidden.X')); } catch(e) {}
    },
    onComplete() {}
  });

  // List all currently loaded class names
  Java.enumerateLoadedClasses({
    onMatch(name) { if (name.includes('cheat')) console.log(name); },
    onComplete() {}
  });
});

// Access the raw JNI environment (outside Java.perform)
const env     = Java.vm.getEnv();
const jstring = env.newStringUtf('hello from JNI');

16. Socket

Creates outbound TCP/UDP connections or inbound listeners directly from within the agent, useful for building a side-channel without going through send()/recv().

// Connect to localhost and write a line
const conn = await Socket.connect({ family: 'ipv4', host: '127.0.0.1', port: 9999 });
await conn.output.writeAll(new TextEncoder().encode('hello\n'));
await conn.close();
// ── Outbound connection ───────────────────────────────────────────────────
const conn = await Socket.connect({
  family: 'ipv4',     // 'ipv4' | 'ipv6' | 'unix'
  host:   '127.0.0.1',
  port:   1337
});
// conn: IOStream { input: InputStream, output: OutputStream }
await conn.output.writeAll(new Uint8Array([0x01, 0x02, 0x03]));
const data = await conn.input.read(256);   // → ArrayBuffer
await conn.close();

// ── Inbound listener ──────────────────────────────────────────────────────
const listener = await Socket.listen({ family: 'ipv4', host: '0.0.0.0', port: 9999 });
const accepted  = await listener.accept();
// accepted has the same IOStream shape as conn

17. File & I/O

Reads and writes files on the device filesystem directly from the agent — useful for dumping memory regions, reading /proc entries, or writing logs without going through the host.

// Dump /proc/self/maps to the console
console.log(new File('/proc/self/maps', 'r').readText());
// ── Open ──────────────────────────────────────────────────────────────────
const f = new File('/path/to/file', 'r');  // mode: 'r' 'w' 'rb' 'wb' 'ab' etc.

// ── Read ──────────────────────────────────────────────────────────────────
f.read(n)        // → ArrayBuffer (up to n bytes)
f.readText()     // → string (entire file)
f.readLine()     // → string (one line, including \n)

// ── Write ─────────────────────────────────────────────────────────────────
f.write(data)    // data: string | ArrayBuffer
f.flush()        // flush OS write buffer

// ── Seek / tell ───────────────────────────────────────────────────────────
f.seek(offset, whence)   // whence: 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END
f.tell()                 // → current offset (number)

f.close()

// ── Common pattern: dump a memory region to disk ──────────────────────────
const out = new File('/tmp/memdump.bin', 'wb');
out.write(ptr('0x100000000').readByteArray(0x1000));
out.flush();
out.close();

18. Hexdump & Utilities

Pretty-prints memory as a hex + ASCII dump, and provides Int64/UInt64 types for safely handling 64-bit values beyond JavaScript's safe integer range.

// Dump 64 bytes from the main module base
console.log(hexdump(Process.mainModule.base, { length: 64, header: true, ansi: true }));
// ── hexdump ───────────────────────────────────────────────────────────────
hexdump(target, options)
// target:  NativePointer | ArrayBuffer
// options: { offset?: number, length?: number, header?: boolean, ansi?: boolean }
// Returns a formatted string — pass directly to console.log()

// ── Int64 / UInt64 ────────────────────────────────────────────────────────
// Use when values exceed Number.MAX_SAFE_INTEGER (2^53 − 1)
const big = new Int64('0xDEADBEEFCAFEBABE');
const u   = new UInt64('18446744073709551615');

big.add(1)  big.sub(1)  big.and(mask)
big.or(v)   big.xor(v)  big.shl(n)  big.shr(n)  big.not()
big.compare(other)       // -1 | 0 | 1
big.toNumber()  big.toString()  big.toString(16)

// ── ArrayBuffer helpers ───────────────────────────────────────────────────
const ab  = somePtr.readByteArray(16);   // NativePointer → ArrayBuffer
const buf = Memory.alloc(16);
buf.writeByteArray(ab);                  // copy ArrayBuffer back to native memory

19. Script IPC (send / recv)

Provides bidirectional messaging between the in-process JS agent and the Python/Node.js host, and an rpc.exports object for synchronous remote procedure calls.

// Agent reports every hook hit; host can query modules via RPC
Interceptor.attach(targetFn, {
  onEnter(args) { send({ type: 'hit', arg: args[0].readCString() }); }
});
rpc.exports = {
  getModules() {
    return Process.enumerateModules().map(m => ({ name: m.name, base: m.base.toString() }));
  }
};
// ── Agent → Host ──────────────────────────────────────────────────────────
send({ type: 'log', msg: 'hello' })                // message only
send({ type: 'dump' }, somePtr.readByteArray(64))  // message + binary payload

// ── Host → Agent ──────────────────────────────────────────────────────────
recv('command', (msg) => {
  console.log('got command:', msg.payload.action);
  recv('command', arguments.callee);   // re-arm for next message
});

// ── RPC exports (synchronous call from host) ──────────────────────────────
rpc.exports = {
  dumpMemory(addrHex, size) {
    return ptr(addrHex).readByteArray(size);   // returned as bytes to Python
  },
  patchAddress(addrHex, value) {
    ptr(addrHex).writeU32(value);
  }
};
# Python host side
import frida, sys

def on_message(msg, data):
    if msg['type'] == 'send':
        print('[agent]', msg['payload'])

device  = frida.get_usb_device()
session = device.attach('com.example.game')
script  = session.create_script(open('agent.js').read())
script.on('message', on_message)
script.load()

modules = script.exports.get_modules()                  # RPC call
dump    = script.exports.dump_memory('0x12345678', 256) # → bytes
script.post({'type': 'command', 'payload': {'action': 'start'}})
sys.stdin.read()

20. Frida.Compiler

Compiles TypeScript (or modern JavaScript with ESM imports) into a self-contained bundle loadable as a Frida agent, replacing any manual Node.js build step.

# Compile once
frida-compile agent/index.ts -o bundle.js

# Watch mode — recompile on save
frida-compile agent/index.ts -o bundle.js -w
# Compile and inject programmatically from Python
import frida, asyncio

async def main():
    compiler = frida.Compiler()
    bundle   = await compiler.build('agent/index.ts', compression='terser')
    session  = await frida.get_usb_device_async().attach_async('com.example.App')
    script   = await session.create_script_async(bundle)
    await script.load_async()

asyncio.run(main())
// agent/index.ts — minimal TypeScript agent structure
import "frida-objc-bridge";   // provides ObjC global
import "frida-java-bridge";   // provides Java global

const openAddr: NativePointer = Module.getGlobalExportByName('open');
Interceptor.attach(openAddr, {
  onEnter(args: InvocationArguments) {
    console.log('[open]', args[0].readCString());
  }
});

21. Arm64Writer / X86Writer / ThumbWriter

Emit machine instructions into a writable code buffer — used inside Memory.patchCode() to build patches, trampolines, or inline stubs without hand-encoding raw bytes.

// NOP out 4 ARM64 instructions at a target address
Memory.patchCode(targetAddr, 16, (code) => {
  const w = new Arm64Writer(code, { pc: targetAddr });
  w.putNop(); w.putNop(); w.putNop(); w.putNop();
  w.flush();
});
// ── Arm64Writer (iOS ARM64, Android ARM64) ────────────────────────────────
Memory.patchCode(targetAddr, 64, (code) => {
  const w = new Arm64Writer(code, { pc: targetAddr });

  w.putNop()                            // NOP
  w.putBImm(destAddr)                   // B <addr>  (unconditional branch)
  w.putBCondImm('eq', destAddr)         // B.EQ <addr>
  w.putBlImm(funcAddr)                  // BL <addr>  (branch with link / call)
  w.putLdrRegAddress('x0', dataAddr)    // LDR x0, =<addr>
  w.putMovRegReg('x1', 'x0')            // MOV x1, x0
  w.putRetReg('x30')                    // RET  (BR x30)
  w.putInstruction(0xD503201F)          // emit raw 32-bit encoding

  // Local labels for loops / conditional branches
  w.putLabel('loop_top')
  w.putBCondLabel('ne', 'loop_top')

  w.flush()   // MUST call flush() to finalise the patch
});

// ── X86Writer (Android x86 / x86_64) ─────────────────────────────────────
Memory.patchCode(targetAddr, 32, (code) => {
  const w = new X86Writer(code, { pc: targetAddr });
  w.putNop()
  w.putRet()
  w.putJmpAddress(destAddr)    // JMP <addr>
  w.putCallAddress(funcAddr)   // CALL <addr>
  w.flush()
});

// ── ThumbWriter (Android ARMv7 Thumb) ────────────────────────────────────
Memory.patchCode(targetAddr, 8, (code) => {
  const w = new ThumbWriter(code, { pc: targetAddr });
  w.putNop()
  w.putBImm(destAddr)
  w.flush()
});

22. Platform Tips: iOS ARM64

Practical patterns for working with iOS binaries: ASLR slide calculation, converting IDA Pro offsets to runtime addresses, Swift symbol demangling, and ObjC class discovery.

// Convert an IDA Pro virtual address to its runtime address (ASLR aware)
const runtimeAddr = Process.mainModule.base.add(ptr('0x1000403F0').sub(ptr('0x100000000')));
Interceptor.attach(runtimeAddr, { onEnter() { console.log('hit'); } });
// ── ASLR slide ────────────────────────────────────────────────────────────
const app   = Process.mainModule;
const slide = app.base.sub(ptr('0x100000000'));
console.log('App:', app.name, '@ base', app.base, '(slide', slide, ')');

// ── IDA offset → runtime address helper ───────────────────────────────────
const IDA_BASE = ptr('0x100000000');
const fromIDA  = (va) => app.base.add(ptr(va).sub(IDA_BASE));
Interceptor.attach(fromIDA('0x100123456'), { onEnter() { console.log('hit'); } });

// ── ObjC class discovery ──────────────────────────────────────────────────
Object.keys(ObjC.classes)
  .filter(n => n.startsWith('Game'))
  .forEach(n => console.log(n, '→', ObjC.classes[n].$ownMethods.slice(0, 5)));

// ── Swift symbol demangling ───────────────────────────────────────────────
const swiftCore   = Process.getModuleByName('libswiftCore.dylib');
const demangleFn  = swiftCore.findExportByName('swift_demangle_getDemangledName')
  ?? swiftCore.findExportByName('swift_demangle');
if (demangleFn) {
  const demangle = new NativeFunction(demangleFn, 'pointer',
    ['pointer', 'pointer', 'pointer', 'pointer', 'uint32']);
  const sym    = Memory.allocUtf8String('_$s9TargetApp14GameControllerC10checkCheatyyF');
  const outBuf = Memory.alloc(256);
  const result = demangle(sym, NULL, outBuf, ptr(256), 0);
  if (!result.isNull()) console.log('demangled:', result.readCString());
}

// ── Hook Swift method via mangled symbol ──────────────────────────────────
const swiftFn = app.findSymbolByName('_$s9TargetApp14GameControllerC10checkCheatyyF');
if (swiftFn) {
  Interceptor.attach(swiftFn, { onLeave(retval) { retval.replace(ptr(0)); } });
}

// ── Code signature note ────────────────────────────────────────────────────
// On jailbroken devices memory is already writable — no extra steps needed

23. Platform Tips: Android

Practical patterns for Android: waiting for native library load via dlopen, intercepting RegisterNatives to discover JNI bindings, and accessing ART internals.

// Hook a native export as soon as its library loads
function whenLoaded(libName, cb) {
  if (Process.findModuleByName(libName)) { cb(Process.getModuleByName(libName)); return; }
  const dlopen = Module.getGlobalExportByName('android_dlopen_ext')
    ?? Module.getGlobalExportByName('dlopen');
  let done = false;
  Interceptor.attach(dlopen, {
    onLeave() {
      if (!done && Process.findModuleByName(libName)) {
        done = true; cb(Process.getModuleByName(libName));
      }
    }
  });
}
whenLoaded('libgame.so', mod => {
  const check = mod.findExportByName('checkLicense');
  if (check) Interceptor.attach(check, { onLeave(r) { r.replace(ptr(1)); } });
});
// ── Read /proc/self/maps ──────────────────────────────────────────────────
console.log(new File('/proc/self/maps', 'r').readText());

// ── Intercept JNI RegisterNatives (discover dynamic JNI bindings) ─────────
const libart  = Process.getModuleByName('libart.so');
const regNat  = libart.findExportByName(
  '_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi');
if (regNat) {
  Interceptor.attach(regNat, {
    onEnter(args) {
      const count = args[3].toInt32();
      for (let i = 0; i < count; i++) {
        const entry = args[2].add(i * 3 * Process.pointerSize);
        const name  = entry.readPointer().readCString();
        const sig   = entry.add(Process.pointerSize).readPointer().readCString();
        const fn    = entry.add(Process.pointerSize * 2).readPointer();
        console.log(`[RegisterNatives] ${name}${sig} → ${fn}`);
      }
    }
  });
}

// ── ART internals via JNI ─────────────────────────────────────────────────
Java.perform(() => {
  const env = Java.vm.getEnv();
  const cls = env.findClass('com/example/Target');
  const mid = env.getMethodID(cls, 'secretMethod', '()V');
  console.log('Method ID:', mid);

  // Hook the generic JNI trampoline (covers @FastNative / @CriticalNative)
  const tramp = Process.getModuleByName('libart.so')
    .findExportByName('artQuickGenericJniTrampoline');
  if (tramp) Interceptor.attach(tramp, { onEnter(args) { /* … */ } });
});

// ── ART SDK version ───────────────────────────────────────────────────────
Java.perform(() => {
  const VMRuntime = Java.use('dalvik.system.VMRuntime');
  console.log('Target SDK:', VMRuntime.getRuntime().targetSdkVersion());
});

24. Quick Reference Table

GoalFrida 17 API
Get module baseProcess.getModuleByName('lib.so').base
Get export addressProcess.getModuleByName('lib.so').getExportByName('fn')
Global export (any module)Module.getGlobalExportByName('open')
Hook function (observe)Interceptor.attach(addr, { onEnter, onLeave })
Replace functionInterceptor.replace(addr, new NativeCallback(…))
Fast replace (no overhead)Interceptor.replaceFast(addr, new NativeCallback(…))
Read memoryptr('0x…').readU32()
Write memoryptr('0x…').writeU32(val)
Chain writesptr('0x…').add(4).writeU32(x).add(4).writeU16(y)
Allocate bufferMemory.alloc(size)
Patch code (W^X safe)Memory.patchCode(addr, size, writerCb)
Scan memory for bytesMemory.scan(base, size, 'AA BB ?? DD', callbacks)
Enumerate modulesProcess.enumerateModules()
Enumerate memory rangesProcess.enumerateRanges('r-x')
Call native functionnew NativeFunction(ptr, retType, argTypes)(…)
Hook ObjC methodInterceptor.attach(ObjC.classes.Cls['- m'].implementation, …)
Hook Java methodJava.perform(() => Java.use('Cls').method.implementation = fn)
BacktraceThread.backtrace(this.context, Backtracer.ACCURATE)
Resolve symbolDebugSymbol.fromAddress(addr).toString()
Trace executionStalker.follow(tid, { events: { block: true }, onReceive })
Send to Python hostsend({ type: 'x', data: val })
Receive from Python hostrecv('type', callback)
Expose function as RPCrpc.exports = { fnName(args) { … } }
Inline C for hot hooksnew CModule('void f() { … }')
Emit ARM64 instructionsnew Arm64Writer(code, { pc: addr }).putNop().flush()
IDA offset → runtime addrapp.base.add(ptr(ida_va).sub(ptr('0x100000000')))

For ready-to-use script templates, see EXAMPLES.md.

Gespeicherte Skills