Files
live-hub-py/tools/huya_device_profile.py
T
yml2213 45b5e9128c docs(huya): 新增总览去困惑地图 + 代码风险标记 + 真机 Frida 稳定注入通道
- docs/HUYA_APP_OVERVIEW.md: 整体流程/三形态hdid区分/登录入口盘点/随机化矩阵/风险清单/真机Frida现状
- core/huya 各模块 docstring 加风险标记与 hdid 名称混淆提示, 指向总览
- tools/app_login_flow 标注已过时(未接注册链), tools/huya_device_profile 标注与 core 策略关系
- scripts/phone_stable_capture.py + diag_phone_lifecycle.py: 真机 spawn+art_callsite 稳定抓帧/四组诊断
- evidence/diag_phone/: 真机基线/attach/稳定抓帧实验证据 (90s 存活无 EGL 崩)
2026-08-28 10:44:56 +08:00

135 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""多账号设备画像生成器 —— 每账号一套独立设备身份 (2026-08-26 实测定论)。
⚠ 本文件是 tools 侧的独立试验版画像器。core 生产链用的是
``core/huya/device_profile.py`` + ``core/huya/dfp_register``safedeviceid/登录帧
device_id 每次登录前动态签发,画像不再写入固定 safedeviceid)。
背景 (服务端校验矩阵, 全部对 wup.huya.com hypasswordLogin 实测):
┌──────────────────┬───────────────────────────────────────────────┐
│ 字段 │ 换值表现 │
├──────────────────┼───────────────────────────────────────────────┤
│ hdid (32hex) │ APP_SIGN_NOT_MATCH 硬错 ❌ (设备注册核心) │
│ app_version │ APP_SIGN_NOT_MATCH 硬错 ❌ (与设备注册态绑定) │
│ fingerprint 40hex│ 触发 safe_auth 滑块(pt_auth) → 自动过验后放行 ✅ │
│ device_id 40hex │ 直接通过 ✅ (无校验) │
│ model/vendor/ │ 直接通过 ✅ (自述类, 需与 screen/UA 自洽) │
│ screen/宽高 │ │
│ safedeviceid │ 直接通过 ✅ (RSA action 令牌, 服务端不校验内容) │
└──────────────────┴───────────────────────────────────────────────┘
结论: hdid + app_version 是"设备注册"硬约束(由真机 dfpReport 链注册,
libhydeviceid.so 生成 / 服务端绑定版本), 多账号时**同一 hdid+版本 必须复用
(它不泄露单一账号身份, 只是设备签名锚点)**; 其余字段每账号随机即可,
首次触发滑块由 solver 自动过 → 登录照常出 cred (已端到端实测)。
hdid 来源与可铸造性 (2026-08-26 实测):
- WUP 帧 t1.t0 的 hdid (32hex) = App 原生 libhydeviceid.so 生成的设备 ID
(APK 静态分析: libhydeviceid.so = 设备ID生成), 经 dfpReport 链注册。
- 换任意新值 → APP_SIGN_NOT_MATCH 硬错: 随机32hex ✗ / df.huya.com
df/collect 下发的 40hex hdid ✗ / 截断 ✗ —— 纯代码无法铸造新 hdid。
- 共用同一 hdid: 登录不受阻 (实测新账号直出 cred), 但服务端可在设备维度
关联账号; 若需真隔离, 须多台真机各采一套 {hdid, fingerprint, device_id}
建设备池轮换。
用法:
python tools/huya_device_profile.py # 生成并打印一套新画像
python tools/huya_device_profile.py <账号> # 按账号取/建画像(持久化)
python tools/huya_device_profile.py --list # 列出已持久化画像
"""
from __future__ import annotations
import hashlib
import json
import os
import random
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
PROFILE_DB = HERE.parent / "evidence" / "device_profiles.json"
# 真实机型库 (vendor, model, screen固件段, 宽x高) —— 与登录自洽即可
REAL_MODELS = [
("xiaomi", "M2102J2SC", "M2102J2SC,30,11", (1080, 2120)),
("vivo", "V2370A", "V2370A,34,13", (1080, 2412)),
("oppo", "PFFM20", "PFFM20,34,13", (1080, 2412)),
("honor", "SDY-AN00", "SDY-AN00,31,12", (1080, 2400)),
("samsung", "SM-G9910", "SM-G9910,31,12", (1440, 3200)),
("oneplus", "PGZ110", "PGZ110,34,13", (1080, 2412)),
("redmi", "23049PCD8G", "23049PCD8G,34,13", (1080, 2400)),
("realme", "RMX3366", "RMX3366,34,13", (1080, 2412)),
("iqoo", "V2183A", "V2183A,34,13", (1080, 2400)),
("nubia", "NX729J", "NX729J,33,13", (1080, 2400)),
("motorola", "XT2301-5", "XT2301-5,33,13", (1080, 2400)),
("gionee", "GN9013", "GN9013,29,10", (720, 1560)),
]
# 设备注册硬锚点 (金样本真机采集, 不可随意更换 —— 见文件头矩阵)
HDID = "ed0db8334cadd236c00cadf7e11ab5a5"
APP_VERSION = "13.4.22"
SDK_VERSION = "1.0.80138"
# safedeviceid 为 dfpReport 下发的 RSA action 令牌, 服务端不校验内容,
# 可直接复用金样本 (或随机同长度 base64, 需 PQwemAN9 前缀)
SAFEDEVICEID_DEFAULT = (
"PQwemAN9NHkZKoMqVTFUZBIypqMTaQEOrmXr37xQVhQZqrL/gUKEQ11xvE0ju48V8O/"
"t9UBGSp27m4+6bP4IiAEnpaR5Rj1kHEfN2SPLPqYZW9vroxUSoAvjJn6ezTP9jWGxxlRDCbt"
"Py4Rd6MencYT/pNImVIWK+YbNKZt1O05bHUFhqHf3"
)
def _rand_sha1_hex() -> str:
return hashlib.sha1(os.urandom(20)).hexdigest()
def generate_profile(model_pick=None) -> dict:
"""生成一套随机设备画像 (不含 hdid/app_version/safedeviceid 硬锚)。"""
vendor, model, screen, (w, h) = random.choice(REAL_MODELS) \
if model_pick is None else model_pick
return {
"app_version": APP_VERSION,
"sdk_version": SDK_VERSION,
"vendor": vendor,
"model": model,
"os": "android",
"ip": "127.0.0.1",
"fingerprint": _rand_sha1_hex(), # 40hex, 换→滑块(自动过)
"screen": screen,
"width": str(w),
"height": str(h),
"device_id": _rand_sha1_hex(), # 40hex, 服务端不校验
"hdid": HDID, # 硬锚: 不可换
"safedeviceid": SAFEDEVICEID_DEFAULT, # 令牌: 可复用
}
def _load_db() -> dict:
if PROFILE_DB.exists():
return json.loads(PROFILE_DB.read_text())
return {}
def _save_db(db: dict) -> None:
PROFILE_DB.parent.mkdir(exist_ok=True)
PROFILE_DB.write_text(json.dumps(db, indent=1, ensure_ascii=False))
def get_profile(account: str, force_new: bool = False) -> dict:
"""按账号取/建画像 (幂等: 同账号复用同套, 保持设备身份稳定)。"""
db = _load_db()
if not force_new and account in db:
return db[account]
p = generate_profile()
db[account] = p
_save_db(db)
return p
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "--list":
for k, v in _load_db().items():
print(f"{k}: {v['vendor']} {v['model']} fp={v['fingerprint'][:12]}...")
sys.exit(0)
acct = sys.argv[1] if len(sys.argv) > 1 else "(临时)"
p = get_profile(acct) if acct != "(临时)" else generate_profile()
print(json.dumps(p, indent=1))