第5章 JavaScript API 参考

适用版本:Frida 17.9.11

5.1 Interceptor API

核心方法

方法说明
Interceptor.attach(target, callbacks[, data])Hook 目标地址的函数
Interceptor.replace(target, replacement[, data])完全替换函数实现
Interceptor.replaceFast(target, replacement)低开销替换,直接修改目标跳转 (17.5+)
Interceptor.revert(target)恢复原始函数
Interceptor.detachAll()移除所有 hook
Interceptor.flush()提交挂起的修改到内存

Interceptor.attach()

const listener = Interceptor.attach(targetAddress, {
    onEnter(args) {
        // args 是 NativePointer 数组
        console.log('参数0:', args[0].toInt32());
        // 存储状态供 onLeave 使用
        this.fd = args[0].toInt32();
    },
    onLeave(retval) {
        // retval 是 NativePointer,有 replace() 方法
        console.log('返回值:', retval.toInt32());
        // 修改返回值
        retval.replace(0);
    }
});
 
// 取消 hook
listener.detach();

性能提示:省略不需要的回调(如只需 onEnter 时不传 onLeave)可降低开销。基准开销约 6 微秒(仅 onEnter)或 11 微秒(双回调)。

FORCE 标志(17.6+):对于函数体过小无法容纳跳板的场景,可使用 Interceptor.attach(target, callbacks, { strategy: 'force' }) 强制内联 hook。

InvocationContext (this 属性)

属性说明
this.returnAddress返回地址 (NativePointer)
this.contextCPU 寄存器 (ARM64: pc, sp, x0-x28)
this.errno当前 errno (可读写,UNIX)
this.lastError当前 lastError (可读写,Windows)
this.threadId操作系统线程 ID
this.depth相对于其他 Interceptor 回调的调用深度

17.2+ 行为修正onLeave 仅在对应的 onEnter 完成后才会被调用,避免了 hook 中途附加时的不可预测行为。

Interceptor.replace()

const orig = new NativeFunction(targetAddr, 'int', ['pointer', 'int']);
 
Interceptor.replace(targetAddr, new NativeCallback(function(path, flags) {
    console.log('open() path:', path.readUtf8String());
    return orig(path, flags);  // 调用原始函数
}, 'int', ['pointer', 'int']));

Interceptor.replaceFast()(17.5+)

低开销替换,直接修改目标地址的跳转指令,无需完整的 trampoline 机制。

// replaceFast 返回指向原始实现的指针
const originalImpl = Interceptor.replaceFast(targetAddr,
    new NativeCallback(function(path, flags) {
        console.log('open() path:', path.readUtf8String());
        // 必须使用 replaceFast 返回的指针调用原始函数
        return origFunc(path, flags);
    }, 'int', ['pointer', 'int'])
);
 
// 使用返回的指针创建 NativeFunction
const origFunc = new NativeFunction(originalImpl, 'int', ['pointer', 'int']);

注意:与 replace() 不同,replaceFast() 返回原始实现指针,必须使用该指针调用原始函数。

使用 CModule 实现高性能 Hook

对于热路径函数,使用 C 实现回调可显著降低开销:

const cm = new CModule(`
#include <gum/guminvocationlistener.h>
#include <stdio.h>
 
void onEnter(GumInvocationContext * ic) {
    const char * path = gum_invocation_context_get_nth_argument(ic, 0);
    printf("open: %s\\n", path);
}
`);
 
Interceptor.attach(Module.getExportByName('libc.so', 'open'), cm.onEnter);

5.2 Memory API

内存分配

// 分配堆内存(JS 引用释放后自动回收)
const buf = Memory.alloc(256);
 
// 分配页对齐内存(大小为 pageSize 整数倍时使用 OS 页面管理)
const page = Memory.alloc(Process.pageSize);
 
// 17.5+ 带保护属性的内存分配(用于需要可执行内存的场景)
const execBuf = Memory.alloc(4096, { near: targetAddr, maxDistance: 0x7fffffff });
 
// 分配字符串
const strPtr = Memory.allocUtf8String('Hello, World!');
const wideStr = Memory.allocUtf16String('Wide string');

内存读取 (NativePointer 方法)

ptr.readPointer()        // 读取指针大小的值
ptr.readS32()            // 有符号 32 位整数
ptr.readU32()            // 无符号 32 位整数
ptr.readS64()            // 有符号 64 位 (返回 Int64)
ptr.readU64()            // 无符号 64 位 (返回 UInt64)
ptr.readFloat()          // 32 位浮点
ptr.readDouble()         // 64 位双精度
ptr.readByteArray(16)    // 返回 16 字节 ArrayBuffer
ptr.readUtf8String()     // 读取 null 结尾 UTF-8 字符串
ptr.readCString()        // 读取 C 字符串

