189 lines
10 KiB
Markdown
189 lines
10 KiB
Markdown
# 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 来源定位
|
||
|
||
增强 hook(bean 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)。
|
||
|
||
## ★ 终局验证(全链路)
|
||
|
||
活体采集(桥调getCred,零重登) -> hyCred(CiAXHUiF.../114B)
|
||
-> cert_forge 铸新证书 -> bindQrLoginUser stage=2
|
||
-> tryQrLogin uid=1199666914671 + biztoken344B ✅
|
||
多账号流程定型: 每号设备登录一次+桥采cred入库, 此后永久全自动。
|
||
纯Python长连接签发客户端(0x1001+JSON)留作后续可选优化, 参数已全部备齐。
|
||
|
||
## ★ 纯Python路线启动(用户需求: 密码进cookie出, 零手动)
|
||
|
||
wsapi信封实测(getDfpConfig 67B): [len][10 03][,LV]["huyaudbwebui"]["getDfpConfig"][}][params]
|
||
rodata: wupudbrequest_v0 与 huyaudbwebui 同池 => 任意udb消息HTTPS透传func名。
|
||
实施: tools/huya_passport_client.py
|
||
step1 复刻信封(huya_wup_encoder同构)
|
||
step2 内层帧: id=0x1001+JSON, 盲发试错读错误码定位格式
|
||
step3 响应解JSON取cred -> cert_forge -> bind -> cookie
|
||
|
||
## 纯Python路线·传输层修正
|
||
|
||
登录6.har实证: 护照登录走 CONNECT https://wup.huya.com TLS隧道(代理不可见),
|
||
wsapi/wupudbrequest_v0 仅用于dfp等短连流程。
|
||
内层帧突破口: createWupPackage(wup::UniPacket)@0x274ea4
|
||
- 函数体内引用 wupudbrequest_v0 与 huyaudbwebui
|
||
下步: 反汇编该函数+其调用链(经PLT), 提取长连接帧格式(msgId+JSON的JCE摆放),
|
||
然后 Python直连 wup.huya.com:443 组 TAF UniPacket 发送取cred。
|