- core/huya: 新增 app_login, wup_encoder, nonce_forge, cert_forge, envelope_forge, device_profile, udb_aes - core/huya/__init__.py: 导出 login_huya_app_password 与 HuyaAppPasswordLogin - web/backend: 新增 /accounts/app-password-login 与 /accounts/app-password-login/selected 路由及 Schema,与原 Web 密码登录独立分开 - web/frontend: 增加 App 登录 API 与前端界面“App 登录选中”操作,弹窗结果明确区分 - tests: 新增 test_huya_app_login.py 单元测试覆盖全链路
81 lines
3.1 KiB
Python
81 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
dfpReport 同步 pair 分析器
|
|
- 从 evidence/dfp_pair_sync.json 提取每个 wire 的 tag2 密文 (去掉 10B 魔数 + 9B 尾部字段)
|
|
- 结合 plains 内存缓冲 (JSON 头 + 二进制尾) 计算 keystream
|
|
- 验证输入布局假设: input = [JSON 554B][binary L-554B]
|
|
"""
|
|
import json, binascii, sys
|
|
|
|
MAGIC = bytes.fromhex('571882cf664bb39401ee')
|
|
TRAILER = bytes.fromhex('00400c0b8c980ca80c')
|
|
EVID = 'evidence/dfp_pair_sync.json'
|
|
|
|
def load_pairs():
|
|
d = json.load(open(EVID))
|
|
pairs = []
|
|
for i in range(0, len(d), 2):
|
|
w, p = d[i], d[i+1]
|
|
raw = binascii.unhexlify(w['hex'])
|
|
idx = raw.find(b'\r\n\r\n')
|
|
body = raw[idx+4:]
|
|
mp = body.find(MAGIC)
|
|
assert body.endswith(TRAILER), f"pair{i//2} trailer mismatch"
|
|
tag2 = body[mp+10 : len(body)-len(TRAILER)]
|
|
plains = []
|
|
for pl in p['plains']:
|
|
if isinstance(pl, dict) and pl.get('hex'):
|
|
plains.append({'addr': pl['addr'], 'buf': binascii.unhexlify(pl['hex'])})
|
|
pairs.append({'t': w['t'], 'tag2': tag2, 'plains': plains})
|
|
return pairs
|
|
|
|
def find_json_end(buf):
|
|
"""locate the end of the 554B JSON string at buf head; return index after closing brace + boundaries of following non-zero region"""
|
|
# JSON starts with '{' at offset 0. Find the matching close at known length 554
|
|
if buf[:1] != b'{':
|
|
return None
|
|
# standard JSON serialized 554B for this device
|
|
json_bytes = buf[:554]
|
|
assert json_bytes.startswith(b'{"appId":"5008"'), json_bytes[:40]
|
|
return 554
|
|
|
|
def nonzero_ranges(buf, start):
|
|
"""return list of (start,end) runs of non-zero bytes in buf[start:]"""
|
|
runs = []
|
|
in_run = False
|
|
for i in range(start, len(buf)):
|
|
if buf[i] != 0 and not in_run:
|
|
s = i; in_run = True
|
|
elif buf[i] == 0 and in_run:
|
|
runs.append((s, i)); in_run = False
|
|
if in_run:
|
|
runs.append((s, len(buf)))
|
|
return runs
|
|
|
|
def main():
|
|
pairs = load_pairs()
|
|
print(f"{len(pairs)} pairs loaded")
|
|
for k, pr in enumerate(pairs):
|
|
t2 = pr['tag2']
|
|
print(f"\n===== pair{k} t={pr['t']} tag2_len={len(t2)} (data={len(t2)}) =====")
|
|
for pi, pl in enumerate(pr['plains']):
|
|
buf = pl['buf']
|
|
je = find_json_end(buf)
|
|
runs = nonzero_ranges(buf, 0) if je else []
|
|
# focus: runs after JSON end
|
|
post = [r for r in runs if r[0] >= 554]
|
|
print(f" plain{pi} addr={pl['addr']} buflen={len(buf)} json@0..{je if je else '?'}")
|
|
# show a compact view: for each 256B block 0..8192 whether zero or nonzero
|
|
blocks = []
|
|
for b in range(0, len(buf), 256):
|
|
chunk = buf[b:b+256]
|
|
nz = sum(1 for x in chunk if x != 0)
|
|
blocks.append(f"{b//256}:{nz}")
|
|
print(" nz/256B:", ' '.join(blocks))
|
|
# print runs summary first 6
|
|
for r in post[:6]:
|
|
print(f" nonzero {r[0]}..{r[1]} (len {r[1]-r[0]}) head={buf[r[0]:r[0]+32].hex()}")
|
|
|
|
if __name__ == '__main__':
|
|
main() |