feat(huya): 纯Python密码->cred->铸证->bind全链路贯通! 零设备闭环达成
This commit is contained in:
@@ -304,3 +304,15 @@ cred字节1轮换: 0x10/0x20/0x30/0x40/0x50/0x70/0x80(观测值)=代次计数器
|
|||||||
或响应cred需会话上下文解密/兑换。
|
或响应cred需会话上下文解密/兑换。
|
||||||
下一步: hook getCred的调用链回溯(谁写入该值), 或登录后逐func试调
|
下一步: hook getCred的调用链回溯(谁写入该值), 或登录后逐func试调
|
||||||
(hyanonymousCredlogin/getAppComomData)观察cred0文件变化。
|
(hyanonymousCredlogin/getAppComomData)观察cred0文件变化。
|
||||||
|
|
||||||
|
## ★★★★ 终局: 纯Python全链路贯通(零设备)!
|
||||||
|
|
||||||
|
完整闭环实测通过:
|
||||||
|
密码aa778899 -> SHA1 -> build_password_login_wup(hypasswordLogin)
|
||||||
|
-> POST wup.huya.com(raw TAF) -> HTTP200
|
||||||
|
-> 响应t3=新鲜cred(114B,0a开头,每次登录轮换)
|
||||||
|
-> cert_forge(新nonce+key_idx0x20) -> 证书194B
|
||||||
|
-> bindQrLoginUser -> ok:true uid=1199666914671 biztoken344B ✅
|
||||||
|
关键教训: 40020拒绝系旧wupData信封过期(session/nonce TTL), 与cred无关!
|
||||||
|
信封需新鲜抓取(keycap钩子桥触发即可)。
|
||||||
|
多账号运营: 账号密码即全部所需, 全自动出cookie。
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"results": {
|
"results": {
|
||||||
"C_forged_swapped_cred": {
|
"C_forged_swapped_cred": {
|
||||||
"bind_fail": true,
|
"ok": true,
|
||||||
"rc": 40020,
|
"uid": 1199666914671,
|
||||||
"desc": "登录失败,请重新登录",
|
"biztoken_len": 344,
|
||||||
"scan_uid": null
|
"biztoken": "AQANuXdRNaWljTvYRXSvqEPiEY-fH82GLpdxZLAQb0TbqRtehX-V6LZb89mudbyUAvppOSdR7e7otDkx049MDjPDZ9bIW8g8Yje5ZbbSiLFGHD3ir3Co3ZqJFRnZMgaH-spHmOdKEnPmzEtFAJshKxYOPVXfFMDatNrbMdvcPN_JOYtMMIy0cKwon7MD88xa40VzM39PW3NDIVZ9uHAixogdKfNd4QCGs4jvbCXHV0OQTIQ_7vslqcJm7dhCWhbUzoQkjUu-8BfrDC2hxrEyxF7CCiBhNE8NbWmjPhFrXx9pAnQXGvaQka34zPeS0VGKTrSHhwUP454dRt0i6yoHccF2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"p1_new_hex": "010400353030381400c25220f81f60cf64ee54e83ef1eab6bdd047b5af28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a20171d4885bb9bb40f29517ab490f3634a0640e942e24039e2f471a5a66a0c64662b68c43f64a90ab982d8298bfc5de4e58a0bb6617a7ab8ecc730cde4a28bd828261096ca95b6247bba5e4714395166f9a22eb0c9954e6d0890ba40fa951e70e63c7c4c8e8e5547f6ca029830f31adbe8",
|
"p1_new_hex": "010400353030381400fd951db27f62fad1580eb969f4e921146552ba3e28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a00469c0c8de6126519e9fe14cc34c908f32bb6e41db93f62c40c8cc806d03d08aeaa5554afa9b0a539875ad7920ea095dd210a2439428d962b40126fc723e777342ba3c58507bd5369e597e4a04c48d580cc1e6cd03a47415e91b562637742446f3a989a8c6464e2e45db4b949464773d1",
|
||||||
"ts": 1787655679.05882
|
"ts": 1787656336.623034
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"p1": "01040035303038140001a0389d70ed0000389d70ed000000000000000028003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a20171d4885bb9bb40f29517ab490f3634a0640e942e24039e2f471a5a66a0c64662b68c43f64a90ab982d8298bfc5de4e58a0bb6617a7ab8ecc730cde4a28bd828261096ca95b6247bba5e4714395166f9a22eb0c9954e6d0890ba40fa951e70e63c7c4c8e8e5547f6ca029830f31adbe8", "cert_hex": "0c2041da0f8b2bc93536f1311d868d6307d3fffdd94452044039d3fa05b8a86f3c2e2cbaa481870772fe2a4df9a94fdb8a0e41312514470c77cbfa586558f87309bea9c2abc3b6948072d9c345292866c8517e60cc7cd93f2c2bb82f8d8a255da88418e86fd21309d9306024a8006b99540c58054f823c7d8924b56f841c8dbb10d093ddf859b21625ba8bb1a34ab8b03d27b5a3ed92a67750464225ba8b9be3cd5c359369ef783b7f4487614e65bc04a159b4e75bd74c62f9cb9755d765ab007748", "cred_hex": "0a20171d4885bb9bb40f29517ab490f3634a0640e942e24039e2f471a5a66a0c64662b68c43f64a90ab982d8298bfc5de4e58a0bb6617a7ab8ecc730cde4a28bd828261096ca95b6247bba5e4714395166f9a22eb0c9954e6d0890ba40fa951e70e63c7c4c8e8e5547f6ca029830f31adbe8"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"p1": "01040035303038140001a0389eef670000389eef67000000000000000028003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a00469c0c8de6126519e9fe14cc34c908f32bb6e41db93f62c40c8cc806d03d08aeaa5554afa9b0a539875ad7920ea095dd210a2439428d962b40126fc723e777342ba3c58507bd5369e597e4a04c48d580cc1e6cd03a47415e91b562637742446f3a989a8c6464e2e45db4b949464773d1", "cert_hex": "0c208d60b6dab6c2d064acb305941347495a8cc61895fe1ad0104cf808656c93d5952cbaa481870772fe2a4df9a94fdb8a0e41312514470c77cbfa586558f87309be9ed66e896f0b46f990dd1db3824813b4409d86ee67d27d43228cdd3b0799e1836a02a37ecd738c16185a10a30f118f3eab361dd1b3181fedf7475e8bf08bead17991f9eb8e0b80101edce6d907ad18cac76ed625e3d1265922d51cbca5943b45cc73902e032e97e59bb95c9d3bffca924cf75b7edb5d416785ae584a545b8c43", "cred_hex": "0a00469c0c8de6126519e9fe14cc34c908f32bb6e41db93f62c40c8cc806d03d08aeaa5554afa9b0a539875ad7920ea095dd210a2439428d962b40126fc723e777342ba3c58507bd5369e597e4a04c48d580cc1e6cd03a47415e91b562637742446f3a989a8c6464e2e45db4b949464773d1"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"p1": "01040035303038140001a0389cc2250000389cc225000000000000000028003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a808e09a2e5608424bd688424632801ebac39dd611376320e9ed2344ce7cbc18de5bcda6b95d115068155dbf5049292f51e6ffd7ace0cb142529e8f408dc4cd071fa80b783f53689a7859af47aa7cb5b817ca730a3b1e110a4d5f99bc5b34f7ca066a8cb7b48c792db29b722ab00364b49c", "cert_hex": "0c20c2e236fe8fb79392d1736affdc382200289c3ff342cc568c667a760cf5499d0c2cbaa481870772fe2a4df9a94fdb8a0e41312514470c77cbfa586558f87309be579c689d1ee8898e16cc0292014ccb6db32a6b5b00b6c8056d8548ff248bcaff3d039c11b71dd22ccfe2ac7b130055e9eba29b86019c5da0b5c71b7f51a882f6c626099601539b2d53170f1aa799dbf9a932911ac7f9f9e082c380f060afd80eb9ee8c2ffcb54dc9b56d265192cce497751cf1dc22d6d5fee107d7dcc6b660f2", "cred_hex": "0a808e09a2e5608424bd688424632801ebac39dd611376320e9ed2344ce7cbc18de5bcda6b95d115068155dbf5049292f51e6ffd7ace0cb142529e8f408dc4cd071fa80b783f53689a7859af47aa7cb5b817ca730a3b1e110a4d5f99bc5b34f7ca066a8cb7b48c792db29b722ab00364b49c"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[{"type": "armed"}, {"type": "wfex", "off": 2433620, "name": "credAnonymous", "len": -1, "v": ""}, {"type": "wfex", "off": 2433952, "name": "credAnonymous", "len": 27, "v": "487579615564623139323833373436353071776572747975696f70"}, {"type": "wfex", "off": 2433620, "name": "cred0", "len": -1, "v": ""}, {"type": "wfex", "off": 2433952, "name": "cred0", "len": 27, "v": "487579615564623139323833373436353071776572747975696f70"}, {"type": "wfex", "off": 2433620, "name": "UDB_SHAREAPP_DATA", "len": 58, "v": "39756e2f665054634a7155385559366b6c706f45314d636a5878563678484e3362394d335653736c746e6f6334763376346c37716e384e796572"}, {"type": "wfex", "off": 2433952, "name": "UDB_SHAREAPP_DATA", "len": 27, "v": "487579615564623139323833373436353071776572747975696f70"}]
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""抓 hyCred 签发链: 挂 saveLoginData(落盘) + LoginCred handler(vtable探测)。
|
||||||
|
|
||||||
|
用法: 脚本跑起来后, 在手机上 手动退出登录 -> 重新登录 一次。
|
||||||
|
捕获: 登录态JSON明文(含cred字段) / saveLoginData 入参 / LoginCred handler 触发。
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import frida
|
||||||
|
|
||||||
|
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||||
|
|
||||||
|
JS = r"""var base = Process.getModuleByName('libudbauthunify.so').base;
|
||||||
|
send({type:'armed'});
|
||||||
|
function hx(p,n){ try{ return Array.from(new Uint8Array(p.readByteArray(n))).map(x=>('0'+x.toString(16)).slice(-2)).join(''); }catch(e){ return 'ERR'; } }
|
||||||
|
function rdStr(p){ try{
|
||||||
|
var b0=p.readU8();
|
||||||
|
if((b0&1)===0){ var l=b0>>1; return {len:l,v:l?hx(p.add(1),Math.min(l,4096)):''}; }
|
||||||
|
var len=parseInt(p.add(8).readU64().toString());
|
||||||
|
if(len>65536) return {len:len,v:''};
|
||||||
|
return {len:len,v:hx(p.add(16).readPointer(),Math.min(len,8192))};
|
||||||
|
}catch(e){ return {len:-1,v:''}; } }
|
||||||
|
function rdC(p){ try{ var t=p.readCString(); return t&&t.length<8000?t.slice(0,6000):''; }catch(e){ return ''; } }
|
||||||
|
// writeFileEx(name, content, out&) 两个重载
|
||||||
|
[0x252254,0x2523a0].forEach(function(off){
|
||||||
|
Interceptor.attach(base.add(off), {
|
||||||
|
onEnter: function(a){
|
||||||
|
var nm=rdC(a[1]);
|
||||||
|
var c=rdStr(a[3]);
|
||||||
|
send({type:'wfex', off:off, name:nm.slice(0,60),
|
||||||
|
len:c.len, v:c.v});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
d = frida.get_device_manager().add_remote_device('127.0.0.1:31877')
|
||||||
|
pid = d.spawn(['com.duowan.kiwi'])
|
||||||
|
s = d.attach(pid)
|
||||||
|
s.create_script((RE / "evidence/scripts/bypass_msaoaid_maps_art_callsite.js").read_text()).load()
|
||||||
|
s.create_script((RE / "evidence/scripts/mask_frida_maps_only.js").read_text()).load()
|
||||||
|
d.resume(pid)
|
||||||
|
time.sleep(11)
|
||||||
|
s.create_script((RE / "evidence/scripts/patch_guard_block_termination.js").read_text()).load()
|
||||||
|
print('armed. >>> 请在手机上退出登录并重新登录 <<<')
|
||||||
|
|
||||||
|
sc = s.create_script(JS)
|
||||||
|
events = []
|
||||||
|
def on_msg(m, _):
|
||||||
|
if m.get('type') == 'error':
|
||||||
|
print('JS ERR:', str(m)[:200]); return
|
||||||
|
if m.get('type') != 'send':
|
||||||
|
return
|
||||||
|
p = m.get('payload') or {}
|
||||||
|
t = p.get('type')
|
||||||
|
events.append(p)
|
||||||
|
Path('/Users/yml/codes/douyu_login_py/evidence/wfextrace.json').write_text(json.dumps(events))
|
||||||
|
if t == 'vt_attached':
|
||||||
|
print(f"[vt] {p['name']} slot{p['slot']} @+{p['fp']}")
|
||||||
|
elif t == 'saveLD_str':
|
||||||
|
print(f"[saveLD_str] str={p['str']['t']}:{p['str']['len']}\n bean={p['bean_head']}\n bt={p['bt']}")
|
||||||
|
elif t == 'saveLD_bean':
|
||||||
|
print(f"[saveLD_bean] flag={p['flag']}\n bean={p['bean_head']}\n bt={p['bt']}")
|
||||||
|
elif t == 'cred_unpack':
|
||||||
|
print(f"[cred_unpack] in={p['in']['t']}:{p['in']['len']}:{p['in']['v'][:160]} <- {p['ra']}")
|
||||||
|
elif t == 'bigaes':
|
||||||
|
print(f"[bigaes] len={p['x2']['len']} head={p['x2']['v'][:120]}")
|
||||||
|
elif t == 'vtable':
|
||||||
|
print(f"[VT!] {p['name']}#{p['slot']} a1={p['a1']} bt={p['bt']}")
|
||||||
|
sc.on('message', on_msg)
|
||||||
|
sc.load()
|
||||||
|
deadline = time.time() + 300
|
||||||
|
while time.time() < deadline:
|
||||||
|
time.sleep(3)
|
||||||
|
if any(e.get('type')=='bean' for e in events) and any(e.get('type')=='getcred' for e in events):
|
||||||
|
time.sleep(10)
|
||||||
|
break
|
||||||
|
Path('/Users/yml/codes/douyu_login_py/evidence/wfextrace.json').write_text(json.dumps(events))
|
||||||
|
from collections import Counter
|
||||||
|
print('saved:', Counter(e.get('type') for e in events))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for attempt in range(4):
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
break
|
||||||
|
except frida.InvalidOperationError as e:
|
||||||
|
print('detached:', e)
|
||||||
|
subprocess.run(['adb', 'shell', 'am', 'force-stop', 'com.duowan.kiwi'])
|
||||||
|
time.sleep(3)
|
||||||
@@ -99,8 +99,8 @@ def main() -> int:
|
|||||||
# => 不校验时效。故铸造策略 = 保留原 nonce/指纹, 替换 cred 字段。
|
# => 不校验时效。故铸造策略 = 保留原 nonce/指纹, 替换 cred 字段。
|
||||||
# 实验 C: 换上 frida_cred_dump 里更早签发的 hyCred(同账号不同轮换值)。
|
# 实验 C: 换上 frida_cred_dump 里更早签发的 hyCred(同账号不同轮换值)。
|
||||||
# 若 bind 成功 => 铸造机制完全打通(任意有效cred即可铸其证书)。
|
# 若 bind 成功 => 铸造机制完全打通(任意有效cred即可铸其证书)。
|
||||||
old_cred = json.load(open("evidence/hycred_live.json"))
|
old_cred = json.load(open("evidence/hycred_pp_cert.json"))
|
||||||
cred_old = bytes.fromhex(old_cred["bin_hex"])
|
cred_old = bytes.fromhex(old_cred["cred_hex"])
|
||||||
P1_new = build_p1(b"5008", fields["fingerprint"], cred_old,
|
P1_new = build_p1(b"5008", fields["fingerprint"], cred_old,
|
||||||
rnd=fields["rnd"]) # 原始 nonce
|
rnd=fields["rnd"]) # 原始 nonce
|
||||||
assert P1_new != P1_old, "应与原P1不同"
|
assert P1_new != P1_old, "应与原P1不同"
|
||||||
|
|||||||
Reference in New Issue
Block a user