继续破解进度
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""铸造验证 v1: 重放 dfpReport wire → 服务端回显(不动设备)。
|
||||
|
||||
路线(XOR 自反性,不需要 keystream):
|
||||
密文 = 明文 XOR ks => 新密文 = 原密文 XOR (原JSON XOR 新JSON)
|
||||
JSON 区(前 554B)等长替换 hdid/deviceId/appkey,collection 区保持原样。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import binascii
|
||||
import json
|
||||
import random
|
||||
import ssl
|
||||
import socket
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
EVID = Path("/Users/yml/codes/douyu_login_py/evidence")
|
||||
HOST = "wsapi.huya.com"
|
||||
|
||||
MAGIC = bytes.fromhex("571882cf664bb39401ee")
|
||||
|
||||
|
||||
def load_first_wire():
|
||||
for f in sorted((EVID / "dfp_pipeline_170031.json", EVID / "dfp_pipeline_160855.json"),
|
||||
reverse=True):
|
||||
d = json.load(open(f))
|
||||
for e in d:
|
||||
if e.get("type") == "wire":
|
||||
return binascii.unhexlify(e["hex"])
|
||||
raise SystemExit("no wire found")
|
||||
|
||||
|
||||
def send_http(host, path, body, timeout=60):
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
raw = socket.create_connection((host, 443), timeout=10)
|
||||
s = ctx.wrap_socket(raw, server_hostname=host)
|
||||
req = (
|
||||
f"POST {path} HTTP/1.1\r\n"
|
||||
f"i-ver: 1\r\n"
|
||||
f"Content-Type: application/octet-stream\r\n"
|
||||
f"Content-Length: {len(body)}\r\n"
|
||||
f"Host: {HOST}\r\n"
|
||||
f"Connection: Keep-Alive\r\n"
|
||||
f"Accept-Encoding: identity\r\n"
|
||||
f"User-Agent: okhttp/3.14.9\r\n"
|
||||
f"\r\n"
|
||||
).encode() + body
|
||||
s.sendall(req)
|
||||
s.settimeout(timeout)
|
||||
buf = b""
|
||||
try:
|
||||
while True:
|
||||
chunk = s.recv(4096)
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
# 响应头拿到 + 至少等 2s 累积 body
|
||||
if b"\r\n\r\n" in buf and len(buf) > 300:
|
||||
time.sleep(2)
|
||||
try:
|
||||
while True:
|
||||
c = s.recv(4096)
|
||||
if not c:
|
||||
break
|
||||
buf += c
|
||||
except socket.timeout:
|
||||
break
|
||||
break
|
||||
finally:
|
||||
try:
|
||||
s.close()
|
||||
except Exception:
|
||||
pass
|
||||
return buf
|
||||
|
||||
|
||||
def gen_random_hex(n):
|
||||
return "".join(random.choice("0123456789abcdef") for _ in range(n))
|
||||
|
||||
|
||||
def main():
|
||||
wire = load_first_wire()
|
||||
idx = wire.find(b"\r\n\r\n")
|
||||
body = wire[idx + 4:]
|
||||
mp = body.find(MAGIC)
|
||||
assert mp >= 0, "magic not in body"
|
||||
cipher = body[mp + 10:]
|
||||
print(f"[*] wire={len(wire)}B body={len(body)}B cipher={len(cipher)}B")
|
||||
|
||||
# 明文 JSON (canonical 554B from pair_sync)
|
||||
ps = json.load(open(EVID / "dfp_pair_sync.json"))
|
||||
jsonb = None
|
||||
for p in ps:
|
||||
if p.get("type") != "plain":
|
||||
continue
|
||||
for item in p["plains"]:
|
||||
b = bytes.fromhex(item["hex"])
|
||||
if b[:8] == b'{"appId"' and len(b) >= 554:
|
||||
jsonb = b[:554]
|
||||
break
|
||||
if jsonb:
|
||||
break
|
||||
assert jsonb, "no json"
|
||||
orig = jsonb.decode("utf-8", "replace")
|
||||
|
||||
# --- 实验1: 原样重放 ---
|
||||
print("\n[实验1] 原样重放")
|
||||
r = send_http(HOST, "/", body)
|
||||
status = r.split(b"\r\n", 1)[0].decode("utf-8", "replace")
|
||||
print(f" resp: {status} len={len(r)}")
|
||||
hdrs, _, rbody = r.partition(b"\r\n\r\n")
|
||||
print(f" resp body head: {rbody[:80].hex(' ')}")
|
||||
|
||||
# --- 实验2: 改明文 hdid/deviceId/appkey (等长) → Δ XOR ---
|
||||
new_hdid = gen_random_hex(40)
|
||||
new_devid = gen_random_hex(40)
|
||||
new_appkey = gen_random_hex(32)
|
||||
new = orig
|
||||
new = new.replace(f'"hdid":"{orig.split(chr(34)+"hdid"+chr(34))[1].split(chr(34))[1]}"',
|
||||
f'"hdid":"{new_hdid}"')
|
||||
# 更稳妥: 正则替换
|
||||
import re
|
||||
new = re.sub(r'"hdid":"[0-9a-f]{40}"', f'"hdid":"{new_hdid}"', new)
|
||||
new = re.sub(r'"deviceId":"[0-9a-f]{40}"', f'"deviceId":"{new_devid}"', new)
|
||||
new = re.sub(r'"appkey":"[0-9a-f]{32}"', f'"appkey":"{new_appkey}"', new)
|
||||
assert new != orig, "no field replaced"
|
||||
nb = new.encode("utf-8")
|
||||
assert len(nb) == 554, f"len {len(nb)}"
|
||||
|
||||
delta = bytes(a ^ b for a, b in zip(nb, jsonb))
|
||||
new_cipher = bytes(a ^ b for a, b in zip(cipher, delta + b"\x00" * (len(cipher) - len(delta))))
|
||||
new_body = body[:mp + 10] + new_cipher
|
||||
# Content-Length 不变 (等长)
|
||||
print(f"\n[实验2] 改 hdid={new_hdid[:12]}... deviceId={new_devid[:12]}... appkey={new_appkey[:8]}...")
|
||||
r = send_http(HOST, "/", new_body)
|
||||
status = r.split(b"\r\n", 1)[0].decode("utf-8", "replace")
|
||||
print(f" resp: {status} len={len(r)}")
|
||||
hdrs, _, rbody = r.partition(b"\r\n\r\n")
|
||||
print(f" resp body head: {rbody[:160].hex(' ')}")
|
||||
|
||||
out = {
|
||||
"new_hdid": new_hdid, "new_devid": new_devid, "new_appkey": new_appkey,
|
||||
"new_json": new,
|
||||
"exp1_status": status,
|
||||
"exp2_status": status,
|
||||
"exp2_resp": rbody[:160].hex(),
|
||||
}
|
||||
(EVID / "forge_replay_result.json").write_text(json.dumps(out, ensure_ascii=False, indent=1))
|
||||
print(f"\n[*] saved -> evidence/forge_replay_result.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -19,7 +19,8 @@ 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_pipeline_v2.json")
|
||||
import time as _t
|
||||
OUT = Path("/Users/yml/codes/douyu_login_py/evidence/dfp_pipeline_" + _t.strftime("%H%M%S") + ".json")
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
@@ -63,7 +64,7 @@ function decodeArg(p, tag){
|
||||
if (startsWith(p, MAGIC_HDR)){
|
||||
res.kind = 'MAGIC';
|
||||
var pre = '';
|
||||
try{ pre = hexb(p.sub(1536), 1536); }catch(e){}
|
||||
try{ pre = hexb(p.sub(8192), 8192); }catch(e){}
|
||||
res.pre = pre;
|
||||
res.full = hexb(p, 9000);
|
||||
return res;
|
||||
@@ -109,6 +110,49 @@ function decodeArg(p, tag){
|
||||
}catch(e){ return null; }
|
||||
}
|
||||
|
||||
// deep decode: scan 1024B at p for plausible pointers, dump 9000B at each
|
||||
function deepDeref(p){
|
||||
var out = {reg: 'deep', addr: String(p)};
|
||||
var found = [];
|
||||
var seen = {};
|
||||
try{
|
||||
// also dump page-aligned base 12000B
|
||||
try{
|
||||
var pg = p.and(ptr('0xfffffffffffff000'));
|
||||
if (isReadable(pg)){
|
||||
found.push({kind: 'page', ptr: String(pg), full: hexb(pg, 12000)});
|
||||
seen[String(pg)] = true;
|
||||
}
|
||||
}catch(e){}
|
||||
var base = p.sub(512);
|
||||
for (var i = 0; i < 1024; i += 8){
|
||||
var v;
|
||||
try{ v = base.add(i).readU64(); }catch(e){ continue; }
|
||||
var cand = v.and(ptr('0x7fffffffffff'));
|
||||
var s = cand.toString();
|
||||
if (cand == ptr('0')) continue;
|
||||
// heap-ish: 0x7[7-9ca].. or 0xb4.. or 0x77..
|
||||
if (!(/^0x(7[78a-c9]|b4)[0-9a-f]{9,}$/.test(s))) continue;
|
||||
if (seen[s]) continue;
|
||||
if (!isReadable(cand)) continue;
|
||||
seen[s] = true;
|
||||
var entry = {off: i - 512, ptr: s};
|
||||
var kind = 'raw';
|
||||
if (startsWith(cand, JSON_HDR)) kind = 'JSON';
|
||||
else if (startsWith(cand, MAGIC_HDR)) kind = 'MAGIC';
|
||||
entry.kind = kind;
|
||||
if (kind === 'JSON' || kind === 'MAGIC'){
|
||||
entry.full = hexb(cand, 9000);
|
||||
} else {
|
||||
entry.head = hexb(cand, 64) || '';
|
||||
}
|
||||
found.push(entry);
|
||||
}
|
||||
}catch(e){}
|
||||
out.entries = found;
|
||||
return out;
|
||||
}
|
||||
|
||||
function hookFn(off, name){
|
||||
try{
|
||||
var base = Process.getModuleByName('libhydeviceid.so').base;
|
||||
@@ -120,6 +164,10 @@ function hookFn(off, name){
|
||||
var pr = decodeArg(ctx[r], r);
|
||||
if (pr) ev[r] = pr;
|
||||
});
|
||||
// cryptoA: deep deref x0
|
||||
if (name === 'cryptoA_5b1f4'){
|
||||
ev.deep = deepDeref(ctx.x0);
|
||||
}
|
||||
send({type:'pipe', ev: ev});
|
||||
}
|
||||
});
|
||||
@@ -146,6 +194,29 @@ function attachOne(base, off, name){
|
||||
var pr = decodeArg(ctx[r], r);
|
||||
if (pr) ev[r] = pr;
|
||||
});
|
||||
// cryptoA: deep deref x0
|
||||
if (name === 'cryptoA_5b1f4'){
|
||||
ev.deep = deepDeref(ctx.x0);
|
||||
}
|
||||
// copy_61098 second call (x0=MAGIC): heap scan for JSON + dump 4600B
|
||||
if (name === 'copy_61098' && (ev.x0||{}).kind === 'MAGIC'){
|
||||
var hits = [];
|
||||
try{
|
||||
var pat = '7b 22 61 70 70 49 64 22 3a 22 35 30 30 38';
|
||||
var ranges = Process.enumerateRanges('rw-');
|
||||
for (var ri = 0; ri < ranges.length; ri++){
|
||||
var rng = ranges[ri];
|
||||
if (rng.size > 0x10000000) continue;
|
||||
var m = Memory.scanSync(rng.base, rng.size, pat);
|
||||
for (var mi = 0; mi < m.length; mi++){
|
||||
var full = hexb(m[mi].address, 4600) || '';
|
||||
var h = full.substring(0, 96);
|
||||
hits.push({addr: String(m[mi].address), head: h, full: full});
|
||||
}
|
||||
}
|
||||
}catch(e){}
|
||||
send({type:'jsonhits', n: hits.length, hits: hits, t: Date.now() - t0});
|
||||
}
|
||||
send({type:'pipe', ev: ev});
|
||||
}
|
||||
});
|
||||
@@ -183,6 +254,20 @@ try{
|
||||
"""
|
||||
|
||||
|
||||
def load_with_retry(session, js, n=5):
|
||||
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])
|
||||
@@ -226,26 +311,25 @@ def main():
|
||||
print(f"[{ev.get('t')}ms] {ev.get('fn')}::{ev.get('phase')} " + " ".join(parts), flush=True)
|
||||
events.append({"type": "pipe", "ev": ev})
|
||||
OUT.write_text(json.dumps(events))
|
||||
elif t == "jsonhits":
|
||||
print(f"[{p.get('t')}ms] [jsonhits] n={p.get('n')}", flush=True)
|
||||
events.append({"type": "jsonhits", "t": p.get('t'), "hits": p.get('hits')})
|
||||
OUT.write_text(json.dumps(events))
|
||||
elif t == "wire":
|
||||
print(f"[{p.get('t')}ms] [wire] len={p.get('len')}", flush=True)
|
||||
events.append({"type": "wire", "len": p.get('len'), "t": p.get('t'), "hex": p.get('hex')})
|
||||
OUT.write_text(json.dumps(events))
|
||||
|
||||
script = session.create_script(JS)
|
||||
script.on("message", on_message)
|
||||
script.load()
|
||||
print("[*] 主JS 已装载, 加载 bypass + resume", flush=True)
|
||||
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()
|
||||
# 严格按约定顺序: 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)
|
||||
session.create_script(
|
||||
(RE / "evidence/scripts/patch_guard_block_termination.js").read_text()
|
||||
).load()
|
||||
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("[*] 已 resume。等待 dfpReport", flush=True)
|
||||
try:
|
||||
while True:
|
||||
|
||||
@@ -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