#!/usr/bin/env python3 """hook SSL_write, 抓 dfpReport 请求发出时的完整调用栈, 定位加密体组装函数。 dfpReport 是 TAF 请求(含 wupudbrequest/huyaudbwebui 特征), 发送时栈上 必然经过 libhydeviceid.so / libudbauthunify.so 的组装函数。 """ from __future__ import annotations import json import time from pathlib import Path import frida PACKAGE = "com.duowan.kiwi" RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") OUT = Path("/Users/yml/codes/douyu_login_py/evidence/dfp_wire_stack.json") JS = r""" 'use strict'; Process.setExceptionHandler(function(d){ return true; }); send({type:'armed'}); function hx(p,n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(x=>('0'+x.toString(16)).slice(-2)).join(''); } catch(e){ return 'ERR'; } } function ra(ctx, depth){ var out = []; try{ var bt = Thread.backtrace(ctx, Backtracer.ACCURATE); for (var i = 0; i < bt.length && i < 40; i++) { var name = ''; try{ name = DebugSymbol.fromAddress(bt[i]).toString().slice(0, 110); }catch(e){} if (name.indexOf('libudbauthunify.so') >= 0 || name.indexOf('libhydeviceid.so') >= 0 || name.indexOf('libhycrypto.so') >= 0 || name.indexOf('libtscsdk.so') >= 0) { out.push(name); } } }catch(e){} return out; } var hooked = 0; var fns = []; try{ fns = DebugSymbol.findFunctionsNamed('SSL_write'); }catch(e){} if (!fns.length) { try{ var r = new ApiResolver('module'); fns = r.enumerateMatchesSync('exports:*!SSL_write').map(function(x){ return x.address; }); }catch(e2){} } fns.slice(0, 6).forEach(function(p, i){ Interceptor.attach(p, { onEnter: function(a){ var len = a[2].toInt32(); if (len < 40 || len > 20000) return; var head = ''; try{ head = a[1].readCString(Math.min(len, 2000)); }catch(e){ return; } if (head.indexOf('wupudbrequest') < 0 && head.indexOf('huyaudbwebui') < 0 && head.indexOf('dfpReport') < 0 && head.indexOf('getDfpConfig') < 0) return; var arr = new Uint8Array(a[1].readByteArray(len)); var hex = ''; for (var j = 0; j < arr.length; j++) hex += ('0'+arr[j].toString(16)).slice(-2); send({type:'wire', idx:i, len:len, stack:ra(this.context, 40), hex:hex}); } }); hooked++; send({type:'hooked', idx:i, at:String(p)}); }); if (hooked === 0) send({type:'nowrite'}); """ def main(): device = frida.get_device_manager().add_remote_device("127.0.0.1:31877") pid = device.spawn([PACKAGE]) print(f"[*] spawned {PACKAGE} pid={pid}") session = device.attach(pid) session.create_script( (RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text() ).load() session.create_script( (RE / "evidence/scripts/mask_frida_maps_only.js").read_text() ).load() device.resume(pid) time.sleep(11) session.create_script( (RE / "evidence/scripts/patch_guard_block_termination.js").read_text() ).load() events = [] def on_message(message, data): if message.get("type") == "error": print("[JS-ERR]", str(message)[:200]); return if message.get("type") != "send": return p = message.get("payload") or {} t = p.get("type") if t == "wire": print(f"[wire] len={p.get('len')}") for s in (p.get("stack") or []): print(f" {s}") print(f" hex head: {p.get('hex','')[:64]}") events.append(p) elif t == "hooked": print(f"[+] SSL_write hooked idx={p.get('idx')} at={p.get('at')}") elif t == "nowrite": print("[-] SSL_write not found!") script = session.create_script(JS) script.on("message", on_message) script.load() print("[*] 请在手机上 退出登录 -> 重新登录 (150s)") deadline = time.time() + 150 try: while time.time() < deadline: time.sleep(5) except KeyboardInterrupt: pass OUT.write_text(json.dumps(events, ensure_ascii=False, indent=1)) print(f"[*] 捕获 {len(events)} 条 -> {OUT}") if __name__ == "__main__": main()