Files
live-hub-py/scripts/probe_huya_login_scene.py
T
yml2213 9aea9851bd test(huya): 风控场景/代理/扫码闭环探针与逆向文档
- probe_huya_login_scene: 各 appId/UA 组合的风控策略探测
- probe_huya_proxy: 走代理源对比直连的风控策略
- probe_huya_qr_bind: 扫码登录闭环(getQrId->scanQrPicNotify->
  bindQrLoginUser->tryQrLogin), 实测出有效web会话; 含wupData
  结构地图与证书定位实验结论(见 docs/虎牙扫码登录闭环逆向.md)
2026-08-25 11:51:01 +08:00

106 lines
3.8 KiB
Python

"""探测虎牙密码登录在不同 appId/UA 组合下的风控策略。
只走到「触发风控」这一步,打印风控策略(strategy 值 + data URL 路径),
不真正提交滑块/短信/扫码验证,也不写库。
用法:
.venv/bin/python scripts/probe_huya_login_scene.py <账号> <明文密码> [--app-id 5002|5008] [--ua desktop|android]
会依次测试多组组合,对比风控返回的是:
qr_auth -> 扫码验证(无法自动化)
dx_auth -> 短信验证(无法自动化)
pt_auth / get3 / slider -> 滑块/点选(可自动化)
"""
from __future__ import annotations
import argparse
import sys
from core.huya.login import (
APP_ID,
APP_ID_MOBILE,
APP_UA_MOBILE,
DEFAULT_UA,
HuyaPasswordLogin,
)
def classify_strategy(payload: dict) -> str:
"""从登录响应里识别风控策略类型。"""
data = payload.get("data") or {}
strategies = data.get("strategys") or []
if not strategies:
return "unknown"
urls = [str(s.get("data") or "") for s in strategies]
joined = " ".join(urls).lower()
strategy_values = [str(s.get("strategy")) for s in strategies]
if "qr_auth" in joined or "64" in strategy_values:
return "qr_auth(扫码)"
if "dx_auth" in joined:
return "dx_auth(短信)"
if "pt_auth" in joined or "get3" in joined or "slider" in joined:
return "pt_auth(滑块)"
return f"其他(strategy={','.join(strategy_values)})"
def probe(username: str, password: str, app_id: str, ua: str | None = None) -> dict:
login = HuyaPasswordLogin(username=username, password=password, app_id=app_id, ua=ua)
ua_label = login.ua[:40]
print(f"\n=== app_id={app_id} | app_sign={login.app_sign[:8]} | ua={ua_label}... ===")
login.prepare_device()
login.init_udb_middle()
payload = login._password_login_once()
return_code = int(payload.get("returnCode") or 0)
print(f"returnCode={return_code}")
if return_code == 0:
print(" → 登录成功(无需验证)")
return payload
if return_code in (10030, 10039):
kind = classify_strategy(payload)
print(f" → 风控策略: {kind}")
data = payload.get("data") or {}
for s in data.get("strategys") or []:
print(f" strategy={s.get('strategy')} dataType={s.get('dataType')}")
url = str(s.get("data") or "")
print(f" data_url={url.split('?')[0]}")
return payload
print(f" message={payload.get('message')} desc={payload.get('description')}")
return payload
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("username")
parser.add_argument("password")
parser.add_argument("--app-id", default=None, help="只测单个 appId")
parser.add_argument("--ua", default=None, choices=["desktop", "android"], help="只测单种 UA")
args = parser.parse_args()
combos = []
app_ids = [args.app_id] if args.app_id else [APP_ID, APP_ID_MOBILE]
for app_id in app_ids:
if args.ua == "desktop":
combos.append((app_id, DEFAULT_UA))
elif args.ua == "android":
combos.append((app_id, APP_UA_MOBILE))
else:
# 默认每组都测;5002 也测 Android UA(验证设备画像自洽假设)
if app_id == APP_ID_MOBILE:
combos.append((app_id, None)) # None -> 自动选 Android UA
else:
combos.append((app_id, None)) # None -> 桌面 UA
combos.append((app_id, APP_UA_MOBILE)) # 强制 Android UA
for app_id, ua in combos:
try:
probe(args.username, args.password, app_id, ua)
except Exception as exc: # noqa: BLE001
print(f" !! 探测异常: {type(exc).__name__}: {exc}")
return 0
if __name__ == "__main__":
sys.exit(main())