feat(huya): 一键全自动测试脚本(密码->cookie)

This commit is contained in:
yml2213
2026-08-25 19:14:34 +08:00
parent d29290ae0e
commit d1a2b6aa8b
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""一键全自动测试: 账号密码 -> 页面cookie(biztoken)。
用法:
python tools/full_auto_test.py <账号如hy_xxx或300023887> <密码>
前提: evidence/cert_keycap.json 内有较新鲜的 wupData 信封
(过期则先在设备上跑 scripts/hook_cert_keycap.py 刷新)。
"""
import sys, re, json, hashlib, base64, time, struct
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "tools"))
import requests
from huya_wup_encoder import build_password_login_wup
from cert_forge import build_p1, forge_cert
def password_login(account: str, password: str) -> bytes:
"""纯Python护照登录, 返回新鲜cred(114B)。"""
uid = account[3:] if account.startswith("hy_") else account
raw = (ROOT / "evidence/wup_passwordlogin_taf.bin").read_bytes()
mj = json.loads(re.search(rb'(\{"associationId.*?\})', raw).group(1))
ua = re.search(rb'(\{"curl".*?"user_action":\[.*?\]\})', raw).group().decode()
sd = re.search(rb'PQwemAN9[A-Za-z0-9+/=]+', raw).group().decode()
dev = {"app_version": "13.4.22", "sdk_version": "1.0.80138",
"vendor": "xiaomi", "model": "M2102J2SC", "os": "android",
"ip": "127.0.0.1",
"fingerprint": "02df398797432eadefcc12767119ad5e80999389",
"screen": "M2102J2SC,30,11", "width": "1080", "height": "2120",
"device_id": "7c5387e0539c023c31c4ff0e807e7256117385ee"}
pkt = build_password_login_wup(
uid, hashlib.sha1(password.encode()).hexdigest(), sd,
"ed0db8334cadd236c00cadf7e11ab5a5",
mj["session"], mj["traceId"], ua, dev)
r = requests.post(
"https://wup.huya.com", data=pkt,
headers={"Content-Type": "application/multipart-formdata; charset=UTF-8",
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 11)",
"Accept-Encoding": "gzip"}, timeout=15)
assert r.status_code == 200, f"登录HTTP {r.status_code}"
b = r.content
s = b.find(b"\x0a\x0a", 0x40)
e = b.find(b"_wup_header")
d = b[s:e - 6]
m = re.search(rb"\x3d\x00([\x00-\x03])(.)", d) # tag3 SIMPLE_LIST, INT8 len
assert m, "响应中未找到cred字段"
ln = m.group(2)[0]
st = m.start() + 4
cred = d[st:st + ln]
assert len(cred) == 114 and cred[:1] == b"\x0a", f"cred异常 {len(cred)}"
return cred
def forge(cred: bytes) -> str:
t = int(time.time() * 1000) & ((1 << 48) - 1)
rnd = (t.to_bytes(6, "big") + b"\x00\x00"
+ ((t << 16) & ((1 << 64) - 1)).to_bytes(8, "big")[2:] + bytes(8))[:20]
fp = b"02df398797432eadefcc12767119ad5e80999389"
P1 = build_p1(b"5008", fp, cred, rnd=rnd)
return forge_cert(P1, key_idx=0x20).hex()
def main():
acct, pwd = sys.argv[1], sys.argv[2]
print("[1/3] 纯Python密码登录 ...")
cred = password_login(acct, pwd)
print(f" cred OK: {len(cred)}B {cred[:8].hex()}")
print("[2/3] 铸造证书 ...")
cert_hex = forge(cred)
out = ROOT / "evidence/auto_test_cred.json"
out.write_text(json.dumps({"p1": "", "cert_hex": cert_hex,
"cred_hex": cred.hex()}))
print(f" 证书 OK: {len(cert_hex)//2}B")
print("[3/3] 扫码绑定取cookie ...")
import subprocess
probe = ROOT / "scripts/probe_forge_cert_bind.py"
src = probe.read_text()
src = re.sub(r'open\("evidence/hycred_[a-z_]+\.json"\)',
f'open("{out.as_posix()}")', src)
tmp = ROOT / "scripts/_probe_auto.py"
tmp.write_text(src)
r = subprocess.run([sys.executable, str(tmp)], capture_output=True, text=True)
print(r.stdout[-800:])
if "ok\": true" in r.stdout:
print("\n🎉 全链路成功: 密码已换出页面cookie(biztoken)!")
return 0
print("\n❌ 绑定失败——若40020多为信封过期, 请刷新wupData后重试")
return 1
if __name__ == "__main__":
raise SystemExit(main())