From 0185296089fab35a64979795d280fd2053fb6651 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 29 Aug 2026 02:17:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(huya):=20=E7=9C=9F=E5=AE=9EgetOtp=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E5=85=A8=E8=A7=A3(serviceTime+=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E5=99=A8+AppLoginData=E5=AD=97=E6=AE=B5)=20-=20=E7=9B=B4?= =?UTF-8?q?=E8=B0=83=E8=B7=AF=E7=BA=BF=E5=B0=B1=E7=BB=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/HUYA_HDID_ALGORITHM_GEN.md | 10 ++++ tools/unidbg/hydev/src/hydev/AesProbe.java | 62 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/docs/HUYA_HDID_ALGORITHM_GEN.md b/docs/HUYA_HDID_ALGORITHM_GEN.md index 204422f..aaef40a 100644 --- a/docs/HUYA_HDID_ALGORITHM_GEN.md +++ b/docs/HUYA_HDID_ALGORITHM_GEN.md @@ -796,3 +796,13 @@ UdbAESUtilC1(…) + encrypt ← 后段 ### OTP 消息名差分 hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → appSign ≠ OTP 任何段 ### 下轮: setSafeDeviceId 直调后 getHDID 状态变化 + 0x64670 "hdid: " 装配函数直调 + +## §11.30 真实 getOtp 调用语义全解 (R7) - 直调路线就绪 +### OTP 双真实调用方 (BusinessCfg::getOtp!) +- 268ca0 (getOtp(y, string&, int&)) + **0x269324 (getOtp(AppLoginData&) = 登录路径!)** +- 269324 参数: x0=to_string(serviceTime)!, x1=2, x2=AESkeyMgr计数器(w25), x3=AppLoginData+0x40, + x4=AppLoginData+0x10, x5=全局串(0x484000+0xa0), x6=4, x7=out, [sp]=m(串) +- **可直调**: BusinessCfg::getInstance@0x281270 + getOtp(AppLoginData&)@0x26916c (T/W 符号) +- AppLoginData 字段布局: +0x40/+0x10/+0x8 (多 string), 全局材料=0x484000+0xa0 +### OTP tail = f(c,d) 差分 (推翻"固定"结论) + 真实布局空输出 (参数微差待调) +### 下轮 (R8): getInstance→getOtp(AppLoginData) 直调, 金样本值注满结构 → 输出 vs ed0db8 diff --git a/tools/unidbg/hydev/src/hydev/AesProbe.java b/tools/unidbg/hydev/src/hydev/AesProbe.java index 7e5164a..508ab95 100644 --- a/tools/unidbg/hydev/src/hydev/AesProbe.java +++ b/tools/unidbg/hydev/src/hydev/AesProbe.java @@ -137,6 +137,45 @@ public class AesProbe { return out; } + String callOtpReal(String in, long counter, String s3, String s4, String s5, long mStack) { + UnidbgPointer pa = writeStdString(in); + UnidbgPointer ps3 = writeStdString(s3); + UnidbgPointer ps4 = writeStdString(s4); + UnidbgPointer ps5 = writeStdString(s5); + UnidbgPointer pout = writeStdString(""); + UnidbgPointer pm = writeStdString("mstack"); + try { + // 真实布局: (x0=in, x1=2, x2=counter, x3, x4, x5, x6=4, x7=out, [sp]=m) + module.callFunction(emulator, 0x32fa24L, + UnidbgPointer.nativeValue(pa), 2L, counter, + UnidbgPointer.nativeValue(ps3), UnidbgPointer.nativeValue(ps4), UnidbgPointer.nativeValue(ps5), + 4L, UnidbgPointer.nativeValue(pout), UnidbgPointer.nativeValue(pm)); + String out = readStdString(pout); + String shortIn = in.length() > 20 ? in.substring(0, 20) : in; + String mid = out.length() > 36 ? out.substring(4, 36) : out; + System.out.println("[otp-real] in=" + shortIn + " cnt=" + counter + " s3=" + s3.substring(0, Math.min(10,s3.length())) + + " s5=" + s5.substring(0, Math.min(10,s5.length())) + " -> out=" + toHex(out) + " mid16=" + mid); + } catch (Throwable t) { System.out.println("[otp-real] err " + t.getMessage()); } + return ""; + } + + String callOtpFull(String a, String b, String c, String d, long h2, long m2) { + UnidbgPointer pa = writeStdString(a); + UnidbgPointer pb = writeStdString(b); + UnidbgPointer pc = writeStdString(c); + UnidbgPointer pd = writeStdString(d); + UnidbgPointer pout = writeStdString(""); + try { + module.callFunction(emulator, 0x32fa24L, + UnidbgPointer.nativeValue(pa), 2L, 0L, + UnidbgPointer.nativeValue(pb), UnidbgPointer.nativeValue(pc), UnidbgPointer.nativeValue(pd), + h2, m2, UnidbgPointer.nativeValue(pout)); + String out = readStdString(pout); + System.out.println("[otp-full] c=" + c + " d=" + d + " h=" + h2 + " m=" + m2 + " -> " + toHex(out)); + } catch (Throwable t) { System.out.println("[otp-full] err " + t); } + return ""; + } + String callOtp(String a, String b) { UnidbgPointer pa = writeStdString(a); UnidbgPointer pb = writeStdString(b); @@ -407,6 +446,29 @@ public class AesProbe { p.callOtp("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); p.callOtp("hypasswordLogin", "owNMiaCgcHmqoTr3iRamFuHj"); p.callOtp("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); + // 尾随参数差分: 变 c/d/h/m + p.callOtpFull("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "X", "Y", 3L, 4L); + p.callOtpFull("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "ABCDEFGH", "IJKLMNOP", 7L, 9L); + p.callOtpFull("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "5008", "13.4.22", 1L, 2L); + p.callOtpFull("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "ed0db8334cadd236c00cadf7e11ab5a5", "1e8bdf7d4f7a01d3", 5L, 6L); + // 金样本帧材料爆破: (c,d) = 帧字段, h=1..6 + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "13.4.22", "1.0.80138", 1L, 2L); + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "13.4.22", "1.0.80138", 3L, 4L); + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "127.0.0.1", "xiaomi", 1L, 2L); + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "1e8bdf7d4f7a01d3", "7c5387e0539c023c31c4ff0e807e7256117385ee", 1L, 2L); + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "7c5387e0539c023c31c4ff0e807e7256117385ee", "02df398797432eadefcc12767119ad5e80999389", 1L, 2L); + p.callOtpFull("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "13.4.22", "xiaomi", 1L, 2L); + p.callOtpFull("", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "13.4.22", "1.0.80138", 1L, 2L); + p.callOtpFull("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ", "13.4.22", "1.0.80138", 1L, 2L); + // 真实 getOtp 布局: (in=serviceTime串, cnt, s3, s4, s5, m) - 金样本值 + p.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("3251699", 1L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("3251699", 2L, "300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("300023887", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("12916753251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("0", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 1L); + p.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "7c5387e0539c023c31c4ff0e807e7256117385ee", 1L); + p.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "13.4.22", 1L); p.callOtp("LogLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); // SDID 块直解 (36B 证书的两个16B块 × 全部钥) p.callDecodeAes("859acf6fad86b24ac479361c2de5132f", "ZMHAVPRaxJ3MtXDjduUnXAKQ");