- probe_huya_login_scene: 各 appId/UA 组合的风控策略探测 - probe_huya_proxy: 走代理源对比直连的风控策略 - probe_huya_qr_bind: 扫码登录闭环(getQrId->scanQrPicNotify-> bindQrLoginUser->tryQrLogin), 实测出有效web会话; 含wupData 结构地图与证书定位实验结论(见 docs/虎牙扫码登录闭环逆向.md)
87 lines
3.0 KiB
Python
87 lines
3.0 KiB
Python
"""用代理池实测虎牙密码登录:验证换 IP 能否把风控从扫码降回滑块。
|
|
|
|
用法:
|
|
.venv/bin/python scripts/probe_huya_proxy.py <账号> <明文密码> --proxy-api <代理API URL>
|
|
|
|
代理 API 返回格式支持:
|
|
- JSON: {"code":0, "data":[{"IP":"x","Port":y}, ...]} 或 {"code":0, "data":[{"ip":"x","port":y}]}
|
|
- 纯文本: 多行 "ip:port" 或 "ip,port"
|
|
|
|
流程:
|
|
1. 直连(当前出口 IP)走一次登录,记录风控策略
|
|
2. 从代理 API 取代理,换 IP 走登录,记录每个 IP 的风控策略
|
|
3. 汇总对比
|
|
|
|
不会写库,不会提交验证码(只走到触发风控)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from core.douyu.proxy_fetcher import ProxyFetcher
|
|
from core.huya.login import HuyaPasswordLogin
|
|
|
|
|
|
def classify(payload: dict) -> str:
|
|
data = payload.get("data") or {}
|
|
strategies = data.get("strategys") or []
|
|
joined = " ".join(str(s.get("data") or "") for s in strategies).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:
|
|
return "滑块(pt_auth)"
|
|
return f"其他(strategy={','.join(strategy_values) or '无'})"
|
|
|
|
|
|
def one_pass(username: str, password: str, proxies=None, label: str = "") -> dict:
|
|
login = HuyaPasswordLogin(username=username, password=password, proxies=proxies)
|
|
login.prepare_device()
|
|
login.init_udb_middle()
|
|
payload = login._password_login_once()
|
|
rc = int(payload.get("returnCode") or 0)
|
|
if rc == 0:
|
|
kind = "成功(无需验证)"
|
|
elif rc in (10030, 10039):
|
|
kind = classify(payload)
|
|
else:
|
|
kind = f"code={rc} message={payload.get('message')}"
|
|
print(f" [{label or '直连'}] returnCode={rc} -> {kind}")
|
|
return payload
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("username")
|
|
parser.add_argument("password")
|
|
parser.add_argument("--proxy-api", required=True, help="代理提取 API URL")
|
|
parser.add_argument("--max-proxies", type=int, default=3, help="最多测几个代理 IP")
|
|
args = parser.parse_args()
|
|
|
|
print(f"=== 账号 {args.username} ===")
|
|
print("1) 直连基线:")
|
|
one_pass(args.username, args.password, label="直连")
|
|
|
|
print(f"\n2) 代理池测试 (最多 {args.max_proxies} 个 IP):")
|
|
fetcher = ProxyFetcher(api_url=args.proxy_api)
|
|
for i in range(args.max_proxies):
|
|
proxy = fetcher.fetch_new_proxy()
|
|
if not proxy:
|
|
print(" 代理耗尽/获取失败,停止")
|
|
break
|
|
proxies = {"http": proxy, "https": proxy}
|
|
print(f" --- 代理 #{i + 1}: {proxy} ---")
|
|
try:
|
|
one_pass(args.username, args.password, proxies=proxies, label=f"IP#{i + 1}")
|
|
except Exception as exc: # noqa: BLE001
|
|
print(f" !! 异常: {type(exc).__name__}: {exc}")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|