docs(huya): 模拟器存活闪退诊断报告与 Frida 探测脚本证据

- 诊断报告: attach 主进程静默退出/EGL 崩溃, 仅约 4s 窗口可抓帧
- scripts: attach/spawn/hook/emu 系列 Frida 脚本与抓帧/验证工具
- evidence: identity/reqchain/frame/inputbuf/magic_buf/propedge 抓取样本,
  emu_* 存活对比, diag_* 策略实验, baseline 裸测基准
This commit is contained in:
yml2213
2026-08-27 17:58:32 +08:00
parent 36b5d78050
commit 49c5c36c05
100 changed files with 8666 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""存活/闪退四组对照诊断器 (统一度量):
指标1 proc_main: /proc/<pid>/cmdline == 包名 (主进程存活)
指标2 ui_focus: dumpsys window mCurrentFocus 是否 com.duowan.kiwi/* (UI 未闪退)
指标3 crash: logcat -b crash 是否出现 Fatal (native/java 崩溃)
A. 无frida 正常启动 (baseline)
B. spawn 挂起~0s 立即resume, 不加载任何脚本
C. spawn 挂起 加载2个bypass脚本后再resume (模拟v8/v9/stale)
D. attach 正常启动7s后 attach 主进程 (裸attach, 无脚本)
用法: python3 diag_lifecycle.py [a|b|c|d]
"""
from pathlib import Path
import frida, time, subprocess, sys, json, re
REMOTE = "127.0.0.1:31878"
ADB = ["adb", "-s", "127.0.0.1:5555"]
PACKAGE = "com.duowan.kiwi"
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
OUT_DIR = Path("/Users/yml/codes/douyu_login_py/evidence/diag_lifecycle")
def sh(*a): return subprocess.run(a, capture_output=True, text=True)
def adb(*a): return sh(*ADB, *a)
def main_pid():
r = adb("shell", "pidof", PACKAGE)
for p in r.stdout.strip().split():
rr = adb("shell", "cat", f"/proc/{p}/cmdline")
if rr.stdout.strip("\x00").strip() == PACKAGE:
return int(p)
return None
def focus_ok():
r = adb("shell", "dumpsys", "window")
m = re.search(r"mCurrentFocus=Window\{([^}]+)\}", r.stdout)
if m:
f = m.group(1)
return PACKAGE in f, f
return False, "?"
def crash_count():
r = adb("shell", "logcat", "-d", "-b", "crash", "-t", "50")
return r.stdout.count("Fatal"), r.stdout[-2000:]
def snapshot(label):
p = main_pid()
ok, foc = focus_ok()
return {"label": label, "proc": p, "ui_ok": ok, "focus": foc}
def main():
which = (sys.argv[1] if len(sys.argv) > 1 else "a").lower()
OUT_DIR.mkdir(parents=True, exist_ok=True)
adb("shell", "logcat", "-c")
adb("shell", "am", "force-stop", PACKAGE)
time.sleep(1.5)
d = frida.get_device_manager().add_remote_device(REMOTE)
pid = None
t_start = time.time()
timeline = []
label = which.upper()
if which in ("a",):
print("[A] 无frida 正常启动", flush=True)
adb("shell", "monkey", "-p", PACKAGE, "-c", "android.intent.category.LAUNCHER", "1")
elif which in ("b",):
print("[B] spawn 挂起~0 立即 resume", flush=True)
pid = d.spawn([PACKAGE])
dt = time.time() - t_start
t_start = time.time()
d.resume(pid)
print(f" spawn->resume 挂起 {dt*1000:.0f}ms", flush=True)
elif which in ("c",):
print("[C] spawn 挂起+加载2个bypass再resume", flush=True)
pid = d.spawn([PACKAGE])
s = d.attach(pid)
for name in ("bypass_msaoaid_maps_art_callsite.js", "mask_frida_maps_only.js"):
t0 = time.time()
sc = s.create_script((RE / "evidence/scripts" / name).read_text())
sc.load()
print(f" loaded {name} +{time.time()-t0:.2f}s", flush=True)
dt = time.time() - t_start
t_start = time.time()
d.resume(pid)
print(f" spawn->resume 挂起 {dt*1000:.0f}ms", flush=True)
elif which in ("d",):
print("[D] 正常启动7s后 attach 主进程(裸)", flush=True)
adb("shell", "monkey", "-p", PACKAGE, "-c", "android.intent.category.LAUNCHER", "1")
time.sleep(7)
pid = main_pid()
print(f" pid={pid}, 开始attach", flush=True)
try:
s = d.attach(pid)
sc = s.create_script("console.log('bare attach ok');")
sc.load()
print(" bare attach loaded", flush=True)
except Exception as e:
print(f" attach err {e}", flush=True)
t_start = time.time()
else:
print(f"未知: {which}"); return
# 观察 60s, 每 2s 快照
t0 = time.time()
while time.time() - t0 < 25:
time.sleep(2)
s = snapshot(label)
s["elapsed"] = round(time.time() - t0, 1)
timeline.append(s)
if s["ui_ok"]:
print(f" +{s['elapsed']:5.1f}s proc={s['proc']} UI={s['focus']}", flush=True)
else:
print(f" +{s['elapsed']:5.1f}s proc={s['proc']} UI_FAIL focus={s['focus']}", flush=True)
if not s["proc"] and s["elapsed"] > 3:
break
crashes, tail = crash_count()
final = snapshot(label)
result = {"case": label, "timeline": timeline, "final": final,
"crashes_in_buffer": crashes, "crash_tail": tail[:1500]}
out = OUT_DIR / f"diag_{label.lower()}.json"
out.write_text(json.dumps(result, ensure_ascii=False, indent=1))
print(f"[*] 结果 -> {out} | 最终: proc={final['proc']} ui_ok={final['ui_ok']} crashes={crashes}", flush=True)
if __name__ == "__main__":
main()