继续破解进度

This commit is contained in:
yml2213
2026-08-26 18:28:33 +08:00
parent da2cf5a61d
commit 36325cf639
11 changed files with 608 additions and 15 deletions
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""间接调用解析器 v3 (按需启用, 零常驻开销):
默认不挂任何桩 hook; copy_61098 轮次边界(x1=MAGIC)触发时动态 attach 72 个 br 桩,
3 秒后全部 detach。捕获 crypto 调用图谱定位 keystream 内核。
"""
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/got_resolve.json")
JS = r"""
'use strict';
Process.setExceptionHandler(function(d){ return true; });
send({type:'armed'});
var t0 = Date.now();
var MAGIC_HDR = [0x57,0x18,0x82,0xcf,0x66,0x4b,0xb3,0x94,0x01,0xee];
var modBase = null;
var counts = new Map();
var roundN = 0;
var stubHandles = []; // Interceptor handles to detach
var stubsActive = false;
var activeSince = 0;
function hexb(p, n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(function(b){return ('0'+b.toString(16)).slice(-2)}).join(''); }catch(e){ return ''; } }
function startsWith(p, arr){
try{ var b = new Uint8Array(p.readByteArray(arr.length)); for (var i=0;i<arr.length;i++) if (b[i]!==arr[i]) return false; return true; }catch(e){ return false; }
}
function hookBrSite(addr){
try{
var h = Interceptor.attach(addr, {
onEnter: function(a){
var ctx = this.context;
var tgt = ctx.x17;
if (tgt.isNull()) return;
var off = tgt.sub(modBase).toInt32();
counts.set(off, (counts.get(off) || 0) + 1);
}
});
return h;
}catch(e){ return null; }
}
function enableStubs(){
if (stubsActive) return;
stubsActive = true;
activeSince = Date.now();
stubHandles = [];
for (var i = 0; i < 24; i++){
var h = hookBrSite(modBase.add(0x461c0 + i*0x10 + 0xc));
if (h) stubHandles.push(h);
}
for (var i = 0; i < 48; i++){
var h = hookBrSite(modBase.add(0x45aa0 + i*0x10 + 0xc));
if (h) stubHandles.push(h);
}
send({type:'stubs_on', n: stubHandles.length, t: Date.now()-t0});
// 3s 后自动摘除
setTimeout(function(){
if (!stubsActive) return;
for (var j = 0; j < stubHandles.length; j++){
try{ stubHandles[j].detach(); }catch(e){}
}
stubHandles = [];
stubsActive = false;
send({type:'stubs_off', t: Date.now()-t0});
}, 3000);
}
function tryHookRound(){
try{
var m = Process.getModuleByName('libhydeviceid.so');
modBase = m.base;
Interceptor.attach(m.base.add(0x61098), {
onEnter: function(a){
var ctx = this.context;
if (!startsWith(ctx.x1, MAGIC_HDR)) return;
roundN++;
// dump round data BEFORE enabling stubs (crypto already done)
var arr = [];
counts.forEach(function(v, k){ if (v > 2) arr.push([k, v]); });
send({type:'round', n: roundN, t: Date.now()-t0, counts: arr, activeMs: Date.now()-activeSince});
counts = new Map();
send({type:'magic', t: Date.now()-t0, full: hexb(ctx.x1, 4300)});
// 延迟到回调外再挂桩(frida 回调内 attach 会失败)
setTimeout(function(){ counts = new Map(); enableStubs(); }, 15);
}
});
send({type:'round_hooked'});
}catch(e){ setTimeout(tryHookRound, 500); }
}
setTimeout(tryHookRound, 400);
try{
var r = new ApiResolver('module');
r.enumerateMatchesSync('exports:*!SSL_write').forEach(function(m){
Interceptor.attach(m.address, {
onEnter: function(a){
var len = a[2].toInt32();
if (len < 100 || len > 20000) return;
var head = '';
try{ head = a[1].readCString(Math.min(len, 2000)); }catch(e){ return; }
if (head.indexOf('dfpReport') < 0) return;
send({type:'wire', len:len, t: Date.now()-t0});
}
});
});
send({type:'ssl_hooked'});
}catch(e){ send({type:'ssl_err', e:String(e)}); }
"""
def load_with_retry(session, js, n=4):
last = None
for i in range(n):
try:
s = session.create_script(js)
s.load()
return s
except Exception as e:
last = e
print(f"[retry {i}] {e}", flush=True)
time.sleep(3)
raise last
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}", flush=True)
session = device.attach(pid)
events = []
def on_message(message, data):
if message.get("type") == "error":
print("[JS-ERR]", str(message)[:200], flush=True)
return
if message.get("type") != "send":
return
p = message.get("payload") or {}
t = p.get("type")
if t == "armed":
print("[*] JS armed", flush=True)
elif t == "round_hooked":
print("[*] round boundary hooked", flush=True)
elif t == "stubs_on":
print(f"[t={p.get('t')}ms] stubs ON x{p.get('n')}", flush=True)
elif t == "stubs_off":
print(f"[t={p.get('t')}ms] stubs OFF", flush=True)
elif t == "round":
c = dict(p.get('counts') or [])
top = sorted(c.items(), key=lambda x: -x[1])[:15]
print(f"[t={p.get('t')}ms round{p.get('n')}] (stubs active {p.get('activeMs')}ms) calls={sum(c.values())} distinct={len(c)}", flush=True)
for off, cnt in top:
print(f" off=0x{off:x} x{cnt}", flush=True)
events.append({"type": "round", **p})
OUT.write_text(json.dumps(events))
elif t == "magic":
events.append({"type": "magic", "t": p.get('t'), "full": p.get('full')})
OUT.write_text(json.dumps(events))
elif t == "wire":
print(f"[t={p.get('t')}ms] [wire] len={p.get('len')}", flush=True)
events.append({"type": "wire", **p})
OUT.write_text(json.dumps(events))
# 严格按约定顺序: spawn -> bypass x2 -> resume + sleep 11 -> patch_guard -> 最后主 hook JS
print("[*] loading bypass", flush=True)
load_with_retry(session, (RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text())
load_with_retry(session, (RE / "evidence/scripts/mask_frida_maps_only.js").read_text())
device.resume(pid)
time.sleep(11)
load_with_retry(session, (RE / "evidence/scripts/patch_guard_block_termination.js").read_text())
print("[*] loading 主 hook JS (最后)", flush=True)
script = load_with_retry(session, JS)
script.on("message", on_message)
print("[*] all loaded. waiting dfpReport", flush=True)
try:
while True:
time.sleep(5)
except KeyboardInterrupt:
pass
print(f"[*] done -> {OUT}", flush=True)
if __name__ == "__main__":
main()