Files
live-hub-py/docs/虎牙App密码登录-WUP明文抓包.md
T
yml2213 ff99c036f8 虎牙App密码登录: 逆向WUP协议并实现纯Python登录编码器
- hook WUP序列化层抓取TLS加密前明文,确认密码登录走
  servant=huyaudbwebui / func=hypasswordLogin (msgType=0x1001)
- 逆向JCE RequestPacket帧格式(1015字节TAF二进制),定稿三层结构:
  WUP header(tag1-10) -> sBuffer Map -> _wup_data业务struct
- 澄清两个疑点字节: 0x66/0x7d 是TAF字段头(非分隔符)
- 实现 tools/huya_wup_encoder.py,逐字节复现金标准
- 实测纯Python动态构造登录成功(返回uid+token),TLS指纹不阻断
- userAction随机坐标/时间戳同样通过,内置make_user_action辅助函数
- 新增证据金标准 + 协议文档
2026-08-25 02:59:20 +08:00

13 KiB
Raw Blame History

虎牙 App 密码登录 — WUP 明文抓包实证(最终)

状态:密码登录完整 WUP 请求已抓到(TLS 加密前明文) 证据:/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/evidence/derived/huya-wup-full3.jsonl 方法:hook createWupRequestData<AppLgnPasswordLoginReq> onLeave 拿到序列化后的 WUP 字节


一、密码登录 WUP 请求(核心结论)

servant huyaudbwebui
func hypasswordLogin
协议 WUP / TAF(JCE) 二进制序列化
msgType 4097 (0x1001) = MsgLogin.mMsgId
请求大小 1356 字节
密码 SHA1(明文) hex,无盐(772ed992b0e161276f44ec63671e60155c506294
name hy_300023887hy_ + 虎牙号)

二、WUP 帧完整结构(已 100% 逆向定稿)

金标准文件:evidence/wup_passwordlogin_taf.bin1015 字节 TAF 二进制)+ evidence/wup_passwordlogin_base64.bin1356 字节 base64,即 HTTP body HTTPPOST https://udblgn.huya.com/open/hy/passwordLoginContent-Type: application/x-wup

2.1 外层:WUP RequestPacket(标准 JCE/TAF 序列化)

[0:4]   uint32 BE 总长 = 1015

JCE RequestPacket 字段(head = (tag<<4)|type:
  tag1  iVersion      = 3                  (INT8:   10 03)
  tag2  cPacketType   = 0                  (ZERO:   2c)
  tag3  iMessageType  = 0                  (ZERO:   3c)
  tag4  iRequestId    = session(3251699)   (INT32:  42 00 31 9d f3)
  tag5  sServantName  = "huyaudbwebui"     (STR1:   56 0c + 12B)
  tag6  sFuncName     = "hypasswordLogin"  (STR1:   66 0f + 15B)
  tag7  sBuffer       = MAP{...}           (SIMPLE_LIST: 7d 00 01 03 c1 + 961B)
  tag8  iTimeout      = 0                  (ZERO:   8c)
  tag9  context       = {} (空map)         (MAP:    98 0c)
  tag10 status        = {} (空map)         (MAP:    a8 0c)

关键澄清:之前误认为 'f'(0x66)'}' (0x7d) 是分隔符,实为 JCE 字段头:

  • 0x56 = 字段5(sServantName)+STRING1
  • 0x66 = 字段6(sFuncName)+STRING1
  • 0x7d = 字段7(sBuffer)+SIMPLE_LIST

2.2 中层:sBuffer961 字节)是一个 MAP

MAP (2条目):
  "_wup_data"         → 915 字节 (内层登录请求 struct)
  "wupudbrequest_v0"  → 5 字节 = 02 00 31 9d f3  (版本2 + session INT32)

2.3 内层:_wup_data915 字节)登录请求 struct(完整字段映射)

_wup_data = STRUCT {
  field0 = STRUCT {                        # 元数据+风控
    tag0 = 0
    tag1 = "1.0"                           # 协议版本
    tag2 = 207B JSON (associationId/funcName/group/id/session/step/stillLogin/traceId/type/uid/userContext)
    tag3 = "5008"                          # bizAppid
    tag4 = 3 (INT8)                        # = type
    tag5 = 180B safedeviceid               # 设备指纹(base64)
    tag6 = ""
    tag7 = ""
    tag8 = 210B userAction JSON             # 风控行为轨迹
    tag9 = ""
  }
  field1 = STRUCT {                        # 设备基础信息
    tag0 = "ed0db8334cadd236c00cadf7e11ab5a5"  # hdid (32 hex)
    tag1 = "13.4.22"                       # App版本
    tag2 = "1.0.80138"                     # SDK版本
    tag3 = ""
    tag4 = "127.0.0.1"                     # 占位
    tag5 = "xiaomi"                        # 厂商
    tag6 = ""
  }
  field2 = STRUCT {                        # 设备详情
    tag0 = 1 (INT8)
    tag1 = "M2102J2SC"                     # 型号 thyme
    tag2 = "02df398797432eadefcc12767119ad5e80999389"  # 设备唯一ID(40 hex)
    tag3 = "android"
    tag4 = "M2102J2SC,30,11"               # 型号,SDK,版本
    tag6 = "1080"                          # 屏宽 (tag5缺失)
    tag7 = "2120"                          # 屏高
    tag8 = "7c5387e0539c023c31c4ff0e807e7256117385ee"  # 另一设备ID(40 hex)
  }
  field3 = STRUCT {                        # 登录凭证
    tag3 = "hy_300023887"                  # name (hy_虎牙号)
    tag4 = "772ed992b0e161276f44ec63671e60155c506294"  # password = SHA1(明文)
    tag5 = LIST["5008"]                    # appid列表
    tag6 = 1 (INT8)
    tag7 = {} (空map)
    tag8 = [] (空simplelist)
  }
}

