diff --git a/docs/HUYA_HDID_ALGORITHM_GEN.md b/docs/HUYA_HDID_ALGORITHM_GEN.md index 0b46e4e..7bc7770 100644 --- a/docs/HUYA_HDID_ALGORITHM_GEN.md +++ b/docs/HUYA_HDID_ALGORITHM_GEN.md @@ -858,3 +858,12 @@ hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → a - BusinessCfg 诸符号: load/saveLoginData/getLoginData@0x26984c etc. (+0x10/+0x40 = 登录数据字段源) - 注入 +0x10/+0x40 (name/sha1) 后 getOtp 流程 269280-2692a4 段 = 新 UC_ERR_READ_UNMAPPED (s4 拷贝路径) → 实例字段注入 = SSO 布局细节需修 (s4 拷贝读 [x8+0x10+0x10] cap 越界?) + +## §11.37 🎯 OTP-OUT 内容捕获成功! (R11c) +### getOtp 内部 OTP 输出完整读取: + content=0401 7c0e461e8c52e9360e90b5af264af667 0f127c3af8c69c3428ef3d1e9b7a8618 (34B) + 结构 = [04=ver][cnt=1][16B mid][16B tail] +- libc++ 长串对象: [0-7]cap [8-15]size [16-23]ptr → 内容读取法定案 +- SSO-only 注入 (s3=s4="hy_300023887") 后 getOtp 全流程 985 hooks 无错误! +### mid = f(in, cnt, s3, s4, s5) (s5 也入 mid!) — 差分闭环就绪 +### 下步: 全组合爆破 (in/cnt/s3/s4/s5 候选族) → mid == ed0db8 diff --git a/tools/unidbg/hydev/src/hydev/AesProbe.java b/tools/unidbg/hydev/src/hydev/AesProbe.java index bd6d71b..c0ed9c2 100644 --- a/tools/unidbg/hydev/src/hydev/AesProbe.java +++ b/tools/unidbg/hydev/src/hydev/AesProbe.java @@ -305,6 +305,21 @@ public class AesProbe { StringBuilder asc = new StringBuilder(); for (byte bb2 : head) asc.append((bb2 >= 0x20 && bb2 < 0x7f) ? (char) bb2 : '.'); System.out.println("[otp-OUT] @" + Long.toHexString(outSlot[0]) + " head=" + hx + " asc=" + asc); + // libc++ 长串: [0-7]cap [8-15]size [16-23]ptr -> 读内容 + long capw = oP.getLong(0), sizew = oP.getLong(8), ptrw = oP.getLong(16); + if (ptrw > 0x10000L && sizew > 0 && sizew < 0x200) { + UnidbgPointer cp = UnidbgPointer.pointer(emulator, ptrw); + if (cp != null) { + byte[] content = new byte[(int) sizew]; + try { cp.read(0, content, 0, content.length); + StringBuilder cx = new StringBuilder(); + for (byte bb2 : content) cx.append(String.format("%02x", bb2 & 0xff)); + System.out.println("[otp-OUTC] size=" + sizew + " content=" + cx); + String mid = content.length >= 20 ? cx.toString().substring(4, 36) : cx.toString(); + System.out.println("[otp-OUTC] mid16=" + mid + (mid.equals("ed0db8334cadd236c00cadf7e11ab5a5") ? " <<< ED0DB8 MATCH!!!" : "")); + } catch (Throwable tc) { System.out.println("[otp-OUTC] read err " + tc); } + } + } outSlot[0] = 0; } } @@ -345,12 +360,12 @@ public class AesProbe { writeStrAt(pv, 0x40, ""); // mobileMask "" writeStrAt(pv, 0x58, ""); // passport "" writeStrAt(pv, 0x70, "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T"); - // 注入登录数据到 BusinessCfg 实例 +0x10/+0x40 (s4/s3 源) + // 注入 SSO-only 短串 (避免 heap 构造器越界) try { - UnidbgPointer ip = UnidbgPointer.pointer(emulator, thisPtr); - writeStrAt(ip, 0x10, "hy_300023887"); - writeStrAt(ip, 0x40, "772ed992b0e161276f44ec63671e60155c506294"); - System.out.println("[getOtp] injected login-data into instance"); + UnidbgPointer ip2 = UnidbgPointer.pointer(emulator, thisPtr); + writeStrAt(ip2, 0x10, "hy_300023887"); + writeStrAt(ip2, 0x40, "hy_300023887"); + System.out.println("[getOtp] injected SSO login-data"); } catch (Throwable ti) { System.out.println("[getOtp] inject err " + ti); } // 调用 getOtp(AppLoginData&) @0x26916c module.callFunction(emulator, 0x26916cL, thisPtr, UnidbgPointer.nativeValue(pv)); @@ -616,6 +631,11 @@ public class AesProbe { p.callDecodeAes("d8042af68502099f0c812d6a8491c5a7", "KNSDNjfohweeromnmkladj3g"); p.callDecodeAes("e53bbc17664c15641fc5a55e89ddc7ae", "KNSDNjfohweeromnmkladj3g"); p.callDecodeAes("d8042af68502099f0c812d6a8491c5a7", "xnkdDFIERRIPT5dfhfgiJ0FD"); + // 空输入 OTP (s5 全零串族) + p.callOtpReal("0", 1L, "", "", "", 0L); + p.callOtpReal("0", 1L, "", "", "00000000000000000000000000000000", 0L); + p.callOtpReal("0", 1L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "00000000000000000000000000000000", 0L); + p.callOtpReal("1724464550000", 0L, "", "", "", 0L); // 基准: 已知 16B 明文 + 24B 钥 // 64B 钥材料测试 (KeyExpansion 以 16B 步长读!!) String k64_zmhav = "ZMHAVPRaxJ3MtXDjduUnXAKQ" + "\0".repeat(0);