feat(huya): 铸币机打通 - doLaunch tReq 结构bug修复(缺外层struct_begin)+双雨确定性实证(sGuid=f(mid))
- encode_live_launch_req: 双层 struct_begin(0)+struct_end 收尾 (服务器tag0 tId 期望STRUCT) - build 只发 tReq 单键 (对齐真机 launch 帧, 不再追加 platform/version 等) - parse_launch_rsp: gzip 解压 + \x06\x20(32hex) sGuid 可靠提取 - live 矩阵实证: 同指纹同 sGuid / 变异指纹新 sGuid / device_id 不驱动, mid 驱动 - docs §11.8: 破案链 + 实证表 + 铸币闭环 (mid→doLaunch→sGuid→登录链)
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -69,6 +70,8 @@ def encode_live_launch_req(profile: dict) -> bytes:
|
||||
model, qimei, luid, apn, net_type
|
||||
"""
|
||||
w = _Writer()
|
||||
# 外层 LiveLaunchReq struct (tag0) —— 缺它整体结构上移一层
|
||||
w.struct_begin(0)
|
||||
w.struct_begin(0) # UserId tId
|
||||
w.int64(0, int(profile.get("luid", 0)))
|
||||
_w_string_or_skip(w, 1, profile.get("guid"))
|
||||
@@ -93,6 +96,7 @@ def encode_live_launch_req(profile: dict) -> bytes:
|
||||
w.struct_end()
|
||||
|
||||
w.int16(2, int(profile.get("b_support_domain", 1))) # bSupportDomain
|
||||
w.struct_end() # 外层 LiveLaunchReq
|
||||
return w.get()
|
||||
|
||||
|
||||
@@ -102,12 +106,9 @@ def build_live_launch_wup(profile: dict, request_id: int | None = None) -> bytes
|
||||
request_id = int.from_bytes(__import__("os").urandom(4), "big") & 0x7FFFFFFF
|
||||
req_jce = encode_live_launch_req(profile)
|
||||
|
||||
# App 端 a09.getOtherParams() 还会追加 platform/version/channel/(yyuid/uid/imei) 键
|
||||
# 真机实测 (WG 全解密捕获): launch servant 的 map 只有 "tReq" 一个键,
|
||||
# 不追加 platform/version/channel 等 (此前追加属过度拟合, 服务器仍拒值)
|
||||
entries = [("tReq", ("bytes", req_jce))]
|
||||
for k in ("platform", "version", "channel", "yyuid", "uid", "imei"):
|
||||
v = profile.get(k)
|
||||
if v:
|
||||
entries.append((k, ("string", v)))
|
||||
sb = _Writer()
|
||||
sb.map_begin(0, len(entries))
|
||||
for k, (kind, val) in entries:
|
||||
@@ -202,6 +203,15 @@ def parse_launch_rsp(resp: bytes) -> dict:
|
||||
"""
|
||||
if not HAVE_TAF:
|
||||
raise RuntimeError("缺少 core.huya.taf_protocol, 无法解码")
|
||||
# HTTP 直连响应可能 gzip 压缩 (1f 8b)
|
||||
if resp[:2] == b"\x1f\x8b":
|
||||
import gzip as _g
|
||||
try:
|
||||
resp = _g.decompress(resp)
|
||||
except Exception:
|
||||
pass
|
||||
# 可靠路径: tRsp 结构内 tag0 = sGuid(STRING4 0x06, len 0x20=32hex)
|
||||
_sguid_re = re.search(rb"\x06\x20([0-9a-f]{32})", resp)
|
||||
# WUP 帧带 4 字节大端长度前缀 (len = 4+body); 剥离后解析
|
||||
if len(resp) >= 4:
|
||||
declared = struct.unpack(">I", resp[:4])[0]
|
||||
@@ -209,7 +219,7 @@ def parse_launch_rsp(resp: bytes) -> dict:
|
||||
resp = resp[4:]
|
||||
i = TafInputStream(resp)
|
||||
header: dict[str, object] = {}
|
||||
sguid_candidates: list[str] = []
|
||||
sguid_candidates: list[str] = ([_sguid_re.group(1).decode()] if _sguid_re else [])
|
||||
structs: list[dict] = []
|
||||
buf = i.buf
|
||||
while True:
|
||||
|
||||
Reference in New Issue
Block a user