2.4 原始可读字符串(完整)

TAF 序列化,可读字符串全量提取如下(关键字段用换行标出):

AAAD9xADLDxCADGd81YMaHV5YXVkYndlYnVpZg9oeXBhc3N3b3JkTG9naW59...   ← WUP 头
  └─ "huyaudbwebui"          ← servant
  └─ "hypasswordLogin"       ← func

_wup_data 字段(关联元数据 JSON:
  {"associationId":8193,
   "funcName":"hypasswordLogin",
   "group":1,
   "id":4097,                ← msgType
   "session":3251699,
   "step":1,
   "stillLogin":false,
   "traceId":"0b8f098ff64a5bdc-23473-94783833787595172451",
   "type":3,
   "uid":0,
   "userContext":""}

业务数据:
  5008                       ← bizAppid (appId)
  PQwemAN9NHkZKoMqVT...     ← safedeviceid (设备指纹, base64)

userAction JSON:
  {"curl":"登录页",
   "furl":"我的",
   "latitude":"-1.0",
   "longitude":"-1.0",
   "ssid":"",
   "user_action":[
     {"id":"24","time":"1787595171950","x":"277","y":"1057"},
     {"id":"11","time":"1787595172444","x":"296","y":"952"}]}

设备信息字段:
  ed0db8334cadd236c00cadf7e11ab5a5   ← hdid (硬件ID)
  13.4.22                            ← App 版本
  1.0.801386                         ← 某版本号
  127.0.0.1                          ← 回环地址(占位)
  xiaomi                             ← 厂商
  M2102J2SC                          ← 设备型号 (thyme)
  M2102J2SC,30,11                    ← 型号,SDK版本,版本
  android                            ← 系统
  1080 / 2120                        ← 分辨率
  7c5387e0539c023c31c4ff0e807e7256117385ee  ← 另一个设备ID

登录凭证:
  hy_300023887                       ← name
  772ed992b0e161276f44ec63671e60155c506294  ← password (SHA1)

结尾:
  wupudbrequest_v0                   ← WUP 请求类型标记
  AAFAgAxnfOMmAyoDA==               ← base64 尾巴(签名/校验)

三、WUP 请求的三层结构

┌──────────────────────────────────────────────────────┐
│ 第1层: WUP/TAF 外层                                  │
│   servant = "huyaudbwebui"                           │
│   func    = "hypasswordLogin"                        │
│   _wup_data = { associationId, funcName, group, id,  │
│                 session, step, stillLogin, traceId,  │
│                 type, uid, userContext }             │
├──────────────────────────────────────────────────────┤
│ 第2层: 业务数据 (AppLgnPasswordLoginReq)              │
│   bizAppid = 5008                                    │
│   name     = "hy_300023887"                          │
│   password = SHA1(明文)                              │
│   userAction = 风控行为 JSON                          │
│   safedeviceid / hdid / 设备信息                      │
├──────────────────────────────────────────────────────┤
│ 第3层: 网络传输                                      │
│   TLS 1.3 → 116.162.209.x:443 (udblgn.huya.com)     │
│   (自定义 BoringSSL / hyssl)                          │
└──────────────────────────────────────────────────────┘

四、网络层(实证)

  • 密码登录走 TLS 443(不是之前看到的 80 端口图片 CDN)
  • 目标 IP116.162.209.83-86udblgn.huya.comkunluncan CDN
  • TLS 层:libhycrypto.so = 定制 BoringSSLhyssl),符号已 strip
  • 连接池:App 启动时预建立,登录时复用
  • HTTP body = 原始 TAF 二进制(1015字节),NOT base64base64 是 hook 层返回值的表示形式,发送前已解码)

四·五、纯 Python 重放验证( 成功)

关键结论:纯 Python 可以复刻密码登录请求,服务端返回完整登录响应。

POST https://udblgn.huya.com/open/hy/passwordLogin
Content-Type: application/x-wup
Body: 原始 TAF 二进制 (1015字节, 非base64)

