"""虎牙多账号设备画像生成与管理。 为每个账号生成并持久化独立的设备身份画像(机型、屏幕、指纹、设备ID等)。 注意:画像只承载 *soft* 设备字段(机型/屏幕/随机指纹/随机 device_id)。 ``safedeviceid``(RSA action 令牌)与登录帧 ``device_id`` 由 :mod:`core.huya.dfp_register` 在每次 WUP 登录前经新设备注册链实时签发, 不再写入画像,也不再允许任何固定金样本令牌被多账号复用。 ⚠ 字段状态速览(详见 ``docs/HUYA_APP_OVERVIEW.md`` §四): - 随机: fingerprint / 机型 / 屏幕 / 宽高 / device_id(画像侧); - 固定: hdid(32hex 硬锚,全账号同一) / app_version / sdk_version; - 动态签发: safedeviceid、登录帧 device_id。 """ from __future__ import annotations import hashlib import json import os import random from pathlib import Path ROOT = Path(__file__).resolve().parent.parent.parent DATA_DIR = ROOT / "data" PRIMARY_PROFILE_DB = DATA_DIR / "huya_device_profiles.json" FALLBACK_PROFILE_DB = ROOT / "evidence" / "device_profiles.json" 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)), ] HDID32 = "ed0db8334cadd236c00cadf7e11ab5a5" # 登录帧t1.t0 设备证书 (与 GUID32/doLaunch 无关系) APP_VERSION = "13.4.22" SDK_VERSION = "1.0.80138" def _rand_sha1_hex() -> str: return hashlib.sha1(os.urandom(20)).hexdigest() def generate_profile(model_pick=None) -> dict: """生成一套随机设备画像。""" 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(), "screen": screen, "width": str(w), "height": str(h), "device_id": _rand_sha1_hex(), "hdid": HDID, # 注: 不含 safedeviceid —— 每次登录前由 dfp_register 注册链签发, # 画像不再持久化固定令牌 (见模块注释)。 } def _load_db() -> dict: if PRIMARY_PROFILE_DB.exists(): try: return json.loads(PRIMARY_PROFILE_DB.read_text("utf-8")) except Exception: pass if FALLBACK_PROFILE_DB.exists(): try: return json.loads(FALLBACK_PROFILE_DB.read_text("utf-8")) except Exception: pass return {} def _save_db(db: dict) -> None: try: PRIMARY_PROFILE_DB.parent.mkdir(parents=True, exist_ok=True) PRIMARY_PROFILE_DB.write_text(json.dumps(db, indent=2, ensure_ascii=False), encoding="utf-8") except Exception: pass 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