attach 模式的致命短板
frida -U com.target.app(attach 模式)的问题是时机:进程已经跑起来了,启动期的代码——反调试初始化、密钥加载、设备指纹采集——全都执行完了。你 hook 上去时,该发生的都发生了,该检测的也检测完了。
spawn 模式(frida -f)让 Frida 创建进程并在 main 之前挂起,你的脚本先执行,App 的代码后运行——攻守之势逆转。
基本用法
# spawn 启动并挂起,加载脚本后手动放行
frida -U -f com.target.app -l script.js
# 进入 REPL 后输入 %resume 放行
# 一条命令自动放行(不等交互)
frida -U -f com.target.app -l script.js --no-pause脚本里的标准结构:
// 立即执行的 hook(此时 App 代码还没跑)
Interceptor.attach(Module.findExportByName('libc.so', 'open'), { ... });
Java.perform(function() {
// Java 层的 hook
});
// 如果用了 --no-pause 之外的交互模式,也可以脚本里自己放行
// setImmediate 确保 hook 全部挂好精髓一:Java.perform 在 spawn 早期的坑
spawn 模式下脚本执行时,ART(Android 运行时)可能还没初始化,Java.perform 直接报错或卡住。稳妥写法:
// 方案 A:等运行时就绪
function waitForArt() {
Java.perform(function() {
console.log('ART ready');
doJavaHooks();
});
}
// Frida 15+ 的 Java.perform 内部已处理等待,但老版本要手动延迟
setImmediate(waitForArt);
// 方案 B:native 层 hook 不依赖 ART,可以先挂
Interceptor.attach(...); // 立即执行,没问题精髓二:hook SO 加载点,抓"加载即初始化"
很多安全 SDK 在 SO 加载时(JNI_OnLoad、构造函数 .init_array)就做反调试初始化和密钥解密。spawn 模式下 hook 加载器,在 SO 加载完成的瞬间接管:
// hook android_dlopen_ext(Android 7+ 的 SO 加载入口)
var dlopen = Module.findExportByName(null, 'android_dlopen_ext');
Interceptor.attach(dlopen, {
onEnter: function(args) {
this.path = args[0].readCString();
},
onLeave: function(retval) {
if (this.path && this.path.indexOf('libtarget.so') >= 0) {
console.log('目标 SO 加载完成,开始 hook');
// 此刻 SO 已映射,但 JNI_OnLoad 可能刚执行或正在执行
hookTargetSo();
}
}
});要抢在 JNI_OnLoad 之前的话,hook 点换成 linker 里的 call_constructors,或者直接在 dlopen 的 onEnter 阶段就挂好对目标 SO 导出函数的 hook(函数还没被调用,hook 先生效)。
精髓三:对付"检测 frida 端口/进程名"的组合拳
spawn 模式本身逃不过 frida-server 的特征检测(默认端口 27042、进程名 frida-server)。完整方案:
# 1. frida-server 改名 + 换端口
adb push frida-server /data/local/tmp/fs64
adb shell "chmod 755 /data/local/tmp/fs64"
adb shell "/data/local/tmp/fs64 -l 0.0.0.0:9999 &"
# 2. 客户端连接指定端口
frida -H 127.0.0.1:9999 -f com.target.app -l script.js --no-pause
# (配合 adb forward tcp:9999 tcp:9999)再狠一点用 frida-inject 或直接编译自定义 gadget 嵌入 APK——检测面完全不同。
精髓四:spawn 失败的处理
spawn 模式偶尔会让 App 启动即崩(尤其是有进程互斥检测的 App)。排查顺序:
- 不加脚本裸 spawn(
frida -f com.target.app --no-pause),崩 = App 检测 spawn 本身 - 逐步注释脚本内容,定位引发崩溃的 hook 点
- 崩溃发生在 hook 的 onEnter 里打印日志时?可能是日志打印触发了递归 hook(比如 hook 了 open,而打印日志的底层也调 open)——hook 回调里避免触发被 hook 的 API
总结
- attach 看不到启动期,spawn 让脚本抢在 App 代码之前
- spawn 早期 ART 未就绪,native hook 先行,Java hook 用 setImmediate
- hook dlopen 系列函数,在 SO 加载瞬间接管初始化流程
- frida-server 改名换端口是配套动作,崩溃排查用二分注释法
交流微信:run1255