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