5.0 KiB
5.0 KiB
Frida 绕过配方 — 虎牙 com.duowan.kiwi (13.4.22 arm64)
⚠️ 当前可用配方 = RE 仓库三脚本两阶段流程 (hook_final_capture.py 同款, 实测唯一稳定):
bypass_msaoaid_maps_art_callsite.js+mask_frida_maps_only.js挂起中加载 → resume → 11s →patch_guard_block_termination.js⚠️
tools/frida/bypass_all.js(合并版) 未通过验证 (R40): 三层健康检查显示卡加载页/延迟死亡, 最小化测试 A1-only 也不通, 与原版的实质差异未定位 (emit 死锁已修但仍有问题)。别用。scripts/bypass_healthcheck.py= 三层判定工具 (pid/界面轨迹/崩溃ANR+冻结探针), 可复用。
一、标准用法 (runner 模板)
import frida, time
d = frida.get_device_manager().add_remote_device("127.0.0.1:31878") # frida-server -l 0.0.0.0:31878
pid = d.spawn(["com.duowan.kiwi"])
s = d.attach(pid)
from bypass_loader import load_bypass # scripts/ 在 sys.path (同目录运行即可)
load_bypass(s) # ← 唯一的绕过步骤, 可传参
d.resume(pid)
# 之后挂业务钩子 (挂业务 JS 的时机不再敏感 — patch_guard 已内置延迟)
sc = s.create_script(business_js)
sc.on("message", on_msg)
sc.load()
参数 (经 globalThis.BYPASS_OPTS 注入):
| 参数 | 默认 | 说明 |
|---|---|---|
patch_guard_delay_ms |
3000 | B 层 (终止拦截) 延迟安装毫秒数。App 启动期被杀 → 调大到 5000-11000; 启动卡死/合法退出被吞 → 调小到 1500 |
persist |
false | true 时写 /data/user/0/<pkg>/cache/bypass-all-<pid>.jsonl 调试日志 (每次掩盖/补丁事件) |
设备侧前提: root (magisk su) + frida-server -l 0.0.0.0:31878 (必须 0.0.0.0, 否则 connection closed) + adb forward tcp:31878 tcp:31878。
二、三层各自防什么
| 层 | 生效时机 | 对抗点 |
|---|---|---|
| A1 msaoaid 补丁 | 立即 (dlopen 双路触发) | libmsaoaidsec.so: 线程名探测 (gmain/gum-js-loop/linjector → pool-worker), fd 扫描 (linjector 掩写), frida 检出后的清理/退出分支 (0x1c4a0 → 直接跳继续路径), ART 完整性周期 callsite (0x1ceec NOP) |
| A2 maps 内容掩盖 | 立即 | libc open/open64/fopen/openat 记录 maps fd → read/pread64 时把 frida-agent/gum-js-loop/linjector 掩写成无害串 (比 A1 更底层的兜底) |
| B 终止拦截 | 延迟 (默认 3s) | kill/tgkill/exit/_exit/_Exit/abort/raise 对自身进程一概吞掉 (turing 检出后 _exit 静默退出的最后防线) + msaoaid crash-guard 0x20ca8 补丁 |
为什么 B 层要延迟: 终止拦截会无差别吞掉 App 自己的 exit/abort。启动初期 (0-2s) 有合法退出路径 (初始化失败重试等), 过早替换会卡死/异常。之前 11s 是保守值, 实测 3s 即可; 如果换 App 版本后启动期就被杀, 优先把 patch_guard_delay_ms 调回 11000 再排查。
三、实战教训清单 (每条都付出过时间)
- 绕过必须 spawn 挂起中加载, attach 已启动进程 = 6/6 死 (登录态下更敏感)。
- spawn 实例"安静退出"无 crash 日志 = msaoaid
_exit静默退, 说明绕过没生效或时序错 — 先检查是否 resume 前加载、frida-server 是否带-l 0.0.0.0。 - 别 hook GL/EGL 驱动里的同名导出 (libllvm-glnext/eglSubDriver 也有
deflate, 非 zlib ABI, 读错内存直接崩) — hook 系统库符号前先确认模块名。 - 别 hook 热路径 (libz
inflate= 图片解码): 首屏卡死、注册超时。要过滤: backtrace 含目标库才转储。 - OLLVM/无帧指针下
Backtracer.ACCURATE会给假帧 — 用Interceptor的this.returnAddress(真实立即调用方) 逐级爬, 或以必经外部导出 (如 libzdeflate) 为锚点反查。 - sret 约定:
md5(out&, in)的 x0 是输出槽, onEnter 读必空; 输入在 x1+。R34 曾因此误判"输入为空"。 - std::string 判读:
b0&1==0→ SSO (len=b0>>1, data=+1); 否则 len@+8, data@*(+16)。 - 业务 JS 里 Java MessageDigest 钩子要在 8 分钟窗口类 runner 里限量 (证书解析会刷几千条)。
四、已知边界
- offset 表 (0x1c4a0/0x1ceec/0x20ca8 等) 对应 13.4.22 arm64 的
libmsaoaidsec.so; App 升级需重新定位 (以绕过事件日志 + IDA 对照)。 - 模拟器上 A1 补丁因 EGL 竞争会崩 (原配方注释), 仅真机验证。
- 需要"清 turing 状态触发重注册"时:
rm -rf /data/data/com.duowan.kiwi/{app_turingdfp,app_turingfd}/*+ 删resinfo*(保留登录态, 别 pm-clear)。 - 设备被风控限流 (登录超时) 时静置即可恢复; 与 frida 绕过无关。
五、历史配方存档
- 三脚本两阶段原版: RE 仓库
evidence/scripts/{bypass_msaoaid_maps_art_callsite,mask_frida_maps_only,patch_guard_block_termination}.js, 流程见scripts/hook_final_capture.py(11s 稳定窗)。 bypass_msaoaid_maps_skip_cleanup.js: 旧单脚本, 匿名启动路径可用, 登录态启动路径不可靠 (R38 实测秒死) — 已被 bypass_all.js 取代, 留作对照。