Files
live-hub-py/docs/Frida绕过配方.md

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/linjectorpool-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 再排查

三、实战教训清单 (每条都付出过时间)

  1. 绕过必须 spawn 挂起中加载, attach 已启动进程 = 6/6 死 (登录态下更敏感)。
  2. spawn 实例"安静退出"无 crash 日志 = msaoaid _exit 静默退, 说明绕过没生效或时序错 — 先检查是否 resume 前加载、frida-server 是否带 -l 0.0.0.0
  3. 别 hook GL/EGL 驱动里的同名导出 (libllvm-glnext/eglSubDriver 也有 deflate, 非 zlib ABI, 读错内存直接崩) — hook 系统库符号前先确认模块名。
  4. 别 hook 热路径 (libz inflate = 图片解码): 首屏卡死、注册超时。要过滤: backtrace 含目标库才转储。
  5. OLLVM/无帧指针下 Backtracer.ACCURATE 会给假帧 — 用 Interceptorthis.returnAddress (真实立即调用方) 逐级爬, 或以必经外部导出 (如 libz deflate) 为锚点反查。
  6. sret 约定: md5(out&, in) 的 x0 是输出槽, onEnter 读必空; 输入在 x1+。R34 曾因此误判"输入为空"。
  7. std::string 判读: b0&1==0 → SSO (len=b0>>1, data=+1); 否则 len@+8, data@*(+16)。
  8. 业务 JS 里 Java MessageDigest 钩子要在 8 分钟窗口类 runner 里限量 (证书解析会刷几千条)。

四、已知边界

  • offset 表 (0x1c4a0/0x1ceec/0x20ca8 等) 对应 13.4.22 arm64libmsaoaidsec.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 取代, 留作对照。