#!/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()