重放金标准请求(wup_passwordlogin_taf.bin)后,服务端返回 1514 字节登录响应

字段
uid 1199666914671
name hy_300023887
手机号掩码 001******7524
token 346 字节 base64(已存 evidence/huya_token.txt
uc_states 1(成功标记)

这证明:

  1. 请求格式 100% 正确(服务端无 BAD_REQUEST
  2. 设备指纹(safedeviceid/hdid)可复用(重放旧值仍通过)
  3. userAction 可复用(旧轨迹未被风控拒绝)
  4. TLS 指纹不阻断

注:此验证是「重放」,即复用已抓到的完整请求。要批量登录新账号,还需编码器动态构造新请求(替换 name/password/session/traceId/userAction)。


四·六、编码器 + 动态构造验证( 完整成功)

编码器已实现,逐字节复现金标准,且能动态构造全新请求。

  1. tools/huya_wup_encoder.py — 自检通过:用已知字段构造,输出与金标准逐字节一致(1015 字节)
  2. 动态构造测试 — 用新 session3251799)、新 traceId 构造全新请求(1009 字节),发送后返回:
字段
uid 1199666914671
name hy_300023887
手机号掩码 001******7524
token 全新 tokenXAQBRJ3H...

这证明 纯 Python 可以完整复刻虎牙密码登录

  1. 帧格式(JCE/WUP)逆向定稿
  2. 编码器逐字节复现
  3. session/traceId 可自由生成(服务端接受新值)
  4. 设备指纹可复用
  5. 动态构造请求返回有效登录响应(uid + token)

userAction 随机化( 已实测)

userAction 只是请求里的一个普通 JSON 字符串字段,编码器已暴露 user_action_json 参数。实测随机坐标 + 随机时间戳同样能通过登录(返回 uid + token),服务端对该字段不做严格校验。

编码器提供辅助函数:

  • make_user_action(now_ms=None) — 生成随机 userAction JSON(坐标/时间戳/点击间隔随机)
  • make_trace_id(pid=0) — 生成 traceId

未实测定论:批量高频登录时,服务端是否按账号/设备/IP 维度限流或风控。这需要实测才能确认,与 userAction 无关。


五、对「纯 Python 复刻」的启示(已实证更新)

TLS 指纹已实测:不阻断。 普通 Python urllib 直连 udblgn.huya.com 能正常收到业务响应。

要脱离 App 用 Python 发密码登录请求,需要:

  1. 密码sha1(明文) —— 已破解,纯 Python 可做
  2. namehy_虎牙号 —— 已知
  3. servant/funchuyaudbwebui / hypasswordLogin —— 已知
  4. WUP 帧格式:标准 JCE RequestPacket,已 100% 定稿(见第二节)
  5. Content-Typeapplication/x-wupbody = 原始 TAF 二进制
  6. 重放验证:服务端返回 uid + token(纯 Python 可行)
  7. TAF/JCE 编码器tools/huya_wup_encoder.py(自检通过,逐字节复现)
  8. _wup_data 元数据session/traceId 可自由生成(服务端接受新值)
  9. 设备指纹(safedeviceid/hdid:可复用旧值(缓存一次即可)
  10. userAction:随机坐标/时间戳即可通过(编码器内置 make_user_action()

唯一待实测项:批量高频登录时的账号/设备/IP 维度限流(与 userAction 无关)。


六、关联证据文件

  • huya-wup-plain.jsonl — 第一次明文抓包(sendMsg JSON 层,305 事件)
  • huya-wup-full3.jsonl — 完整 WUP 序列化(含本次密码登录 1356 字节)
  • wup_passwordlogin_taf.bin — 金标准:1015 字节 TAF 二进制(= HTTP body,编码器自检依赖)
  • wup_passwordlogin_base64.bin — 金标准:1356 字节 base64hook 返回值)
  • wup_response_badreq.bin — 服务端响应帧(383 字节,returnCode=104
  • tools/huya_wup_encoder.py — WUP 帧编码器(自检通过,逐字节复现金标准,含 make_user_action/make_trace_id
  • hook 脚本:hook_huya_wup_full.js
  • runnerrun_huya_netcapture.py

含有效登录 token 的响应文件(wup_response_login.binwup_response_new_session.binhuya_token.txt)已加入 .gitignore,不提交。


七、结论与下一步

已完成并验证:

  1. TLS 指纹:实测不阻断(Python urllib 正常收到业务响应)
  2. TAF 编码器tools/huya_wup_encoder.py,逐字节复现金标准
  3. 纯 Python 登录:重放 + 动态构造均返回 uid + token
  4. userAction 随机化:随机坐标/时间戳通过

待实测(唯一未定论项):

  1. 批量高频登录的限流/风控:需用编码器连续发请求,观察账号/设备/IP 维度是否触发限制(错误码、频率阈值)
  2. 集成进 core/huya/login.py:把 APP 密码登录(/open/hy/passwordLogin)作为 Web 登录被风控阻断时的替代路径