其他
需要编写 Frida hook 脚本进行运行时插桩时…
适用于 Android 等目标上生成 Frida 动态分析脚本,hook 函数、追踪调用、捕获参数/返回值、dump 内存及等待 native 模块加载。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
需要编写 Frida hook 脚本进行特定
解决思路
- 用 Frida 做 native export hooks、Java 或 ObjC method hooks、runtime tracing、参数或返回值捕获、内存 dumping、loader-aware native instrumentation。
- 现代 Frida CLI 不使用 --no-pause;spawn 的进程会在脚本加载后恢复。使用命令:frida -U -f com.example.app -l hook.js;frida -U com.example.app -l hook.js;frida -U -p 1234 -l hook.js。
- 模块与符号查找:用 Process.getModuleByName('libssl.so') 获取模块,读取 mod.name、mod.base、mod.size、mod.path;用 mod.getExportByName('SSL_read') 查导出;用 Process.enumerateModules() 枚举模块;用 mod.enumerateExports()、mod.enumerateImports() 枚举导入导出;用 Module.getExportByName(null, 'open') 查全局导出。
- Interceptor 用法:用 Interceptor.attach(ptr, { onEnter(args) { console.log('arg0:', args[0].toInt32()); console.log('arg1 str:', args[1].readUtf8String()); }, onLeave(retval) { console.log('ret:', retval.toInt32()); } }) 挂函数;用 Interceptor.replace(ptr, new NativeCallback(function (a0, a1) { console.log('replaced'); return 0; }, 'int', ['pointer', 'int'])) 替换实现。
- NativeFunction 与 NativeCallback:用 new NativeFunction(Module.getExportByName(null, 'open'), 'int', ['pointer', 'int']) 创建调用句柄;用 open(Memory.allocUtf8String('/etc/hosts'), 0) 调用;用 new NativeCallback(function (arg) { console.log('called with:', arg); return 0; }, 'int', ['int']) 创建回调。
- 内存操作:用 ptr(addr).readByteArray(size)、readUtf8String()、readU32()、readPointer() 读取;用 writeByteArray(bytes)、writeUtf8String('hello')、writeU32(0x41414141) 写入;用 Memory.alloc(256)、Memory.allocUtf8String('hello') 分配;用 Memory.scan(mod.base, mod.size, '48 89 5C 24 ?? 48 89 6C', { onMatch(address, size) { console.log('found at:', address); }, onComplete() {} }) 扫描。
- ObjC hook:先检查 if (ObjC.available);用 ObjC.classes.ClassName['- methodName:'] 取方法;用 Interceptor.attach(hook.implementation, { onEnter(args) { const selfObj = new ObjC.Object(args[0]); const param = new ObjC.Object(args[2]); console.log(selfObj.toString()); console.log(param.toString()); } }) 挂方法。
- Java hook:先检查 if (Java.available);在 Java.perform(function () { ... }) 内用 Java.use('android.app.Activity') 取类;替换 Activity.onCreate.implementation = function (bundle) { console.log('onCreate called'); return this.onCreate(bundle); }。
- 生成 Frida 脚本时始终使用现代 API,例如 Process.getModuleByName() 和 mod.getExportByName()。
- 生成 Frida 脚本时不要使用 --no-pause。
- 生成 Frida 脚本时优先 load-event-driven native hooking,而不是 polling。
- 生成 Frida 脚本时以可读形式打印指针和缓冲区。
- 生成 Frida 脚本时将 risky hooks 包在 try/catch 中。
- 生成 Frida 脚本时用 hexdump() 检查二进制。
- 处理 native 模块加载时机时不要假设目标 .so 已加载;首选顺序是:1)hook android_dlopen_ext 或 dlopen,并在目标库加载时安装 hook;2)用 Process.findModuleByName() 立即检查已加载模块;3)轮询仅作为 fallback。
- 默认使用 hookModuleLoad helper:用 Module.findGlobalExportByName('android_dlopen_ext') || Module.findGlobalExportByName('dlopen') 找 dlopen;找不到就 throw new Error('dlopen/android_dlopen_ext not found');用 const hooked = new Set() 去重;Interceptor.attach(dlopen, { onEnter(args) { this.path = args[0].isNull() ? null : args[0].readCString(); this.shouldHook = this.path && this.path.indexOf(moduleName) !== -1; }, onLeave(retval) { if (!this.shouldHook || retval.isNull()) { return; } const mod = Process.findModuleByName(moduleName); if (!mod) { return; } const key = mod.base.toString(); if (hooked.has(key)) { return; } hooked.add(key); callback(mod); } })。
- 使用 hookModuleLoad:hookModuleLoad('libtarget.so', function (mod) { const target = mod.getExportByName('target_export'); Interceptor.attach(target, { onEnter(args) { console.log('target_export called'); } }); })。
- 如果目标可能已加载,组合立即检查与加载 hook:function hookNowOrOnLoad(moduleName, callback) { const mod = Process.findModuleByName(moduleName); if (mod) { callback(mod); return; } hookModuleLoad(moduleName, callback); }。
- 轮询仅作 fallback:function hookWhenReady(moduleName, exportName, callbacks) { const mod = Process.findModuleByName(moduleName); if (mod) { Interceptor.attach(mod.getExportByName(exportName), callbacks); return; } const timer = setInterval(function () { const loaded = Process.findModuleByName(moduleName); if (!loaded) { return; } clearInterval(timer); Interceptor.attach(loaded.getExportByName(exportName), callbacks); }, 100); }。
- 模块加载注意事项:Android 上优先 android_dlopen_ext 再 dlopen;按 module base 去重,不只按 path;dlopen/android_dlopen_ext 的 onLeave 通常是构造已运行后安装 hook 的合适时机;如果 Java 驱动 native 加载,也考虑 System.loadLibrary、Runtime.loadLibrary0、dlsym 或 RegisterNatives。
- 不要盲目 hook init 系列函数:不要指示用户盲目 hook .init、.init_array、constructors 或 JNI_OnLoad;这些是脆弱点,许多库在此做一次性 setup,坏 hook 可能在真正目标逻辑运行前崩溃进程,早期 hook 可能改变时序并隐藏用户想研究的行为,constructor 代码常扩散到无关 helper。
- 在建议 init-stage hook 前:1)识别为什么需要早期 hook;2)识别确切模块和确切初始化例程;3)确认目标符号或行为是否只在正常导出可达前存在;4)如果后面的稳定 hook 同样可见,优先后面的稳定 hook。
- 早期 hook 的优先顺序:1)hook 模块加载后稳定导出函数;2)hook RegisterNatives、dlsym 或第一个真实业务函数;3)只有 native 注册或 anti-debug setup 发生在 JNI_OnLoad 时才 hook JNI_OnLoad;4)只有强证据表明关键逻辑在 constructors 或 .init_array 时才 hook 它们。
- 如果提议早期 init hook,说明:为什么正常 export hook 不足;应 hook 哪个确切函数或地址;预期什么 failure mode;如何验证 hook 没破坏初始化。
- 不要给坏建议:不要在没有理由时建议 Interceptor.attach(Module.findBaseAddress('libtarget.so').add(0x1234), ...);更好的建议是等待模块加载,通过 symbols、xrefs、strings 或 trace evidence 确认 constructor 目标,只在识别确切 initialization function 后 attach,并在使用 init-stage hook 前向用户解释风险。
- 优先 constructor dispatchers 而不是盲目 init hooks:如果怀疑 anti-debug 逻辑在 constructor chain 内,优先观察或 hook dispatcher,而不是盲目 attach 原始 .init_array 条目;有用高层目标包括 call_constructors、call_array、linker-side constructor walkers、app-side wrapper functions 迭代 constructor tables。
- constructor dispatcher 更安全的原因:一个 hook 可揭示完整 constructor 序列;可见哪个 constructor 在 termination 或 anti-debug setup 前立即运行;减少盲目 patch 无关初始化代码;先记录 constructor targets,之后只 patch 有问题者。
- 推荐 constructor dispatcher workflow:1)hook module load;2)识别进程是否在 constructor 执行期间终止;3)如果是,在可用时 hook call_constructors 或 call_array;4)记录每个 constructor target 被 dispatch;5)识别执行 anti-debug 检查的确切 constructor;6)patch 或 hook 该 constructor,或 patch 其中特定 anti-debug 分支。
- 仅在以下情况建议 call_constructors 或 call_array:目标平台或 linker build 暴露这些函数;symbols、traces 或 disassembly 表明它们实际被使用;用户需要查看 constructor-time anti-debug 行为的能力。
- 警告 constructor dispatcher 约束:这些函数是 loader 或 linker internals,名称可能因 Android 版本、vendor build 或平台而异;可能未导出,需要 symbol recovery 或 offset-based attachment;过早 hook 可能影响每个 library load,因此仔细 scope logging;先用于 discovery,不作为默认永久 bypass。
- constructor dispatcher 好指导:如果进程在 native library initialization 期间死亡,先 hook constructor dispatcher 如 call_constructors 或 call_array(如果存在),记录 constructor targets,然后转向确切 offending constructor;坏指导:hook 每个 .init_array entry 并 patch 直到不再崩溃。
- 指针和缓冲区日志:console.log(args[0]); console.log(args[0].toString()); console.log(hexdump(args[0], { offset: 0, length: 64, header: true, ansi: false }))。
适用边界 · 注意事项
- 不要在没有明确理由时 hook .init、.init_array、constructors 或 JNI_OnLoad,也不要盲目 attach 裸地址;这些做法容易导致进程崩溃、改变时序并隐藏用户想研究的行为。
- 当目标平台或 linker 未暴露 call_constructors/call_array,或没有 symbols、traces、disassembly 证据时,不要建议使用它们。
- 不要默认用轮询等待模块加载;应优先 hook android_dlopen_ext/dlopen 的 load-event-driven 方式,轮询仅作为无法 hook 时的 fallback。