From 702c6276fc4456d5be7e7afc3ec9a2f014c26285 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 29 Aug 2026 02:27:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(huya):=20hook=E7=AA=81=E7=A0=B4(getOtp?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E5=91=BD=E4=B8=AD)=20+=20275d0ff6=E6=8E=92?= =?UTF-8?q?=E9=99=A4(pre=E5=AD=98=E5=9C=A8)=20+=20=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E5=88=A4=E5=AE=9A=E7=82=B9=E5=AE=9A=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/HUYA_HDID_ALGORITHM_GEN.md | 11 ++++ tools/unidbg/hydev/src/hydev/AesProbe.java | 75 +++++++++++++++------- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/docs/HUYA_HDID_ALGORITHM_GEN.md b/docs/HUYA_HDID_ALGORITHM_GEN.md index 3855918..935ec2d 100644 --- a/docs/HUYA_HDID_ALGORITHM_GEN.md +++ b/docs/HUYA_HDID_ALGORITHM_GEN.md @@ -817,3 +817,14 @@ hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → a - mid = f(in, cnt, s3, s4, s5); OTP 输出=[h6][00][16B mid][s5 加密块...] ### 新候选: 堆 0x127d4b90 = 275d0ff676c0d65114acdefbd2ad87a3 (32hex! 来源待查) ### R9: ① hook 269328 (post-OTP, X29 读 [x29-0x40] = OUT!) ② 0x123M-0x130M 扫窗 before/after 差分 + +## §11.32 Hook 突破 (R9) + getOtp 结构校验 + 275d0ff6 排除 +### Hook 首次触发! (此前"不触发"=地址错) +- unicorn2 hook_add_new ✓ → getOtp 入口 0x26916c 命中: + x0=0x12491540(this) x1=0x127c2700(AppLoginData&) +- OTP 调用点 0x269324 未到 → getOtp 在 2691a4 "cmn x20,#0x8; b.hs 2695dc(函数尾)" 提前返回 + = 对伪造结构判定失败 (x20-layout 未知, 需读 0x26916c-0x2691a4 前导) +### 275d0ff676c0d65114acdefbd2ad87a3 = 调用前已有 (pre/post 扫描同现) → 非 getOtp 输出, 排除 +### 堆窗 pre/post 差分法就绪 (getOtp-pre/post 双扫描框架) +### R10: ① hook 2691a4 读 x20 (分支条件) + 读 0x26916c-0x2691a4 前导定 AppLoginData 布局 +### ② 布局修正 → getOtp 走到 269324 → hook 抓 OTP 实参+OUT diff --git a/tools/unidbg/hydev/src/hydev/AesProbe.java b/tools/unidbg/hydev/src/hydev/AesProbe.java index 4f06d9d..3e2bf75 100644 --- a/tools/unidbg/hydev/src/hydev/AesProbe.java +++ b/tools/unidbg/hydev/src/hydev/AesProbe.java @@ -218,8 +218,59 @@ public class AesProbe { return ""; } + void scanHeapWindow(String tag) { + byte[] win = new byte[0x10000]; + int hits2 = 0; + for (long baseA = 0x12300000L; baseA < 0x13000000L && hits2 < 40; baseA += 0x10000L) { + UnidbgPointer hp2 = UnidbgPointer.pointer(emulator, baseA); + if (hp2 == null) continue; + try { hp2.read(0, win, 0, win.length); } catch (Throwable t) { continue; } + StringBuilder cur = new StringBuilder(); + int st = -1; + for (int i = 0; i < win.length; i++) { + int b = win[i] & 0xff; + if ((b >= 0x20 && b < 0x7f)) { if (st < 0) st = i; cur.append((char) b); } + else { + if (cur.length() >= 30 && cur.chars().allMatch(c -> "0123456789abcdef".indexOf(Character.toLowerCase(c)) >= 0 + || "hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T".indexOf(c) >= 0)) { + System.out.println(tag + " @" + String.format("0x%x", baseA + st) + " = " + cur); + hits2++; + } + cur.setLength(0); st = -1; + } + } + } + System.out.println(tag + " done hits=" + hits2); + } + void callGetOtp() { try { + // hook: getOtp 入口 + 内部 OTP 调用点 + final long ENTRY = module.base + 0x26916cL; + final long OTPC = module.base + 0x269324L; + com.github.unidbg.arm.backend.CodeHook th = new com.github.unidbg.arm.backend.CodeHook() { + public void hook(com.github.unidbg.arm.backend.Backend backend, long address, int size, Object user) { + System.out.println("[hook] hit @0x" + Long.toHexString(address)); + if (address == OTPC || address == ENTRY) { + try { + long a0 = backend.reg_read(Arm64Const.UC_ARM64_REG_X0).longValue(); + long a1 = backend.reg_read(Arm64Const.UC_ARM64_REG_X1).longValue(); + long a2 = backend.reg_read(Arm64Const.UC_ARM64_REG_X2).longValue(); + long a3 = backend.reg_read(Arm64Const.UC_ARM64_REG_X3).longValue(); + long a6 = backend.reg_read(Arm64Const.UC_ARM64_REG_X6).longValue(); + long a7 = backend.reg_read(Arm64Const.UC_ARM64_REG_X7).longValue(); + System.out.println("[hook-args] x0=" + Long.toHexString(a0) + " x1=" + a1 + " x2=" + Long.toHexString(a2) + + " x3=" + Long.toHexString(a3) + " x6=" + a6 + " x7=" + Long.toHexString(a7)); + } catch (Throwable t) { System.out.println("[hook-args] err " + t); } + } + } + public void onAttach(com.github.unidbg.arm.backend.UnHook unHook) {} + public void detach() {} + }; + emulator.getBackend().hook_add_new(th, OTPC, OTPC, null); + emulator.getBackend().hook_add_new(th, ENTRY, ENTRY, null); + System.out.println("[hook] installed @0x26916c + @0x269324"); + scanHeapWindow("[getOtp-pre]"); // BusinessCfg::getInstance @0x281270 Number inst = module.callFunction(emulator, 0x281270L); long thisPtr = inst.longValue(); @@ -240,28 +291,8 @@ public class AesProbe { StringBuilder sb2 = new StringBuilder(); for (byte bb : rb) sb2.append(String.format("%02x", bb)); System.out.println("[getOtp] call ok, struct: " + sb2); - System.out.println("[getOtp] done, scanning heap for outputs..."); - byte[] win = new byte[0x10000]; - int hits2 = 0; - for (long baseA = 0x12300000L; baseA < 0x13000000L && hits2 < 40; baseA += 0x10000L) { - UnidbgPointer hp2 = UnidbgPointer.pointer(emulator, baseA); - if (hp2 == null) continue; - try { hp2.read(0, win, 0, win.length); } catch (Throwable t) { continue; } - StringBuilder cur = new StringBuilder(); - int st = -1; - for (int i = 0; i < win.length; i++) { - int b = win[i] & 0xff; - if ((b >= 0x20 && b < 0x7f)) { if (st < 0) st = i; cur.append((char) b); } - else { - if (cur.length() >= 30 && cur.chars().allMatch(c -> "0123456789abcdef".indexOf(Character.toLowerCase(c)) >= 0 || "hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T".indexOf(c) >= 0)) { - System.out.println("[getOtp-heap] @" + String.format("0x%x", baseA + st) + " = " + cur); - hits2++; - } - cur.setLength(0); st = -1; - } - } - } - System.out.println("[getOtp] heap-scan done hits=" + hits2); + scanHeapWindow("[getOtp-post]"); + System.out.println("[getOtp] heap-scan done"); } catch (Throwable t) { System.out.println("[getOtp] err: " + t); }