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:
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""T4 终局验证: 全吞退出函数 replace成no-op, 读LR抓调用者, 观察App是否存活.
|
||||
|
||||
T3 已证 _exit(0)/_Exit(0) 被调且 backtrace 空 (内联/清栈调用)。
|
||||
T4: Interceptor.replace _exit/_Exit/exit/exit_group/abort 为 no-op (不真正退出)
|
||||
同时 attach libc 'syscall' 打印 SYS_exit_group(94)/SYS_tgkill(131)/SYS_kill(129) 调用
|
||||
并读 LR (return address) 找 msaoaidsec 偏移。
|
||||
若 App 存活 >10s → 挡住退出=绕过成功; 若仍死 → 是 syscall/tgkill 自杀。
|
||||
"""
|
||||
from pathlib import Path
|
||||
import frida, time, json, subprocess, sys
|
||||
|
||||
REMOTE = "127.0.0.1:31878"
|
||||
ADB = ["adb", "-s", "127.0.0.1:5555"]
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
OUT = Path("/tmp/t4_all_block.json")
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
const PACK = 'libmsaoaidsec.so';
|
||||
let nExit = 0, nSyscall = 0;
|
||||
|
||||
function where(addr) {
|
||||
try {
|
||||
const m = Process.findModuleByAddress(addr);
|
||||
if (m === null) return addr.toString();
|
||||
return m.name === PACK ? 'MS:' + addr.sub(m.base).toString() : m.name;
|
||||
} catch (_) { return '?'; }
|
||||
}
|
||||
|
||||
// 1) 全吞退出函数
|
||||
for (const name of ['_exit', '_Exit', 'exit', 'exit_group', 'abort']) {
|
||||
try {
|
||||
const addr = Module.findExportByName('libc.so', name);
|
||||
if (addr === null) { send({ skip: name }); continue; }
|
||||
Interceptor.replace(addr, new NativeCallback(function () {
|
||||
nExit++;
|
||||
send({ exit: name, n: nExit, lr: where(this.context.lr) });
|
||||
return; // no-op: 吞掉退出
|
||||
}, 'void', name === 'abort' ? [] : ['int']));
|
||||
send({ blocked: name });
|
||||
} catch (e) { send({ err: name, e: String(e) }); }
|
||||
}
|
||||
|
||||
// 2) 观察 syscall 的 exit_group/tgkill/kill
|
||||
try {
|
||||
const sc = Module.findExportByName('libc.so', 'syscall');
|
||||
Interceptor.attach(sc, {
|
||||
onEnter(args) {
|
||||
const nr = args[0].toInt32() & 0xffffffff;
|
||||
if ([94, 93, 131, 129].includes(nr)) {
|
||||
nSyscall++;
|
||||
send({ sc: nr, n: nSyscall, lr: where(this.context.lr), arg1: args[1].toString() });
|
||||
}
|
||||
}
|
||||
});
|
||||
send({ watch_syscall: true });
|
||||
} catch (e) { send({ err: 'syscall', e: String(e) }); }
|
||||
|
||||
// 3) 观察 tgkill/tkill/kill/raise
|
||||
for (const name of ['tgkill', 'tkill', 'kill', 'raise']) {
|
||||
try {
|
||||
const addr = Module.findExportByName('libc.so', name);
|
||||
if (addr) Interceptor.attach(addr, { onEnter(args) { send({ kill: name, a0: args[0].toString(), a1: args[1].toString(), lr: where(this.context.lr) }); } });
|
||||
} catch (_) {}
|
||||
}
|
||||
send({ ready: true });
|
||||
"""
|
||||
|
||||
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 main():
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
for rnd in (1, 2, 3):
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
adb("shell", "logcat", "-c")
|
||||
pid = d.spawn([PACKAGE])
|
||||
session = d.attach(pid)
|
||||
t0 = time.time()
|
||||
events = []
|
||||
def on(m, dta):
|
||||
if m.get('type') != 'send': return
|
||||
p = m.get('payload') or {}
|
||||
p['elapsed'] = round(time.time() - t0, 3)
|
||||
events.append(p)
|
||||
if 'exit' in p or 'sc' in p or 'kill' in p:
|
||||
line = f" [+{p['elapsed']:6.2f}s]"
|
||||
if 'exit' in p: line += f" 退出被吞 {p['exit']}(#{p['n']}) lr={p['lr']}"
|
||||
if 'sc' in p: line += f" syscall#{p['sc']} lr={p['lr']}"
|
||||
if 'kill' in p: line += f" kill {p['kill']}({p['a0']},{p['a1']}) lr={p['lr']}"
|
||||
print(line, flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on('message', on)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print(f"[T4.r{rnd}] pid={pid} resume +{(time.time()-t0)*1000:.0f}ms", flush=True)
|
||||
died = None
|
||||
while time.time() - t0 < 15:
|
||||
time.sleep(1)
|
||||
mp = main_pid()
|
||||
if mp is None or mp != pid:
|
||||
died = time.time() - t0
|
||||
break
|
||||
print(f"[T4.r{rnd}] {'死@+'+str(round(died,1))+'s' if died else '存活15s'} exit吞={sum(1 for e in events if 'exit' in e)} syscall命中={sum(1 for e in events if 'sc' in e)}", flush=True)
|
||||
try: session.detach()
|
||||
except Exception: pass
|
||||
try: d.kill(pid)
|
||||
except Exception: pass
|
||||
time.sleep(1)
|
||||
OUT.write_text(json.dumps(events, ensure_ascii=False, indent=1))
|
||||
print(f"[*] -> {OUT}", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user