- 诊断报告: attach 主进程静默退出/EGL 崩溃, 仅约 4s 窗口可抓帧 - scripts: attach/spawn/hook/emu 系列 Frida 脚本与抓帧/验证工具 - evidence: identity/reqchain/frame/inputbuf/magic_buf/propedge 抓取样本, emu_* 存活对比, diag_* 策略实验, baseline 裸测基准
118 lines
3.9 KiB
Python
118 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""T3: 抓 msaoaidsec 的真实退出途径 (raw syscall / tgkill / kill).
|
|
|
|
T2 proved libc _exit/exit/abort/pthread_exit NOT used (0 calls, app still dies).
|
|
This experiment hooks:
|
|
- libc 'syscall' (可变参数) → 若 msaoaidsec 用 syscall(exit_group)
|
|
- libc 'tgkill' / 'tkill' / 'kill' → 若自杀信号
|
|
- libc 'raise'
|
|
- libc '_exit' _Exit exit exit_group (再拦一次确认)
|
|
每次命中打印 backtrace (库内偏移)。不做任何返回改写 (只读观察) —— 纯归因。
|
|
"""
|
|
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/t3_exit_trace.json")
|
|
|
|
JS = r"""
|
|
'use strict';
|
|
const PACK = 'libmsaoaidsec.so';
|
|
function bt() {
|
|
const out = [];
|
|
try {
|
|
for (const a of Thread.backtrace(this.context, Backtracer.ACCURATE)) {
|
|
const m = Process.findModuleByAddress(a);
|
|
out.push(m ? (m.name === PACK ? 'MS:' + a.sub(m.base).toString() : m.name) : a.toString());
|
|
}
|
|
} catch (_) {}
|
|
return out.slice(0, 10);
|
|
}
|
|
function watch(name, nargs) {
|
|
try {
|
|
const addr = Module.findExportByName('libc.so', name);
|
|
if (addr === null) { send({ skip: name }); return; }
|
|
Interceptor.attach(addr, {
|
|
onEnter(args) {
|
|
const src = bt();
|
|
const ms = src.some(s => typeof s === 'string' && s.startsWith('MS:'));
|
|
const argv = [];
|
|
for (let i = 0; i < nargs; i++) { try { argv.push(args[i].toString()); } catch (_) { argv.push('?'); } }
|
|
send({ fn: name, ms, src, argv });
|
|
}
|
|
});
|
|
send({ hooked: name });
|
|
} catch (e) { send({ err: name, e: String(e) }); }
|
|
}
|
|
watch('syscall', 8);
|
|
watch('tgkill', 3);
|
|
watch('tkill', 2);
|
|
watch('kill', 2);
|
|
watch('raise', 1);
|
|
watch('_exit', 1);
|
|
watch('_Exit', 1);
|
|
watch('exit', 1);
|
|
watch('exit_group', 1);
|
|
watch('abort', 0);
|
|
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()
|
|
hits = []
|
|
def on(m, dta):
|
|
if m.get('type') != 'send': return
|
|
p = m.get('payload') or {}
|
|
p['elapsed'] = round(time.time() - t0, 3)
|
|
if 'fn' in p:
|
|
hits.append(p)
|
|
mark = 'MSAO!!' if p.get('ms') else ' '
|
|
print(f" [{mark} +{p['elapsed']:6.2f}s] {p['fn']} argv={p.get('argv')}", flush=True)
|
|
print(f" bt={p.get('src')}", flush=True)
|
|
elif 'hooked' in p:
|
|
pass
|
|
elif 'skip' in p:
|
|
print(f" [skip] {p['skip']}", flush=True)
|
|
sc = session.create_script(JS)
|
|
sc.on('message', on)
|
|
sc.load()
|
|
d.resume(pid)
|
|
print(f"[T3.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"[T3.r{rnd}] {'死@+'+str(round(died,1))+'s' if died else '存活'} 命中={len(hits)}", flush=True)
|
|
try: session.detach()
|
|
except Exception: pass
|
|
try: d.kill(pid)
|
|
except Exception: pass
|
|
time.sleep(1)
|
|
OUT.write_text(json.dumps(hits, ensure_ascii=False, indent=1))
|
|
print(f"[*] -> {OUT}", flush=True)
|
|
|
|
if __name__ == "__main__":
|
|
main() |