继续破解进度
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user