#!/usr/bin/env python3 """抓 hyCred 签发链: 挂 saveLoginData(落盘) + LoginCred handler(vtable探测)。 用法: 脚本跑起来后, 在手机上 手动退出登录 -> 重新登录 一次。 捕获: 登录态JSON明文(含cred字段) / saveLoginData 入参 / LoginCred handler 触发。 """ from __future__ import annotations import json import subprocess import time from pathlib import Path import frida RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0") JS = r"""var base = Process.getModuleByName('libudbauthunify.so').base; send({type:'armed'}); 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,400)); }catch(e){ return; } if(head.indexOf('wupudbrequest')<0 && head.indexOf('huyaudbwebui')<0) return; var arr=new Uint8Array(a[1].readByteArray(len)); var hex=''; for(var j=0;j>> 请在手机上退出登录并重新登录 <<<') sc = s.create_script(JS) events = [] def on_msg(m, _): if m.get('type') == 'error': print('JS ERR:', str(m)[:200]); return if m.get('type') != 'send': return p = m.get('payload') or {} t = p.get('type') events.append(p) if t == 'vt_attached': print(f"[vt] {p['name']} slot{p['slot']} @+{p['fp']}") elif t == 'saveLD_str': print(f"[saveLD_str] str={p['str']['t']}:{p['str']['len']}\n bean={p['bean_head']}\n bt={p['bt']}") elif t == 'saveLD_bean': print(f"[saveLD_bean] flag={p['flag']}\n bean={p['bean_head']}\n bt={p['bt']}") elif t == 'cred_unpack': print(f"[cred_unpack] in={p['in']['t']}:{p['in']['len']}:{p['in']['v'][:160]} <- {p['ra']}") elif t == 'bigaes': print(f"[bigaes] len={p['x2']['len']} head={p['x2']['v'][:120]}") elif t == 'vtable': print(f"[VT!] {p['name']}#{p['slot']} a1={p['a1']} bt={p['bt']}") sc.on('message', on_msg) sc.load() deadline = time.time() + 300 while time.time() < deadline: time.sleep(3) if any(e.get('type') == 'saveLD_bean' for e in events): time.sleep(10) break Path('/Users/yml/codes/douyu_login_py/evidence/sslwrite.json').write_text(json.dumps(events)) from collections import Counter print('saved:', Counter(e.get('type') for e in events)) if __name__ == '__main__': for attempt in range(4): try: main() break except frida.InvalidOperationError as e: print('detached:', e) subprocess.run(['adb', 'shell', 'am', 'force-stop', 'com.duowan.kiwi']) time.sleep(3)