内存写入

ptr.writeS32(42)
ptr.writeU32(0xdeadbeef)
ptr.writePointer(otherPtr)
ptr.writeByteArray([0x90, 0x90, 0x90, 0x90])
ptr.writeUtf8String('replaced')

内存操作

// 复制内存
Memory.copy(dst, src, numBytes);
 
// 复制内存区域(分配 + 复制)
const copied = Memory.dup(srcAddress, size);
 
// 修改内存保护
Memory.protect(addr, 4096, 'rwx');   // 读/写/执行
Memory.protect(addr, 4096, 'rw-');   // 读/写
Memory.protect(addr, 4096, 'r--');   // 只读
 
// 查询内存保护(17.5+)
const prot = Memory.queryProtection(addr);  // 返回如 'r-x'
 
// 安全修补代码(处理平台代码签名差异)
Memory.patchCode(addr, 16, function(code) {
    // code 是可写指针,可能不是 addr 本身(某些系统需要临时映射)
    code.writeByteArray([0xC3]);  // x86 RET
});

内存扫描

模式格式:十六进制字节,?? 为通配符,支持半字节级通配(如 ?3)和 r2 风格掩码

const module = Process.getModuleByName('libexample.so');
 
// 异步扫描
Memory.scan(module.base, module.size, '48 89 5c 24 ??', {
    onMatch(address, size) {
        console.log('找到模式:', address);
        // return 'stop' 停止扫描
    },
    onError(reason) {
        console.log('扫描错误:', reason);
    },
    onComplete() {
        console.log('扫描完成');
    }
});
 
// 同步扫描
const matches = Memory.scanSync(module.base, module.size, '48 89 5c 24 ??');
 
// 半字节级通配符(17.5+)
// ?3 匹配高半字节为任意值、低半字节为 3 的字节
Memory.scanSync(module.base, module.size, '?3 37 13 ?7');
 
// r2 风格掩码:模式:掩码(17.5+)
Memory.scanSync(module.base, module.size, '13 37 13 37 : 1f ff ff f1');

5.3 Process 和 Module API

Process 属性

Process.id                // PID
Process.arch              // 'ia32', 'x64', 'arm', 'arm64'
Process.platform          // 'windows', 'darwin', 'linux', 'freebsd', 'qnx', 'barebone'
Process.pageSize          // 4096
Process.pointerSize       // 4 或 8
Process.codeSigningPolicy // 'optional' 或 'required'
Process.mainModule        // 主可执行文件 Module 对象

Process 方法

// 模块发现
Process.enumerateModules()              // 返回 Module[] 数组
Process.findModuleByName('libc.so')     // Module 或 null
Process.getModuleByName('libc.so')      // Module 或抛异常
Process.findModuleByAddress(addr)       // Module 或 null
Process.getModuleByAddress(addr)        // Module 或抛异常
 
// 线程管理
Process.getCurrentThreadId()
Process.enumerateThreads()              // [{id, state, context}]
 
// 在指定线程执行代码(17.5+)
Process.runOnThread(threadId, function() {
    console.log('在目标线程上执行');
}).then(() => console.log('完成'));
 
// 线程生命周期观察(17.5+)
const observer = Process.attachThreadObserver({
    onAdded(thread) {
        console.log('新线程:', thread.id);
    },
    onRemoved(thread) {
        console.log('线程退出:', thread.id);
    },
    onRenamed(thread) {
        console.log('线程重命名:', thread.id);
    }
});
// observer.detach() 取消观察
 
// 模块加载观察(17.5+)
const modObserver = Process.attachModuleObserver({
    onAdded(module) {
        console.log('模块加载:', module.name);
    },
    onRemoved(module) {
        console.log('模块卸载:', module.name);
    }
});
 
// 内存区域
Process.enumerateRanges('r-x')          // 可执行页面
Process.enumerateMallocRanges()         // 堆分配
Process.findRangeByAddress(addr)        // 查询地址所在内存区域
Process.getRangeByAddress(addr)         // 同上,失败抛异常
 
// 目录方法
Process.getCurrentDir()                 // 当前工作目录
Process.getHomeDir()                    // 用户主目录
Process.getTmpDir()                     // 临时目录
 
// 调试检测
Process.isDebuggerAttached()            // Boolean
 
// 异常处理
Process.setExceptionHandler(function(details) {
    // details: {type, address, memory, context, nativeContext}
    console.log('异常:', JSON.stringify(details));
    return false;  // 不处理,让进程崩溃
});

Module 属性和方法

