From d46654438f06e63db6c9ec5c6843fd760af96756 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 29 Aug 2026 02:42:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(huya):=20getOtp=E5=85=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E6=88=90=E5=8A=9F!OTP=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E5=AE=9E=E5=BC=B9+=E5=8F=82=E6=95=B0=E8=AF=BB=E5=8F=96=20-=20?= =?UTF-8?q?=E6=A0=B9=E5=9B=A0=3DBusinessCfg=E5=AE=9E=E4=BE=8B=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=AD=97=E6=AE=B5=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/HUYA_HDID_ALGORITHM_GEN.md | 7 +++ tools/unidbg/hydev/src/hydev/AesProbe.java | 67 ++++++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/HUYA_HDID_ALGORITHM_GEN.md b/docs/HUYA_HDID_ALGORITHM_GEN.md index f291f44..81d7a32 100644 --- a/docs/HUYA_HDID_ALGORITHM_GEN.md +++ b/docs/HUYA_HDID_ALGORITHM_GEN.md @@ -845,3 +845,10 @@ hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → a - OTP 调用点 0x269324 未达 → **流程死在 to_string-CALL (libc++-PLT 0x4543c0)** - x0=serviceTime=0 (仿真时钟 0) — 需查 0x4543c0 解析/或给 getServiceTime 注入真实时间 - 剩余步骤: ① 过 to_string (hook 0x4543c0 验证) ② OTP 调用 0x269324 [sp]=OUT& ③ 输出 vs ed0db8 + +## §11.35 getOtp 全流程执行成功! + 配置依赖定界 (R11) +### to_string@0x4543c0 = UC_ERR_READ_UNMAPPED (libc++ 符号缺失) → NOP 补丁 + hook 预写 [x29-0x58] +### 里程碑: OTP 调用 0x269324 实弹命中, 139 指令全流程走通 (getOtp 完整返回!) +- 真实参数: x0=in串(预写) x1=2 x2=counter(1) x3/x4=BusinessCfg+0x40/+0x10 x5=len0-SSO x6=4 x7=nonce OUT@[sp] +- OUT 槽=全零 + s3/s4 全空 → 根因: BusinessCfg 实例(0x12491540) 配置字段空 (真机=服务器 112B config 填充!) +### 下步: 112B config(2AQq9oUCCZ8M...) 解析→注入实例 +0x10/+0x40 → OTP-OUT 出现 → 差分进 ed0db8 diff --git a/tools/unidbg/hydev/src/hydev/AesProbe.java b/tools/unidbg/hydev/src/hydev/AesProbe.java index b089fce..f8af5ab 100644 --- a/tools/unidbg/hydev/src/hydev/AesProbe.java +++ b/tools/unidbg/hydev/src/hydev/AesProbe.java @@ -248,6 +248,8 @@ public class AesProbe { // hook: getOtp 入口 + 内部 OTP 调用点 final long ENTRY = module.base + 0x26916cL; final long OTPC = module.base + 0x269324L; + final boolean[] f1 = {false}; + final long[] outSlot = {0}; 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)); @@ -264,10 +266,47 @@ public class AesProbe { long x23 = backend.reg_read(Arm64Const.UC_ARM64_REG_X23).longValue(); long x20 = backend.reg_read(Arm64Const.UC_ARM64_REG_X20).longValue(); long sp = backend.reg_read(Arm64Const.UC_ARM64_REG_SP).longValue(); + long x29r = backend.reg_read(Arm64Const.UC_ARM64_REG_X29).longValue(); + if (address == module.base + 0x269278L && !f1[0]) { + f1[0] = true; + UnidbgPointer inslot = UnidbgPointer.pointer(emulator, x29r - 0x58L); + if (inslot != null) { + byte[] sso = new byte[24]; + sso[0] = 0x02; // len1<<1 + sso[1] = (byte) '0'; + inslot.write(0, sso, 0, 24); + System.out.println("[hook] in-slot pre-written @0x" + Long.toHexString(x29r - 0x58)); + } + } long outPtr = 0, x19r = 0; if (address == module.base + 0x269324L) { UnidbgPointer spP = UnidbgPointer.pointer(emulator, sp); - if (spP != null) { outPtr = spP.getLong(0); x19r = backend.reg_read(Arm64Const.UC_ARM64_REG_X19).longValue(); } + if (spP != null) { outPtr = spP.getLong(0); outSlot[0] = outPtr; x19r = backend.reg_read(Arm64Const.UC_ARM64_REG_X19).longValue(); } + // 读 x0/x3/x4/x5 槽内容 (各 24B) + for (long rr : new long[]{a0, a3, a4, a5}) { + UnidbgPointer strp = UnidbgPointer.pointer(emulator, rr); + if (strp != null && rr != 0) { + byte[] hh = new byte[24]; + try { strp.read(0, hh, 0, 24); } catch (Throwable tx) { continue; } + StringBuilder sbx = new StringBuilder(); + for (byte bb2 : hh) sbx.append(String.format("%02x", bb2 & 0xff)); + System.out.println("[otp-arg] @" + Long.toHexString(rr) + " = " + sbx); + } + } + } + if ((address == module.base + 0x269328L || address == module.base + 0x2693a8L || address == module.base + 0x2693b0L) && outSlot[0] != 0) { + // 后调: 读 OUT 串内容 + UnidbgPointer oP = UnidbgPointer.pointer(emulator, outSlot[0]); + if (oP != null) { + byte[] head = new byte[24]; + oP.read(0, head, 0, 24); + StringBuilder hx = new StringBuilder(); + for (byte bb2 : head) hx.append(String.format("%02x", bb2 & 0xff)); + 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); + outSlot[0] = 0; + } } System.out.println("[hook-args] @0x" + Long.toHexString(address) + " x0=" + Long.toHexString(a0) + " x1=" + Long.toHexString(a1) + " x2=" + Long.toHexString(a2) + " x3=" + Long.toHexString(a3) + " x4=" + Long.toHexString(a4) + " x5=" + Long.toHexString(a5) @@ -280,10 +319,14 @@ public class AesProbe { public void detach() {} }; emulator.getBackend().hook_add_new(th, ENTRY, ENTRY, null); - emulator.getBackend().hook_add_new(th, module.base + 0x2691a4L, module.base + 0x2691d8L, null); - emulator.getBackend().hook_add_new(th, module.base + 0x26921cL, module.base + 0x269330L, null); - emulator.getBackend().hook_add_new(th, module.base + 0x4543a0L, module.base + 0x4543a4L, null); - System.out.println("[hook] installed full-flow 0x2691a4->0x269330"); + emulator.getBackend().hook_add_new(th, module.base + 0x26916cL, module.base + 0x2695dcL, null); + emulator.getBackend().hook_add_new(th, module.base + 0x4542c0L, module.base + 0x4542c4L, null); + emulator.getBackend().hook_add_new(th, module.base + 0x4543a0L, module.base + 0x4543c4L, null); + // to_string@0x4543c0 符号缺失(UC_ERR_READ_UNMAPPED) -> 269278 bl 换 NOP + byte[] nop = new byte[]{(byte)0x1f, (byte)0x20, (byte)0x03, (byte)0xd5}; + try { emulator.getBackend().mem_write(module.base + 0x269278L, nop); System.out.println("[hook] NOP-patched 0x269278"); } + catch (Throwable tn) { System.out.println("[hook] NOP-patch failed: " + tn); } + System.out.println("[hook] MEGA + to_string bypass"); scanHeapWindow("[getOtp-pre]"); // BusinessCfg::getInstance @0x281270 Number inst = module.callFunction(emulator, 0x281270L); @@ -310,6 +353,20 @@ public class AesProbe { for (byte bb : rb) sb2.append(String.format("%02x", bb)); System.out.println("[getOtp] call ok, struct: " + sb2); scanHeapWindow("[getOtp-post]"); + System.out.println("[getOtp] instance dump 0x0-0xc0:"); + { + byte[] ib = new byte[0xc0]; + UnidbgPointer iptr = UnidbgPointer.pointer(emulator, thisPtr); + if (iptr != null) { + try { iptr.read(0, ib, 0, ib.length); } catch (Throwable te) {} + StringBuilder hx = new StringBuilder(); + for (int i = 0; i < ib.length; i++) { hx.append(String.format("%02x", ib[i] & 0xff)); if (i % 2 == 1) hx.append(' '); } + System.out.println("[inst] " + hx); + StringBuilder asc = new StringBuilder(); + for (int i = 0; i < ib.length; i++) asc.append((ib[i] >= 0x20 && ib[i] < 0x7f) ? (char) ib[i] : '.'); + System.out.println("[inst] " + asc); + } else System.out.println("[inst] null iptr"); + } System.out.println("[getOtp] heap-scan done"); } catch (Throwable t) { System.out.println("[getOtp] err: " + t);