#!/usr/bin/env python3 """真机: 主动 Java 调用 NativeEntry 的 getGUID/getMID/getCDID/getSDID/getHDID, 找 32hex hdid(field1.tag0). hypasswordLogin 登录帧 field1.tag0 的 32hex hdid 未在 SSL 帧/其他 get* 中抓到, 最可能是 getGUID()/getMID()(之前未触发) 的返回值。本脚本在 Java VM 就绪后 用 Java.use 主动调用全部 get 方法并打印, 确定 32hex hdid 的来源方法。 用法: /Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \ scripts/phone_java_call_nativeentry.py [serial] [remote] 输出: evidence/diag_phone/java_nativeentry.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" / "java_nativeentry.json" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js" JS = r""" 'use strict'; setTimeout(function(){ Java.perform(function(){ var out = {}; try{ var NE = Java.use('com.huya.security.hydeviceid.NativeEntry'); send({type:'class-ok', cls:'com.huya.security.hydeviceid.NativeEntry'}); ['init','getGUID','getMID','getCDID','getSDID','getHDID'].forEach(function(m){ try{ out[m] = NE[m](); }catch(e){ out[m] = ''; } send({type:'val', method:m, value:out[m]}); }); send({type:'all', out:out}); }catch(e){ send({type:'use-err', e:String(e).slice(0,200)}); } }); }, 6000); """ def adb(*a): return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True) def main(): OUT.parent.mkdir(parents=True, exist_ok=True) adb("shell", "am", "force-stop", PACKAGE) time.sleep(1.5) d = frida.get_device_manager().add_remote_device(REMOTE) pid = d.spawn([PACKAGE]) print(f"[*] spawn pid={pid}", flush=True) session = d.attach(pid) det = [] session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True)) session.create_script(ART_CALLSITE.read_text()).load() result = {"vals": {}, "detached": det} def on_message(m, data): if m.get("type") == "error": print("[JS-ERR]", str(m)[:200], flush=True); return p = m.get("payload") or {} t = p.get("type") if t == "val": result["vals"][p["method"]] = p["value"] print(f"[val] {p['method']} = {p['value']}", flush=True) elif t == "class-ok": print(f"[*] class found", flush=True) elif t == "all": print(f"[*] 全部读取完成", flush=True) elif t in ("use-err",): print(f"[!] use-err: {p.get('e')}", flush=True) sc = session.create_script(JS) sc.on("message", on_message) sc.load() d.resume(pid) print("[*] resumed", flush=True) time.sleep(20) result["detached"] = det OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8") print(f"[*] saved {OUT}") if __name__ == "__main__": main()