第9章 问题排查
9.1 “Failed to spawn”
原因与解决方案
| 原因 | 解决方案 |
|---|---|
| SELinux 阻止 | adb shell "su -c setenforce 0" |
| 版本不匹配 | pip install frida==17.9.11 确保与 server 一致 |
| 32位应用+64位server | 同时推送 arm 和 arm64 版本 |
| Zygote 问题 (Android 12+) | 升级到 Frida 17.6.0+(轻量级 Zygote hook) |
| App spawn 保护 | 改用 attach 模式 |
| Android 14+ 新进程模型 | 17.9.10 修复了自定义进程的 spawn 匹配 |
| 仅 64 位设备 spawn 慢 | 17.9.0+ 自动跳过 32 位 helper |
# 验证版本匹配
frida --version # 客户端
adb shell "/data/local/tmp/frida-server --version" # 服务端
# 使用 attach 替代 spawn
frida -U --no-pause -p $(pidof com.target.app) -l script.js17.6.0+ Zygote Hook 改进
Frida 17.6.0 完全重写了 Android Zygote hook 机制:
- 不再注入代码到 Zygote 进程内部
- 改为通过
/proc/$pid/mem扫描 + 920 字节轻量 payload - 使用 abstract UNIX socket 通信
- 消除了线程停止/重启和文件描述符泄漏问题
如果遇到 spawn 问题,确保升级到 17.6.0+。
9.2 “Process crashed”
常见原因
1. Hook 时类未加载
// ❌ 错误: 类可能还未加载
Java.perform(function() {
var MyClass = Java.use('com.dex.DynamicClass'); // 可能崩溃
});
// ✅ 正确: 等待 ClassLoader
Java.perform(function() {
Java.enumerateClassLoaders({
onMatch(loader) {
try {
loader.loadClass("com.dex.DynamicClass");
Java.classFactory.loader = loader;
// 现在安全 hook
} catch(e) {}
},
onComplete() {}
});
});2. 重载选择错误
// 先列出所有重载
var methods = Java.use("com.target.Class").targetMethod.overloads;
methods.forEach(function(m) {
console.log(m.returnType.name + " " +
m.argumentTypes.map(t => t.name).join(', '));
});3. Native hook 参数类型错误
// 确保使用正确的 NativePointer 类型和大小
// ARM64: 指针 8 字节,int 4 字节4. 脚本异常导致进程崩溃
// 全局异常处理
Process.setExceptionHandler(function(details) {
console.log("异常: " + JSON.stringify(details));
return true; // 已处理,不崩溃
});5. NativeCallback 返回未初始化数据(17.9.9 修复)
// ❌ 旧版本中可能导致崩溃:NativeCallback 返回值包含未初始化栈数据
// ✅ 17.9.9+ 修复了 struct 返回和未初始化栈数据问题
var callback = new NativeCallback(function() {
return [0, 0, 0]; // 确保所有字段初始化
}, ['int', 'int', 'int'], ['pointer']);6. Transsion(传音)ROM 初始化崩溃(17.8.0 修复)
// 某些基于 Transsion 的 ROM 需要 Looper.prepareMainLooper()
// 17.8.0+ 已自动处理,旧版本可能在这些设备上崩溃
// 解决方案:升级到 17.8.0+9.3 “Unable to find application”
| 原因 | 解决方案 |
|---|---|
| 包名错误 | adb shell pm list packages | grep target |
| App 未运行 (attach 模式) | 改用 frida -U -f com.target.app spawn |
| 多用户/工作配置 | frida -U -f com.target.app --aux='uid=10' |
| frida-server 未运行 | adb shell "ps -e | grep frida" |
| USB 调试问题 | adb kill-server && adb start-server |
| 带作用域的包名 (17.9.10) | 确保 @ 符号正确转义 |
9.4 “PTRACE_ATTACH failed”
# 禁用 Magisk Hide / Zygisk DenyList
# 确保 frida-server 以 root 运行
adb shell "su -c /data/local/tmp/frida-server -D"
# 检查 ptrace_scope
adb shell "cat /proc/sys/kernel/yama/ptrace_scope"
# 0 = 允许, 1 = 仅父进程, 2 = 仅 admin, 3 = 禁止
# Android 14+: 检查是否有 seccomp 限制
adb shell "su -c cat /proc/$(pidof com.target.app)/status | grep Seccomp"9.5 “Script is destroyed”
目标进程退出了。解决方案:
- 使用 spawn 模式
frida -U -f - 添加
--no-pause参数 - 监听
session.on('detached')事件做重连
// Python 客户端自动重连
import frida
import time
def on_detached(reason, crash):
print(f"[!] 断开: {reason}")
if reason == 'process-terminated':
time.sleep(1)
reattach()
def reattach():
device = frida.get_usb_device()
pid = device.spawn(['com.target.app'])
session = device.attach(pid)
session.on('detached', on_detached)
# ... 加载脚本
device.resume(pid)9.6 “Java.use: class not found”
类可能由非默认 ClassLoader 加载:
Java.perform(function() {
Java.enumerateClassLoaders({
onMatch(loader) {
try {
loader.loadClass("com.target.ObfuscatedClass");
Java.classFactory.loader = loader;
console.log('[*] 找到正确的 ClassLoader');
// 现在可以 Java.use
var MyClass = Java.use('com.target.ObfuscatedClass');
} catch(e) {}
},
onComplete() {}
});
});9.7 连接问题排查清单
# 1. 确认设备连接
adb devices
# 2. 确认 frida-server 运行
adb shell "ps -e | grep frida"
# 3. 确认端口正常(默认 27042)
adb shell "netstat -tlnp | grep 27042"
# 4. 确认版本一致(17.9.11)
frida --version
adb shell "/data/local/tmp/frida-server --version"
# 5. 测试基本连接
frida-ps -U
# 6. 查看 server 日志
adb shell "su -c 'cat /data/local/tmp/frida-server.log'"
# 7. 重启 server
adb shell "su -c 'pkill -f frida-server'"
adb shell "su -c '/data/local/tmp/frida-server -D &'"
# 8. 使用自定义端口排除端口冲突(17.9.0+)
adb shell "su -c '/data/local/tmp/frida-server -l 0.0.0.0:31337 -D &'"
frida -H 127.0.0.1:31337 -U -f com.target.app
# 9. 使用 abstract socket(避免 TCP 端口扫描检测)
adb shell "su -c '/data/local/tmp/frida-server -l localabstract:frida-sock -D &'"9.8 性能问题
症状: Hook 后应用明显卡顿
解决方案:
- 减少 hook 数量,只 hook 必要的函数
- 减少
console.log调用(序列化开销大),改用send() - 缓存 args[] 值到局部变量
- 使用
Java.performNow()替代Java.perform()(不创建新线程) - Stalker 中排除系统库:
Stalker.exclude(libc) - 使用 CModule 替代 JS 回调(性能提升 10-100 倍)
- 对于高频函数,考虑
NativeFunction的scheduling: 'exclusive'选项
症状: 脚本加载很慢
解决方案:
- 拆分大脚本为按需加载的模块
- 使用 QJS 运行时 (
--runtime=qjs) 替代 V8(内存更小) - 避免在全局作用域做大量枚举
- 使用
Module.findSymbolByName()替代全量枚举 + 过滤(17.9.11)
症状: Stalker 追踪导致极慢
解决方案:
- 排除不需要追踪的模块:
Stalker.exclude(module) - 调高
Stalker.trustThreshold(默认 1,可设为 2-3) - 使用
onCallSummary替代onReceive减少事件量 - 只在必要的代码段启用精细追踪
9.9 Android 14/15 特有问题
问题: SELinux 拒绝 frida-server 操作
Android 14+ 强化了 SELinux 策略:
# 查看拒绝日志
adb shell "su -c dmesg | grep avc | grep frida"
# 临时禁用(仅测试环境)
adb shell "su -c setenforce 0"
# 永久方案:使用 Magisk + 自定义 SEPolicy 模块问题: frida-server 无法 spawn non-debuggable 应用
# 17.8.0+ 支持通过 ProcessControlService spawn non-debuggable 应用
# 确保 frida-server 以 root 运行
# 如果仍然失败,可尝试 gadget 注入方案
objection patchapk --source target.apk --gadget-version 17.9.11问题: 匿名内存检测
# 17.8.2 修复了 setArgV0() 在匿名内存中被检测的问题
# 确保升级到 17.8.2+ 以避免此问题问题: App 使用 Play Integrity API 检测环境
// Play Integrity 是服务端验证,无法纯客户端绕过
// 推荐方案:
// 1. Magisk + Shamiko(隐藏 root)
// 2. 使用 LSPosed 的 Hide My Applist 模块
// 3. 在已解锁的测试设备上禁用 GMS9.10 版本升级注意事项(17.4.1 → 17.9.11)
关键变更
| 版本 | 重要变更 | 影响 |
|---|---|---|
| 17.5.0 | Swift 绑定重写为 async/await | Swift 客户端需更新 |
| 17.6.0 | Zygote hook 轻量化重写 | Android spawn 更稳定 |
| 17.6.0 | frida-java-bridge 从核心移除 | Java hook 不受影响(已内置) |
| 17.6.0 | Interceptor FORCE 标志 | 可 hook 极小函数 |
| 17.8.0 | eBPF 系统调用追踪 | 新增 frida-strace 工具 |
| 17.8.0 | frida-helper.dex 进程内执行 | 减少兼容性问题 |
| 17.9.0 | control-endpoint 选项 | 支持自定义端口/socket |
| 17.9.0 | eBPF spawn gating | Linux spawn 更可靠 |
| 17.9.8 | Memory.alloc 可选保护参数 | 精细内存控制 |
| 17.9.9 | NativeCallback struct 修复 | 结构体返回更安全 |
| 17.9.11 | Darwin PAC 增强 + arm32 relocator 修复 | 跨平台稳定性 |
升级步骤
# 1. 升级客户端
pip install frida==17.9.11 frida-tools==13.6.1
# 2. 下载对应版本 server
wget https://github.com/frida/frida/releases/download/17.9.11/frida-server-17.9.11-android-arm64.xz
unxz frida-server-17.9.11-android-arm64.xz
# 3. 部署到设备
adb push frida-server-17.9.11-android-arm64 /data/local/tmp/frida-server
adb shell "su -c 'chmod 755 /data/local/tmp/frida-server'"
# 4. 停止旧版本并启动新版本
adb shell "su -c 'pkill -f frida-server'"
adb shell "su -c '/data/local/tmp/frida-server -D &'"
# 5. 验证
frida --version # 应显示 17.9.11
frida-ps -U # 应正常列出进程9.11 最佳实践总结
| 实践 | 说明 |
|---|---|
| 版本严格匹配 | client/server/gadget 三者版本必须完全一致 (17.9.11) |
| 先注册再 load | script.on('message', ...) 在 script.load() 之前 |
| spawn 后 resume | 否则进程永远挂起 |
| 引用防 GC | Memory.alloc 的结果存到 this.xxx 或模块级变量 |
| 异常处理 | 总是注册 session.on('detached') |
| 重载确认 | hook 前先列出 .overloads 确认签名 |
| ClassLoader | 找不到类时枚举 ClassLoader |
| 最小 hook | 只 hook 必要的,完成后 detachAll() |
| CModule 优先 | 高频函数用 CModule 替代 JS 回调 |
| 符号直接查找 | 用 findSymbolByName 替代全量枚举过滤 |
| 自定义端口 | 生产环境使用非默认端口或 abstract socket |
| eBPF 追踪 | 系统调用分析优先用 frida-strace |