检测方的完整武器库
把商业 App/安全 SDK 的反 Frida 手段按层汇总,这是一份"对手视角"的清单。逆向时对照排查,比盲目试错高效得多。
应用层检测(Java/Kotlin)
1. 端口扫描
// 连接本机 27042 端口,通了就说明 frida-server 在跑
Socket socket = new Socket("127.0.0.1", 27042);绕过:换端口(-l 127.0.0.1:8888)。
2. 进程枚举
// 读 /proc 下所有 cmdline 找 frida-server
// 或 Runtime.exec("ps") 解析输出绕过:frida-server 改名。
3. maps 扫描
// 读 /proc/self/maps 找 frida-agent / frida-gadget 字样绕过:hook 文件读取过滤,或自定义编译改特征字符串。
4. 已安装包检测
// 查 com.saurik.substrate、frida 相关包
pm.getInstalledPackages()绕过:Xposed hook 包查询接口。
native 层检测(SO)
5. 线程名扫描
// 遍历 /proc/self/task/*/comm
// 找 "gum-js-loop", "pool-frida", "gmain" 等绕过:自定义编译 frida 改线程名,或 hook readdir/read 伪造。
6. 内存特征扫描
// 扫描自身地址空间找 frida agent 的特征字节
// (agent 的字符串常量、代码 pattern)绕过:自定义 gadget 编译,特征字符串全部自定义。
7. inline hook 检测
// 校验关键函数的开头字节是否被改成跳转指令
// (frida 的 Interceptor 就是 inline hook)
check_prologue(native_func);绕过:这是最难缠的检测。方案:换 hook 点(hook 检测函数没校验的函数)、用 Stalker(不修改函数开头)、或先 hook 校验函数让它永远报"未被 hook"。
8. 调试器/跟踪检测
// TracerPid、ptrace 自附加、断点指令扫描绕过:见"SO 反调试对抗"篇,TracerPid 伪造 + Interceptor.replace。
行为/时序检测
9. 执行时序异常
frida 的 JS 引擎执行会拖慢被 hook 函数——检测代码测量关键函数的耗时,异常变慢即报警。
绕过:hook 逻辑尽量轻(别在 onEnter 里做重活),日志异步发送;或用 CModule(编译型 hook)替代 JS。
10. 调用栈校验
检测代码检查调用栈里有没有 frida 的栈帧。
绕过:少见但存在,用 Stalker 或 CModule 规避 JS 栈帧。
绕过的优先级策略
面对一个未知 App,按这个顺序处理:
1. 改名 + 换端口(解决 1/2/3 的大部分)
↓ 还闪退?
2. hook 文件读取(maps/task/comm 过滤)(解决 3/5)
↓ 还闪退?
3. 逆向找检测函数,Interceptor.replace 反制(解决 7/8 及未知检测)
↓ 还闪退?
4. 换注入方式:gadget 嵌入 / CModule / Stalker(解决 6/9/10)精髓:三个认知
- 检测代码也是代码,也能被 hook。反 Frida 的本质是"检测函数 vs hook 检测函数"的套娃——谁在最底层谁赢。Frida 的 native hook 足够底层,理论上应用层和 SO 层的检测都能反制
- 静默检测比闪退危险。闪退型检测告诉你"被发现了",静默型检测(标记上报)让你以为成功了。绕过验证必须对比请求/响应差异
- 自定义编译是终极方案。改名换端口能过的都是弱检测;强检测(内存扫描、inline hook 检测)的终极解是自己编译 frida,把所有特征字符串换成随机值——一次编译,长期受用
总结
- 反 Frida 检测十项:端口、进程、maps、包名、线程名、内存、inline hook、TracerPid、时序、调用栈
- 绕过按优先级:改名换端口 → 文件读取过滤 → 反制检测函数 → 自定义编译
- 检测函数本身可被 hook,套娃游戏里 frida 在更底层
- 静默检测最危险,验证靠对比法
交流微信:run1255