const mod = Process.getModuleByName('libexample.so');
mod.name     // 'libexample.so'
mod.base     // 基地址 NativePointer
mod.size     // 大小(字节)
mod.path     // '/system/lib64/libexample.so'
 
// 确保模块初始化器已运行(17.5+,早期注入安全)
mod.ensureInitialized();
 
// 导出/符号查找
Module.findExportByName('libc.so', 'open')   // NativePointer 或 null
Module.getExportByName('libc.so', 'open')    // NativePointer 或抛异常
Module.findExportByName(null, 'open')        // 搜索所有模块
 
// 符号查找
mod.findSymbolByName('_ZN3art...')           // NativePointer 或 null
mod.getSymbolByName('_ZN3art...')            // NativePointer 或抛异常
 
// 全局导出查找(开销较大)
Module.findGlobalExportByName('open')
Module.getGlobalExportByName('open')
 
// 枚举
mod.enumerateExports()       // [{type, name, address}]
mod.enumerateImports()       // [{type, name, module, address, slot}]
mod.enumerateSymbols()       // [{isGlobal, type, name, address, size}](Linux/macOS)
mod.enumerateSections()      // [{id, name, address, size}]
mod.enumerateDependencies()  // [{name, type}] type: regular/weak/reexport/upward
mod.enumerateRanges('r-x')  // 模块内存区域
 
// 动态加载模块
const loaded = Module.load('/path/to/library.so');

5.4 NativeFunction 和 NativeCallback

NativeFunction — 从 JS 调用 Native 代码

// new NativeFunction(address, returnType, argTypes[, options])
const openFunc = new NativeFunction(
    Module.getExportByName('libc.so', 'open'),
    'int',              // 返回类型
    ['pointer', 'int'], // 参数类型
    {
        scheduling: 'cooperative',  // 默认值,允许其他 JS 代码执行
        exceptions: 'steal',        // 默认值,Frida 接管异常
        traps: 'default'            // 默认值
    }
);
 
// 调用
const path = Memory.allocUtf8String('/proc/self/maps');
const fd = openFunc(path, 0);

选项参数(17.5+)

选项说明
scheduling'cooperative' (默认)允许其他 JS 代码在调用期间执行
scheduling'exclusive'独占线程,不允许切换
exceptions'steal' (默认)Frida 接管异常处理
exceptions'propagate'让异常正常传播
traps'default'默认行为
traps'all'触发所有陷阱(用于 Stalker)

支持的类型

void, pointer, int, uint, long, ulong, char, uchar, size_t, ssize_t, float, double, int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool

结构体/类类型使用数组表示:['int', 'float'] 表示含 int 和 float 成员的结构体。

NativeCallback — 用 JS 实现 Native 回调

const myCallback = new NativeCallback(function(arg0, arg1) {
    console.log('回调触发:', arg0, arg1);
    return 0;
}, 'int', ['pointer', 'int']);
 
// myCallback 是 NativePointer,可传给 Native 代码

17.9.8+ 结构体返回值修复NativeCallback 正确处理通过 libffi closure 路径返回结构体的情况。

SystemFunction — 同时捕获 errno

const open = new SystemFunction(
    Module.getExportByName('libc.so', 'open'),
    'int', ['pointer', 'int']
);
const result = open(Memory.allocUtf8String('/nonexistent'), 0);
console.log('value:', result.value, 'errno:', result.errno);

5.5 Stalker API(代码追踪)

基本使用

Stalker.follow(Process.getCurrentThreadId(), {
    events: {
        call: true,     // CALL 指令
        ret: false,     // RET 指令
        exec: false,    // 每条指令(非常详细!)
        block: false,   // 基本块执行
        compile: false  // 块编译事件
    },
 
    onReceive(events) {
        const parsed = Stalker.parse(events, {stringify: true, annotate: true});
        parsed.forEach(entry => console.log(JSON.stringify(entry)));
    },
 
    onCallSummary(summary) {
        // 聚合 target -> count 映射
        Object.keys(summary).forEach(target => {
            console.log(target + ': ' + summary[target] + ' calls');
        });
    }
});
 
// 5 秒后停止追踪
setTimeout(function() {
    Stalker.unfollow(Process.getCurrentThreadId());
    Stalker.garbageCollect();
}, 5000);

Transform 回调(高级)

Stalker.follow(threadId, {
    transform(iterator) {
        let instruction = iterator.next();
        do {
            if (instruction.mnemonic === 'call') {
                iterator.putCallout(function(context) {
                    console.log('即将调用:', context.pc);
                });
            }
            iterator.keep();
        } while ((instruction = iterator.next()) !== null);
    }
});

