#!/usr/bin/env python3 """真机 E2-v4: 身份输入全捕获 — svc 位点 + libc FILE* + Java 侧候选 API. 背景: GUID 与 serialno/ANDROID_ID/ro.product.*/IMEI属性/ro.boot.cpuid/全清数据 均无关, 属性层完全不可触碰 -> 种子来自 (a)内联 svc 文件读(SoC serial 等) (b)JNI->Java (TelephonyManager/NetworkInterface/蓝牙) (c)TEE. libc 钩子看不见 (a)/(b). 本脚本三层齐抓: 1. svc 位点 (dump 定位的 6 个 svc #0): x8=系统调用号 + x0-x3 参数 + (read->内容) 2. libc fopen64/fopen/fread/fgets (兜底 FILE* 路径) 3. Java 侧: TelephonyManager.getImei/getDeviceId/getSubscriberId/SimSerial、NetworkInterface getHardwareAddress/getNetworkInterfaces、BluetoothAdapter.getAddress/getName、 Settings.Secure/Global.getString —— 带参数与返回值 窗口: init/get* 全部标注 fn; 6s 后触发一次 Java 重算(捕获 Java 侧). 用法: /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \ scripts/phone_identity_inputs.py [serial] [remote] 输出: evidence/diag_phone/identity_inputs.json """ from __future__ import annotations import json import subprocess import sys import time from pathlib import Path import frida SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f" REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878" PACKAGE = "com.duowan.kiwi" REPO = Path("/Users/yml/codes/douyu_login_py") OUT = REPO / "evidence" / "diag_phone" / "identity_inputs.json" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js" WINDOW_OFFS = { "init": 0x20E6E4, "getGUID": 0x20F8C8, "getCDID": 0x20EB08, "getSDID": 0x20EDE8, "getHDID": 0x20F0C8, "getMID": 0x20FBA8, } SVC_SITES = [0x30BEF4, 0x30BF04, 0x30BF14, 0x30BF24, 0x30BF34, 0x30BF58] SYSCALLS = { 29: "ioctl", 48: "faccessat", 56: "openat", 57: "close", 63: "read", 64: "write", 65: "readv", 66: "writev", 67: "pread64", 79: "fstat", 61: "lseek", 97: "futex", 99: "set_robust_list", 113: "clock_gettime", 178: "gettid", 222: "mmap", 226: "mprotect", 278: "getrandom", 291: "statx", 263: "faccessat2", } JS_TEMPLATE = r""" 'use strict'; var LIB = 'libhydeviceid.so'; var WINDOW = __WINDOW_JSON__; var SITES = __SITES_JSON__; var SYSCALL = __SYSCALL_JSON__; var libBase = null, libSize = 0; var inLibWindow = 0; var hooked = false; function inWindow(){ return inLibWindow > 0; } function inLibCall(inv){ try{ var ra = inv.returnAddress; if (!ra || ra.isNull()) return false; return (ra.compare(libBase) >= 0 && ra.compare(libBase.add(libSize)) < 0); }catch(e){ return false; } } function snap(inv){ var c = inv.context; return { x0: ptr(c.x0), x1: ptr(c.x1), x2: ptr(c.x2), x3: ptr(c.x3), x4: ptr(c.x4), x5: ptr(c.x5), x8: ptr(c.x8) }; } function hookAll(){ if(hooked) return; var md = Process.findModuleByName(LIB); if(!md){ return; } libBase = md.base; libSize = md.size; hooked = true; Object.keys(WINDOW).forEach(function(fn){ try{ Interceptor.attach(libBase.add(parseInt(WINDOW[fn], 16)), { onEnter: function(){ inLibWindow++; send({type:'w-open', fn:fn, tid:Process.getCurrentThreadId()}); }, onLeave: function(){ inLibWindow--; send({type:'w-close', fn:fn}); } }); }catch(e){ send({type:'w-err', fn:fn, e:String(e)}); } }); send({type:'windows-hooked'}); function addHook(name, t, onEnter, onLeave){ try{ if(!t) return; Interceptor.attach(t, { onEnter: function(){ if(!inWindow()) return; if(!inLibCall(this)) return; var s = snap(this); this._valid = true; try{ onEnter(this, s); }catch(e){ send({type:'hook-err', name:name, e:String(e).slice(0,120)}); } }, onLeave: function(){ if(!this._valid) return; try{ onLeave(this); }catch(e){ send({type:'hook-err', name:name, e:String(e).slice(0,120)}); } } }); }catch(e){ send({type:'attach-err', name:name, e:String(e).slice(0,120)}); } } // ---- 1) svc 位点: 全 syscall census (x8 在 onEnter 需读寄存器, 此处特化) SITES.forEach(function(site){ try{ Interceptor.attach(libBase.add(site), { onEnter: function(){ if(!inWindow()) return; var s = snap(this); this._nr = s.x8.toInt32(); this._s = s; this._valid = true; var name = SYSCALL[this._nr]||('nr'+this._nr); var extra = ''; if ((name==='faccessat'||name==='openat'||name==='faccessat2') ) { try{ extra = s.x1.readCString()||'?'; }catch(e){ extra='?'; } } send({type:'svc', site:site, nr:this._nr, name:name, p:extra, a0:s.x0.toString(), a1:s.x1.toString(), a2:s.x2.toString(), a3:s.x3.toString(), tid:Process.getCurrentThreadId()}); }, onLeave: function(){ if(!this._valid) return; var name = SYSCALL[this._nr]||('nr'+this._nr); var ret = 0; try{ ret = this.context.x0.toInt32(); }catch(e){} if ((name==='read'||name==='pread64') && ret>0){ var cnt = Math.min(ret,512); var hex=''; try{ hex = this._s.x1.readByteArray(cnt) ? Array.from(new Uint8Array(this._s.x1.readByteArray(cnt))).map(function(b){return ('0'+b.toString(16)).slice(-2);}).join('') : ''; }catch(e){} send({type:'svc-read', site:site, nr:this._nr, name:name, n:ret, hex:hex, tid:Process.getCurrentThreadId()}); } else if (name==='openat' && ret>=0){ var p=''; try{ p = this._s.x1.readCString()||'?'; }catch(e){ p='?'; } send({type:'svc-open', site:site, nr:this._nr, name:name, p:p, fd:ret, tid:Process.getCurrentThreadId()}); } else if (name==='ioctl'){ send({type:'svc-ioctl', site:site, fd:this._s.x0.toInt32(), req:this._s.x1.toInt32(), ret:ret, tid:Process.getCurrentThreadId()}); } } }); }catch(e){ send({type:'attach-err', name:'svc@'+site.toString(16), e:String(e)}); } }); send({type:'svc-hooked'}); // ---- 2) libc FILE* 兜底 addHook('libc:fopen64', Module.findExportByName('libc.so','fopen64'), function(th,s){ th._p = s.x0.readCString()||'?'; }, function(th){ send({type:'fopen', p:th._p, ret:th.returnValue.toString(), tid:Process.getCurrentThreadId()}); }); addHook('libc:fopen', Module.findExportByName('libc.so','fopen'), function(th,s){ th._p = s.x0.readCString()||'?'; }, function(th){ send({type:'fopen', p:th._p, ret:th.returnValue.toString(), tid:Process.getCurrentThreadId()}); }); addHook('libc:fread', Module.findExportByName('libc.so','fread'), function(th,s){ th._buf=s.x0; th._len=s.x2.toInt32(); }, function(th){ var n=th.returnValue.toInt32(); var hex=''; if(n>0){ try{ hex = th._buf.readByteArray(Math.min(n*1,512)) ? Array.from(new Uint8Array(th._buf.readByteArray(Math.min(n*1,512)))).map(function(b){return ('0'+b.toString(16)).slice(-2);}).join('') : ''; }catch(e){} } send({type:'fread-c', n:n, hex:hex, tid:Process.getCurrentThreadId()}); }); addHook('libc:__system_property_get', Module.findExportByName('libc.so','__system_property_get'), function(th,s){ th._key=s.x0.readCString()||''; th._vp=s.x1; }, function(th){ var v=''; try{ v=th._vp.readCString()||''; }catch(e){} send({type:'prop', k:th._key, v:v, tid:Process.getCurrentThreadId()}); }); send({type:'libc-hooked'}); } setInterval(function(){ try{ hookAll(); }catch(e){} }, 150); // ---- 3) Java 侧候选 API (启动后装) function hookJava(){ try{ Java.perform(function(){ function wrap(cls, m, showArgs){ try{ Java.use(cls)[m].overloads.forEach(function(ov){ ov.implementation = function(){ var r; try{ r = ov.apply(this, arguments); }catch(e){ throw e; } var args = []; try{ if(showArgs){ for(var i=0;i {p.get('r')}", flush=True) elif t == "val": result["values"][p["m"]] = p["v"] print(f"[*] {p['m']} = {p['v']}", flush=True) elif t == "all-vals": result["values"].update(p["out"]) elif t in ("w-open","w-close","windows-hooked","svc-hooked","libc-hooked","java-hooked","cls-ok"): print(f"[*] {t}", flush=True) elif "err" in t: print(f"[!] {t}: {p.get('name','')} {p.get('e')}", flush=True) JS = (JS_TEMPLATE .replace("__WINDOW_JSON__", json.dumps({k: hex(v) for k, v in WINDOW_OFFS.items()})) .replace("__SITES_JSON__", json.dumps([hex(s) for s in SVC_SITES])) .replace("__SYSCALL_JSON__", json.dumps(SYSCALLS))) sc = session.create_script(JS) sc.on("message", on_message) sc.load() d.resume(pid) print("[*] resumed; 等待自然 init + 4.5s Java 钩子 + 6s 触发重算", flush=True) time.sleep(12) print("[*] 采集结束", flush=True) time.sleep(2) result["detached"] = det OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8") print(f"[*] saved {OUT} (events={len(result['events'])})", flush=True) if __name__ == "__main__": main()