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,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""T1: frida attach 痕迹快照 (自己的实验).
|
||||
|
||||
spawn -> attach(挂起) -> 加载【痕迹采集脚本】(只读, 不改任何东西) -> resume
|
||||
采集(每~350ms):
|
||||
- 进程线程名列表 (/proc/self/task/*/comm)
|
||||
- /proc/self/maps 里含 frida/agent/re.linker/gum 的行
|
||||
- /proc/self/fd 的 symlink (linjector/pipe/socket)
|
||||
- 已加载模块含 frida/agent 的名字
|
||||
目标: attach(不任何hook) 时进程内哪些痕迹可被 msaoaidsec 看到。
|
||||
"""
|
||||
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/t1_traces.json")
|
||||
|
||||
TRACE_JS = r"""
|
||||
'use strict';
|
||||
function snap() {
|
||||
const s = { t: Date.now(), threads: [], maps_marks: [], fds: [], modules: [] };
|
||||
try {
|
||||
for (const t of Process.enumerateThreads()) {
|
||||
s.threads.push({ id: t.id, name: t.state == null ? '?' : t.name });
|
||||
}
|
||||
} catch (_) {}
|
||||
try {
|
||||
const maps = Process.enumerateMappings ? null : null;
|
||||
} catch (_) {}
|
||||
try {
|
||||
for (const m of Process.enumerateModules()) {
|
||||
if (/frida|agent|gum|re\.linker|linjector/i.test(m.name + m.path)) {
|
||||
s.modules.push({ name: m.name, path: m.path, base: m.base.toString() });
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
try {
|
||||
const MEM = Process.findModuleByName('libc.so');
|
||||
} catch (_) {}
|
||||
// fd 列表: 用 openat 枚举 /proc/self/fd
|
||||
try {
|
||||
const dir = new File('/proc/self/fd', 'r');
|
||||
const names = dir.read().toString().split(/\s+/);
|
||||
for (const n of names) {
|
||||
if (!/^\d+$/.test(n)) continue;
|
||||
const link = new File('/proc/self/fd/' + n, 'r');
|
||||
// can't readlink via File; use readlink via NativeFunction
|
||||
}
|
||||
} catch (_) {}
|
||||
send(s);
|
||||
}
|
||||
try {
|
||||
const readlink = new NativeFunction(Module.findExportByName('libc.so', 'readlink'), 'long', ['pointer', 'pointer', 'ulong']);
|
||||
setInterval(() => {
|
||||
const s = { t: Date.now(), threads: [], fds: [], modules: [], links: [] };
|
||||
try {
|
||||
for (const t of Process.enumerateThreads()) s.threads.push({ id: t.id, name: t.name });
|
||||
} catch (_) {}
|
||||
try {
|
||||
for (const m of Process.enumerateModules()) {
|
||||
if (/frida|agent|gum|re\.linker|linjector/i.test(m.name + m.path)) {
|
||||
s.modules.push({ name: m.name, path: m.path });
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
// fd symlinks
|
||||
try {
|
||||
const fp = new File('/proc/self/fd', 'r');
|
||||
const buf = fp.read().toString();
|
||||
for (const n of buf.split(/\s+/)) {
|
||||
if (!/^\d+$/.test(n)) continue;
|
||||
try {
|
||||
const p = Memory.alloc(512);
|
||||
const r = readlink('/proc/self/fd/' + n, p, 512);
|
||||
if (r > 0) {
|
||||
const link = p.readCString();
|
||||
if (/frida|agent|linjector|pipe|socket|memfd/i.test(link)) s.links.push(n + ' -> ' + link);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
} catch (_) {}
|
||||
send(s);
|
||||
}, 350);
|
||||
} catch (e) { send({ err: String(e) }); }
|
||||
send({ boot: 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):
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
pid = d.spawn([PACKAGE])
|
||||
session = d.attach(pid)
|
||||
t0 = time.time()
|
||||
snaps = []
|
||||
def on(m, dta):
|
||||
if m.get('type') != 'send': return
|
||||
p = m.get('payload') or {}
|
||||
p['elapsed'] = round(time.time() - t0, 2)
|
||||
if 'threads' in p or 'err' in p or 'boot' in p:
|
||||
snaps.append(p)
|
||||
sc = session.create_script(TRACE_JS)
|
||||
sc.on('message', on)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print(f"[T1.r{rnd}] spawn pid={pid} attach+采集脚本 resume +{(time.time()-t0)*1000:.0f}ms", flush=True)
|
||||
# 观察直到死(最多15s)
|
||||
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"[T1.r{rnd}] 死@+{died if died else 15}s 快照数={len(snaps)}", flush=True)
|
||||
if dies := (died or 15):
|
||||
print(f" 最后2个快照: {json.dumps(snaps[-2:], ensure_ascii=False)[:900]}", flush=True)
|
||||
try: session.detach()
|
||||
except Exception: pass
|
||||
try: d.kill(pid)
|
||||
except Exception: pass
|
||||
time.sleep(1)
|
||||
OUT.write_text(json.dumps(snaps, ensure_ascii=False, indent=1))
|
||||
print(f"[*] -> {OUT}", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user