- 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辅助函数 - 新增证据金标准 + 协议文档
9.7 KiB
虎牙 App 登录逆向笔记
抓包来源:
/Users/yml/Desktop/抓包/hy/8.23/(登录-1.har / 登录-5.har) 分析工具:tools/taf_decode.py(TAF/JCE 纯字节流解码)、tools/hy_har_parse.py(HAR 批量解析) 状态:App 登录前置风控链路已完整解明;登录提交(最后一跳)走 cdn.wup.huya.com 长连接 + SSL Pinning,尚未解出明文。
一、结论速览
-
App 登录 ≠ Web 登录,是两套完全不同的协议栈:
- Web 登录(旧,appId=5002):
udblgn.huya.com/web/v2/passwordLogin+udbrtt.huya.com/auth/pt/*滑块 +df.huya.com/web/df/token|collect。已在core/huya/login.py、e语言/huya/登录/虎牙操作.txt实现。 - App 登录(新,appId=5008):
wsapi.huya.com(servant=huyaudbwebui)走getDfpConfig → selectOperator → dfpReport设备指纹,udbdf.huya.com/dckey/check拿 dckey,运营商取号,最后登录提交进cdn.wup.huya.com长连接。
- Web 登录(旧,appId=5002):
-
Web 登录"风控加强"的本质:Web 纯脚本无法伪造 App 侧的设备态(dckey + dfp 指纹 + appId=5008),被风控拦下。登录接口本身没变,是前置设备校验变严。
-
抓包抓不到登录的原因(两层锁):
- TLS 层:
cdn.wup.huya.com与裸 IP(211.91.243.102等)有 SSL Pinning,Reqable 中间人证书被拒,只留 CONNECT 隧道。 - 应用层:
statwup.huya.com的部分 body 是877de5c4...纯二进制(应用层加密),即使破 TLS 也还要再解一层。
- TLS 层:
二、App 登录完整链路(已解明的普通 HTTP 部分)
按时间顺序(登录-5.har):
① 数美安全 SDK 初始化(App 启动)
cfgc.zztfly.com/v6/gcf · cdn-api-auth.zztfly.com/api/initSecCdn · upc.flysleep.cn/v6/gcl
② 设备指纹配置
wsapi.huya.com huyaudbwebui.getDfpConfig ← 只传 appId=5008,返回 80 字节加密配置
③ 选运营商(一键登录前置)
wsapi.huya.com huyaudbwebui.selectOperator ← 传设备信息 + RSA 加密段,返回运营商索引
④ 设备指纹上报 ×4(循环)
wsapi.huya.com huyaudbwebui.dfpReport ← 上报加密设备指纹,返回 32 位指纹 + action
udbdf.huya.com/dckey/check ← 加密 body,返回 dckey
⑤ 运营商取号(联通)
auth.wosms.cn/dro/netm/v1.0/qc
nisportal.10010.com:9001/api?appid=2074469246116
⑥ 登录提交(❌ 未抓取)
→ 进入 cdn.wup.huya.com / 裸 IP 长连接(被 SSL Pinning 挡住)
wup.huya.com 上看到的 mobileui.getDynamicConfigHotFixUpdateInfo / reportMobileUpdateResult 只是 App 热更配置,与登录无关。statwup.huya.com 上 metric.reportDetailV2 是埋点。
三、各接口字段级结构
1. getDfpConfig(wsapi,servant=huyaudbwebui)
- 请求 tReq:
{"t0": {"t0": "5008"}}—— 只传 appId。 - 响应 tRsp:
{"t0": {"t0": {"5008": "<base64>"}}}—— 按 appId 返回配置。 - base64 配置解码后 = 80 字节(64+16 结构,疑似密钥材料):
hex: 34b946b716f1481d8584980b79cf756b962e79c999d752016904c1fc07d689
694a148ef872206826745498088e61be3f062f7d8d1a0276e29c0048062ac8e2
b58fe0d1ee376c412472d8b9124d0bbf7d
2. selectOperator(wsapi,servant=huyaudbwebui)
请求 key 是 _wup_data(注意不是 tReq),三层嵌套结构:
_wup_data.t0.t0 = { # 会话上下文
t0: 0,
t1: "1.0", # 协议版本
t2: "hyudb_1787583730402", # 会话 id = "hyudb_" + 毫秒时间戳
t3: "5008", # appId
t4: 0,
t5: "PQwemAN9NHkZKoMqV...", # RSA 公钥加密段(与 dfpReport 响应 t2 同前缀)
t6:"", t7:"", t8:"", t9:""
}
_wup_data.t1 = { # SDK 信息
t0:"", t1:"13.4.22"(App版本), t2:"2.0.8"(SDK版本), t3:"",t4:"",t5:"",t6:""
}
_wup_data.t2 = { # 设备信息
t0: 1,
t1: "M2102J2SC", # 机型
t2: "02df398797432eadefcc12767119ad5e80999389", # 40 hex = SHA1 设备指纹
t3: "android",
t4: "M2102J2SC,30,11", # 机型,Android版本,SDK版本
t6:"", t7:""
}
_wup_data.t3 = [0, 1, 3] # 运营商列表
- 响应:
_wup_data.t0.t1 = 1(选中运营商索引 1)。
3. dfpReport(wsapi,servant=huyaudbwebui)
- 请求 tReq:
{t0: {t0:"", t1:"android", t2:"<加密体>", t3:"", t4:12}}
-
t2加密体结构:10 字节固定魔数 + 变长加密数据 -
魔数(跨 6 次请求完全一致):
571882cf664bb39401ee -
加密数据长度 3300~4100 字节(每次递增,说明是累积式采集上报,每次追加新采集数据)
-
响应 tRsp:
{t0: {
t0: "10",
t1: "865a4924a40897ac1fcfe6b4c2cbb045", # 32 hex = 稳定设备指纹(4 次响应一致)
t2: "PQwemAN9NHkZKoMqV...", # base64 = RSA 公钥加密的 action(每次变)
t3: 43200, # 过期时间(秒)=12h
t4: "action",
t5: "7c5387e0539c023c31c4ff0e807e7256117385ee" # 16 hex = 稳定 session hash(4 次一致)
}}
4. dckey/check(udbdf.huya.com,okhttp/3.14.9,application/octet-stream)
- 请求 body 结构:100 字节固定头 + 变长加密体(总长 543B 或 547B,两种长度差 4 字节)
- 100 字节固定头(跨 5 个样本、两个会话完全一致):
32f1990eb8f46c503c3b5db81be58ae84dd3e961b991dd9e9c593aba8c78881c
f1281c555198710a1009f1ec897c46be8b26d81e8eab98f27242de3444315cab8
d617eae189d597e296099f0e5bc817a7554456c1af99515254ab7665a2a3f861a
7d941b
-
变长加密体(543/547B 减 100B = 443/447B)每次变化。
-
同一长度下,末尾 32 字节可复现(同设备+同 session 重复调用时)。
-
响应:
{"dckey":"AAAAAMC1eP4iV43WYoI57ZOu0...","status":"success"}- dckey 固定前缀
AAAAAMC1eP4iV43WYoI57ZOu0(base64 前 16 字节),后续每次变。
- dckey 固定前缀
5. 运营商取号(联通一键登录)
POST auth.wosms.cn/dro/netm/v1.0/qcPOST nisportal.10010.com:9001/api?appid=2074469246116&ret_url=<base64>
四、加密体分析汇总
| 加密体 | 位置 | 结构 | 猜测算法 |
|---|---|---|---|
| dfpReport t2 | wsapi | 10B 魔数 571882cf664bb39401ee + 变长加密 |
自定义格式,魔数疑似协议版本/类型标识 |
| dckey/check body | udbdf | 100B 固定头 + 443/447B 变长 | 固定头疑似 RSA/ECDH 公钥或设备态加密快照 |
| getDfpConfig 配置 | wsapi 响应 | 80B = 64B+16B | 疑似 dfpReport 加密密钥 + IV/nonce |
| selectOperator t5 / dfpReport t2(action) | wsapi | base64,同前缀 PQwemAN9NHkZKoMqV |
RSA 公钥加密(同公钥 → 密文前缀相同) |
| statwup #7 | statwup | 400B 纯二进制 | 应用层加密(session-key) |
关键判断:selectOperator.t5 和 dfpReport 响应 t2 前缀相同 → 同一个 RSA 公钥加密,明文大概率是「设备信息 + 会话 key」的协商包。拿到这个公钥(APK 内嵌)就能解密。
五、稳定字段清单(可直接复用的常量)
| 字段 | 值 | 用途 |
|---|---|---|
| appId(App 场景) | 5008 |
替代 web 的 5002 |
| App 版本 | 13.4.22 |
versionCode=115315 |
| SDK 版本 | 2.0.8 |
selectOperator.t1.t2 |
| dfpReport 魔数 | 571882cf664bb39401ee |
t2 加密体前缀 |
| dckey 固定头 | 100B(见上) | dckey/check body 前缀 |
| dckey 响应前缀 | AAAAAMC1eP4iV43WYoI57ZOu0 |
dckey 前 16B |
| 设备指纹(selectOperator) | 02df398797432eadefcc12767119ad5e80999389 |
40 hex = SHA1 |
| 设备指纹(dfpReport) | 865a4924a40897ac1fcfe6b4c2cbb045 |
32 hex,服务端下发 |
| session hash(dfpReport) | 7c5387e0539c023c31c4ff0e807e7256117385ee |
t5 |
| 会话 id 格式 | hyudb_<毫秒时间戳> |
selectOperator.t2 |
| appSign | 60576904(md5 取前 8,见 login.py 注释) |
App 场景签名 |
六、与现有 Web 登录(appId=5002)的差异
| 维度 | Web(5002) | App(5008) |
|---|---|---|
| 登录入口 | udblgn.huya.com/web/v2/passwordLogin | cdn.wup.huya.com 长连接 |
| 设备指纹 | df.huya.com/web/df/token+collect | wsapi getDfpConfig+dfpReport + dckey/check |
| 验证码 | udbrtt pt/get3+verify3(滑块/点选) | 运营商取号 + (未解明) |
| 传输 | JSON over HTTP | TAF/WUP + 部分应用层加密 |
七、待破解项(按优先级)
- [最硬] dckey/check 的加密算法:100B 固定头 + 变长体。需 hook APK 里 dckey 生成方法。
- [核心] dfpReport t2 加密体:魔数 + 累积式采集数据。需 hook dfp 采集模块。
- [核心] 登录提交的 WUP servant/func + 参数:被 SSL Pinning 挡住,需 Frida unpin 或 hook 登录方法。
- [可解] RSA 公钥:APK 内嵌,解出 selectOperator.t5 / dfpReport action 明文。
- [可解] getDfpConfig 80B 配置:确定是否 dfpReport 加密密钥。
八、Frida hook 点建议(供 hook 阶段参考)
- dckey 生成:hook
udbdf请求发出前,dump 加密前明文。搜com/duowan/kiwi下 dckey / dcKey 相关类。 - dfp 采集:hook dfpReport 的 t2 字节流写入点(
writeTo/toByteArray)。 - 登录提交:hook
com.duowan.kiwi.base.login.UdbNetHelper(appSign 已逆向自此类),以及登录 servant 的writeStruct("tReq", ...)前的参数。 - SSL unpin:hook OkHttp
CertificatePinner.check+ conscryptTrustManagerImpl.verifyChain,让 Reqable 能解 cdn.wup.huya.com。
九、工具使用
# 解码某个 HAR 里的所有 WUP(TAF) 请求/响应字段
.venv/bin/python tools/hy_har_parse.py "/Users/yml/Desktop/抓包/hy/8.23/登录-5.har"
# 底层 TAF 解码器(可单独 import)
from tools.taf_decode import Taf, decode_struct_bytes