Stalker 辅助方法

// 排除模块(不追踪系统库,提升性能)
Stalker.exclude(Process.findModuleByName("libc.so"));
 
// 刷新缓冲事件
Stalker.flush();
 
// 使缓存的块翻译失效(17.5+,支持动态重插桩)
Stalker.invalidate(threadId, address);
 
// 轻量级 Call Probe
const probeId = Stalker.addCallProbe(targetAddress, function(args) {
    console.log('Probe 命中! arg0:', args[0]);
});
Stalker.removeCallProbe(probeId);
 
// 性能调优
Stalker.trustThreshold = 1;        // 缓存前执行次数(默认 1)
Stalker.queueCapacity = 16384;     // 事件缓冲大小(默认 16384)
Stalker.queueDrainInterval = 250;  // 事件刷新间隔 (ms)

Stalker + CModule(高性能追踪)

const cm = new CModule(`
#include <gum/gumstalker.h>
#include <stdio.h>
 
void transform(GumStalkerIterator * iterator,
               GumStalkerOutput * output,
               gpointer user_data) {
    cs_insn * insn;
    while (gum_stalker_iterator_next(iterator, &insn)) {
        if (insn->id == ARM64_INS_BL) {
            // 在 BL 指令前插入回调
        }
        gum_stalker_iterator_keep(iterator);
    }
}
`);
 
Stalker.follow(threadId, { transform: cm.transform });

17.9.7 修复:Stalker-x86 在 Windows 7 WoW64 下正确处理 AVX2 寄存器。


5.6 CModule API(17.0+)

从 C 源码直接编译到内存,用于实现高性能 hook:

const cm = new CModule(`
#include <gum/guminvocationlistener.h>
#include <stdio.h>
 
static int call_count = 0;
 
void onEnter(GumInvocationContext * ic) {
    call_count++;
    printf("call #%d\\n", call_count);
}
 
void init(void) {
    // 模块初始化时调用
}
 
void finalize(void) {
    // 模块卸载时调用
}
`, {
    // 可选:注入外部符号
    someBuffer: Memory.alloc(256)
});
 
// 全局函数自动导出为 NativePointer 属性
Interceptor.attach(target, cm.onEnter);
 
// 手动释放(否则等待 GC)
cm.dispose();

工具链选项

const cm = new CModule(code, symbols, {
    toolchain: 'internal'   // 'internal'(TinyCC) | 'external'(系统工具链) | 'any'(自动)
});

5.7 ApiResolver(模式匹配 API 查找)

// 模块导出查找
const resolver = new ApiResolver('module');
const matches = resolver.enumerateMatches('exports:*!open*');
matches.forEach(m => console.log(m.name, m.address));
 
// 模块导入查找
resolver.enumerateMatches('imports:*app*!*ssl*');
 
// 模块节区查找
resolver.enumerateMatches('sections:*!*text*');
 
// 大小写不敏感查找
resolver.enumerateMatches('exports:*!*SSL*/i');

5.8 hexdump 工具

// 格式化显示内存内容
console.log(hexdump(ptr, {
    offset: 0,
    length: 64,
    header: true,
    ansi: true
}));

5.9 DebugSymbol

// 地址 -> 符号名
const sym = DebugSymbol.fromAddress(ptr(0x7fff12345));
console.log(sym.name);        // 函数名
console.log(sym.moduleName);  // 模块名
console.log(sym.fileName);    // 源文件
console.log(sym.lineNumber);  // 行号
 
// 符号名 -> 地址
const addr = DebugSymbol.getFunctionByName('open');

5.10 MemoryAccessMonitor(页级内存访问监控)

// 监控指定内存区域的访问
MemoryAccessMonitor.enable([
    { base: addr, size: 4096 }
], {
    onAccess(details) {
        console.log('访问类型:', details.operation);  // 'read', 'write', 'execute'
        console.log('来自:', details.from);
        console.log('地址:', details.address);
    }
});
 
// 停止监控
MemoryAccessMonitor.disable();

5.11 版本变更摘要(17.4.1 → 17.9.11)

版本关键变更
17.5.0Memory.alloc() 支持 options 参数;Swift 绑定现代化
17.6.0Interceptor.attach 支持 FORCE 策略;arm64e deflector 签名修复
17.8.0系统调用追踪器(frida-strace);eBPF 后端(Linux)
17.9.2WebRequestHandler/WebRequest/WebResponse API
17.9.7Stalker-x86 AVX2 修复;Darwin 代码页调试映射
17.9.8NativeCallback 结构体返回值修复;Module#version 属性
17.9.11Darwin 加固改进;ARM32 relocator 条件分支修复