Files
live-hub-py/docs/虎牙hyCred签发-侦察笔记.md
T

163 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# hyCred 签发协议 — 侦察笔记(2026-08-25 傍晚)
> 目标:任意账号取得有效 hyCred(114B),配合 tools/cert_forge 实现全自动多账号。
> 当前状态:**铸证已闭环**(见《虎牙证书算法闭环-差分实证.md》),本文记录签发链侦察结果。
## 已确认事实
1. **cred 是长寿命凭证**:早上抓的 hyCred 在下午(且中间用户重新登录过一次后)
依然能通过 bind 铸证(实验C)。=> 每账号只需采集一次,非会话级短时效。
2. **cred 每次登录轮换值但旧的仍有效**:今晨 `0a80a762...` ≠ 午后 `0a50ec12...`,两者均可用。
3. **密码登录响应不含 cred**`evidence/wup_response_login.bin`(1514B) 无 114B/152b64 结构。
4. **cred 存于本地登录态文件**`BusinessCfg::saveLoginData` (@0x265fb0 / 0x265a6c)
-> JSON(含"cred"字段) -> UdbAESUtil AES 加密 -> `UdbFileUtil::writeFileEx` 写盘。
`BusinessCfg::getCred(uid)` @0x265524 纯本地读(UdbLock+日志),无网络。
5. **签发走 cdn.wup.huya.com 长连接隧道**HAR 里只有 CONNECT,载荷不可见;
相关消息族符号齐全:
- `MsgRequestLgnCred` / `MsgResponseLgnCred`(请求/响应)
- `HandlerRequestLoginCred`(@0x3a1820) / `HandlerResponseLoginCred`(@0x3a182c)
- 已知方法: `handlerCredLoginOverTimeMsg` @0x3a00d0
- `UdbObjCreator_Msg{Request,Response}LgnCred``AppLgnCredentialLoginReq/Resp`(wup JCE)
- `MsgGetCred`/`HandlerGetCred`、Anony 变体、`MsgResponseUpdateCred`
6. **dckey/check ≠ hyCred**udbdf.huya.com/dckey/check 返回设备钥 dckey
(固定前缀 AAAAAMC1eP4iV43WYoI57ZOu0),与账号 cred 不同物。
## 本次抓取(evidence/cred_issue_trace.json
用户重登时捕获 saveLoginData bean 内存头:
`..."hy_300023887"...len前缀"1199666914671"..."4C5fozan"...`
bean 为 BusBeansLoginData 结构体,cred 字段在更深偏移,待下次 dump 全量)
vtable 探测结论:slot2/3 是析构/通用槽(打中 MsgLoop::doMSG/sendMessage),
真正 onHandler 未命中 —— 下次应挂 `ResponseMsgHandler` 派生 onHandler 或
直接挂 `hyudb_packet_util::cred_pack/cred_unpack`(@0x333170) 收发两端。
## 三条候选路线(按性价比)
| 路线 | 做法 | 成本 | 备注 |
|---|---|---|---|
| A 混合采集(立即可用) | 手机登目标号一次,Frida 自动抓 cred 入库;此后该号永久全自动铸证 | 低 | 已验证可行,脚本齐备 |
| B wsapi 短连接复刻 | 若 MsgRequestLgnCred 可经 huyaudbwebui servant 走 wsapi.huya.com HTTPS | 中 | 需 msgId+JCE 结构+OTP加密链(已有) 组包试验 |
| C 长连接完整复刻 | 实现 cdn.wup.huya.com WSP 握手/心跳/加密隧道 | 高 | 最后手段 |
## 下次继续的抓手
1. 重跑 hook_cred_issue.py 增强版:saveLD_bean dump 640B 定位 cred 字段偏移 +
backtrace 10 帧(找推送来源 handler+ 挂 `cred_unpack`@0x333170 抓网络侧明文。
2. 静态:`MsgRequestLgnCred::getClassName`@0x2a37d0 / 各消息 getClassName 取类名串,
在 rodata 找 类名→msgId 表(参考 MsgGetH5InfoEx=184549387 的命名规律)。
3. 若走 B:用 tools/huya_wup_encoder.py 的信封 + OTP 加密链组 RequestLgnCred 发 wsapi 试探。
## ★ 2026-08-25 晚 · 关键突破:cred 来源定位
增强 hookbean 768B 全量 + backtrace)在用户重登时抓到决定性调用链:
```
BusinessCfg::saveLoginData ← _Z13saveLoginDataiR17BusBeansLoginData
← HandlerResponseLoginPassport ★
← udbjce::JceInputStream (wup/JCE 反序列化)
```
- **cred 随"护照登录"响应一次性下发**BusBeansLoginData 内),不是单独消息。
- 手机 App 的登录主通道是长连接上的 LoginPassport;我们的 HTTPS
`/open/hy/passwordLogin` 是简化通道,响应(1514B)只有 uid/name/mask/token
无 cred(已二次扫描确认:无 `0a 80` 高熵段、无 114B 结构)。
- bean 内存可见字段样例: "hy_300023887"、uid串、"01******7524"掩码、
user_action JSON 片段 —— 与 BusBeansLoginData 字段对应。
- 证据: /tmp/credissue2.log + evidence/cred_issue_trace.json(第二次覆盖保存)
### 路线修正
- 路线 B 修正为「补全 HTTPS passwordLogin 的请求字段」:App 端若也存在
HTTPS 登录路径,其请求参数比我们的最小实现多 ⇒ 响应可能就含 cred。
下一步:静态对比 HandlerRequestLoginPassport 组包 vs huya_wup_encoder 差异;
或 mitmproxy 抓一次 App 真实密码登录流量对照。
- 若确认必须长连接 → 路线 C(WSP 客户端)或路线 A(混合采集)兜底。
## ★ B1 静态第一铲(rodata 字符串集群 @0x1c36db
登录态 JSON 字段名: appId / sdkVer / cred / timestamp / biztoken_vec
新发现 24 字符密钥: ViDjMAKmkzvYEUFmrMdk9YPd (@0x1c3704, 疑似登录态文件AES钥或密钥表第4条)
消息类名串: MsgRequestLgnCred(@0x1c385a) / hyAppOtpLogin / LgnOtpLogin
下一步: 反汇编 BusBeansRequestLoginPassport::toString(JsonUtil)@0x2a6118 提取请求全字段,
对照 huya_wup_encoder 补齐 → 试发 HTTPS 端点看响应是否带 cred。
## ★ BusBeansLoginData 完整字段表(B1 第二铲)
toString 委托链: LoginData(0x26ca64) -> BusBeansAppLoginData::toString(0x26d518)
AppLoginData 字段(JCE 序列):
regOrigin userIdState uid hyid passport cred cookie mobileMask
emailMask userId token timestamp subUid hyOpenId isHuya status
biztoken_vec thirdParams
=> 护照登录响应一次含 cred/cookie(biztoken)/token; 铸证所需全在响应内。
剩余: RequestLoginPassport 六字段的 JCE tag 号 + 传输通道(wsapi or 长连接)。
## ★ Round2: 载荷格式=JSON(非二进制JCE)!
UdbRequestMsg<RequestLoginPassport>::unPackageMsg(@0x2a57cc) =
UdbContext::unPackageContext(str) -> JsonUtil::loadFromString
=> 消息上下文为JSON字符串, 字段名即线上的键:
请求: {name,password,userAction,isAuthLogin,lgnExtParam,bizAppids}
响应bean: {regOrigin,...,cred,cookie,token,...,biztoken_vec}
剩余: msgId定位 + servant/endpoint映射(huyaudbwebui?) + 密码格式(sha1?)
## Round3: msgId 线索
类名串引用点在 UdbObjCreator_Msg*::C2 注册构造器内:
- "MsgRequestLoginPassport" 邻近 movz #0x1001
- "MsgResponseLoginPassport" 邻近 movz #0x2001
=> 疑似 (方向类型<<12)|序号 或注册type常量; 完整id需反汇编
UdbObjCreator_MsgInit::C2 的 registerMsg(id,className,creator) 调用序列。
已知参照: MsgGetH5InfoEx=184549387(0x0B00000B)。
## Round4: 注册常量=0x20
Request/Response LoginPassport 两个 Creator::C2 均以 movz w0,#0x20 构造
(UdbClassFactory), 地址 0x289b28/0x289c2c。0x20 与证书 key_idx 字节(0x20)同值!
=> 猜测: 0x20 是该消息族的类型号, 线上完整 msgId 需结合工厂注册表;
或 key_idx 本就是消息族类型号的镜像(服务端按它选处理分支/密钥)。
捷径备选: 设备hook UdbMsgHandler::sendMessage 抓真实线上id(需用户重登一次)。
## ★ Round5: msgId 实锤
@0x289b48: mov w1,#0x1001; x2="MsgRequestLoginPassport";
bl UdbMsgFactory::RegisterMsg(uint, char const*)
=> MsgRequestLoginPassport id=0x1001(4097), Response=0x2001。
(前轮0x20为operator new大小, 红鲱鱼)
剩余: 传输端点+servant、password编码、活体发送测试。
## Round6: 传输通道确认
servant 字符串 huyaudbwebui 与 Handler 族类名同池出现在 libudbauthunify.so
(另见 wupudbrequest_v0 / KFWAH2cbkkNgFAgQPzriFPcU 等邻串)。
=> MsgRequestLoginPassport(0x1001) 经 huyaudbwebui servant 走 WUP;
dfp 的 func 名不在本库(getDfpConfig 计数=0), func 名或由Java层/运行时拼接。
下一步(需用户重登一次): hook UdbMsgHandler::sendMessage 抓真实线上包
(id/servant/func/JSON载荷), 一次拿全所有待定参数。
## Round7: sendMessage(0x1001) 实锤触发!
hook UdbMsgHandler::sendMessage 双重载: 登录时 id=0x1001 出现2次
(另有 0x0B000003/0x21/0x25 长连接族 + 0x102b/0x1072 等)。
wire id 双体系: 0x0Bxxxxxx=业务推送族; udb请求用裸id(0x1001...)。
payload读取bug: 参数是char*非std::string, 下轮改readCString重抓即得完整JSON包。
证据: evidence/cred_issue_trace.json (sendmsg:260 domsg:17)
## ★★ Round8: LoginPassport 完整请求包到手!
sendMessage(0x1001, json) 载荷(char*直读):
{"bizAppids":[],"isAuthLogin":false,"lgnExtParam":{},
"name":"hy_300023887","password":"<sha1hex40>","userAction":"{...}"}
- password=SHA1 hex(40) 与 tools/huya_password_sha1 一致
- 六字段与静态预测完全吻合; 空对象/空数组原样传
剩余: 响应侧(doMSG a2偏移待修, cred响应未读到) + 信封包装层。
证据: evidence/cred_issue_trace.json
## Round9: bean为指针对象, 改挂getCred出口
saveLoginData bean(1536B dump)=C++对象, cred在堆上经指针引用, 非内联。
最直接抓点: BusinessCfg::getCred(ulong,string&out)@0x265524
-> onLeave 读 out(x2) std::string 即152字符hyCred原文。
scripts/hook_beandump.py 已建(时序模板可复用), 下轮改钩子地址重跑一次登录即得。
另: 响应JSON键已确认含"cred"键(0x1c36e8)。