feat(huya): getOtp直调全通(C2初始化+AppLoginData伪造) - OTP真布局定案+堆32hex新候选
This commit is contained in:
@@ -145,11 +145,11 @@ public class AesProbe {
|
||||
UnidbgPointer pout = writeStdString("");
|
||||
UnidbgPointer pm = writeStdString("mstack");
|
||||
try {
|
||||
// 真实布局: (x0=in, x1=2, x2=counter, x3, x4, x5, x6=4, x7=out, [sp]=m)
|
||||
// 真实布局: (x0=in, x1=2, x2=counter, x3, x4, x5, x6=4, x7=nonce-scalar, [sp]=OUT&)
|
||||
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));
|
||||
4L, 0x12345678L, UnidbgPointer.nativeValue(pout));
|
||||
String out = readStdString(pout);
|
||||
String shortIn = in.length() > 20 ? in.substring(0, 20) : in;
|
||||
String mid = out.length() > 36 ? out.substring(4, 36) : out;
|
||||
@@ -218,6 +218,105 @@ public class AesProbe {
|
||||
return "";
|
||||
}
|
||||
|
||||
void callGetOtp() {
|
||||
try {
|
||||
// BusinessCfg::getInstance @0x281270
|
||||
Number inst = module.callFunction(emulator, 0x281270L);
|
||||
long thisPtr = inst.longValue();
|
||||
System.out.println("[getOtp] instance=0x" + Long.toHexString(thisPtr));
|
||||
if (thisPtr == 0) { System.out.println("[getOtp] instance null"); return; }
|
||||
// 伪造 AppLoginData: 4 个 string 槽 (24B SSO each) @ +0x8 +0x20 +0x38 +0x50
|
||||
com.github.unidbg.memory.MemoryBlock mb = emulator.getMemory().malloc(0x1000, false);
|
||||
UnidbgPointer pv = mb.getPointer();
|
||||
pv.write(0, new byte[0x1000], 0, 0x1000);
|
||||
writeStrAt(pv, 0x8, "hy_300023887");
|
||||
writeStrAt(pv, 0x20, "772ed992b0e161276f44ec63671e60155c506294");
|
||||
writeStrAt(pv, 0x38, "1e8bdf7d4f7a01d3");
|
||||
writeStrAt(pv, 0x50, "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T");
|
||||
// 调用 getOtp(AppLoginData&) @0x26916c
|
||||
module.callFunction(emulator, 0x26916cL, thisPtr, UnidbgPointer.nativeValue(pv));
|
||||
byte[] rb = new byte[0x60];
|
||||
pv.read(0, rb, 0, rb.length);
|
||||
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);
|
||||
} catch (Throwable t) {
|
||||
System.out.println("[getOtp] err: " + t);
|
||||
}
|
||||
}
|
||||
|
||||
void writeStrAt(UnidbgPointer p, long off, String s) {
|
||||
byte[] b = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
int n = b.length;
|
||||
if (n < 23) {
|
||||
p.write(off, new byte[]{(byte)(n << 1)}, 0, 1);
|
||||
p.write(off + 1, b, 0, n);
|
||||
p.write(off + 1 + n, new byte[]{0}, 0, 1);
|
||||
} else {
|
||||
// 真正堆串: [0]=len<<1|1 [8]=ptr [16]=cap
|
||||
long cap = (n + 15) & ~15L;
|
||||
com.github.unidbg.memory.MemoryBlock mb2 = emulator.getMemory().malloc((int)(cap + 8), false);
|
||||
UnidbgPointer hp = mb2.getPointer();
|
||||
hp.write(0, b, 0, n);
|
||||
hp.write(n, new byte[]{0}, 0, 1);
|
||||
byte[] b8 = new byte[8];
|
||||
long v = com.github.unidbg.pointer.UnidbgPointer.nativeValue(hp);
|
||||
for (int k = 0; k < 8; k++) b8[k] = (byte)(v >>> (8 * k));
|
||||
p.write(off, new byte[]{(byte)(n << 1 | 1)}, 0, 1);
|
||||
p.write(off + 8, b8, 0, 8);
|
||||
for (int k = 0; k < 8; k++) b8[k] = (byte)(cap >>> (8 * k));
|
||||
p.write(off + 16, b8, 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
void scanUdbGlobals() {
|
||||
long base = module.base;
|
||||
byte[] buf = new byte[0x12000];
|
||||
int hits = 0;
|
||||
for (int win = 0; win < 8 && hits < 20; win++) {
|
||||
long addr = base + 0x470000L + win * 0x10000L;
|
||||
UnidbgPointer pp = UnidbgPointer.pointer(emulator, addr);
|
||||
if (pp == null) continue;
|
||||
try { pp.read(0, buf, 0, buf.length); } catch (Throwable t) { continue; }
|
||||
StringBuilder cur = new StringBuilder();
|
||||
int start = -1;
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
int b = buf[i] & 0xff;
|
||||
if (b >= 0x20 && b < 0x7f) { if (start < 0) start = i; cur.append((char) b); }
|
||||
else {
|
||||
if (cur.length() >= 10) {
|
||||
System.out.println("[globals] @" + String.format("0x%x", addr + start) + " = " + cur);
|
||||
hits++;
|
||||
}
|
||||
cur.setLength(0); start = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("[globals] done hits=" + hits);
|
||||
}
|
||||
|
||||
void dumpRegion(String tag, long addr, long size) {
|
||||
UnidbgPointer pp = UnidbgPointer.pointer(emulator, addr);
|
||||
if (pp == null) { System.out.println("[dump] " + tag + " null"); return; }
|
||||
@@ -396,6 +495,7 @@ public class AesProbe {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String so = args.length > 0 ? args[0] : "so/libudbauthunify_merged.so";
|
||||
AesProbe p = new AesProbe(so);
|
||||
p.callGetOtp();
|
||||
// 基准: 已知 16B 明文 + 24B 钥
|
||||
// 64B 钥材料测试 (KeyExpansion 以 16B 步长读!!)
|
||||
String k64_zmhav = "ZMHAVPRaxJ3MtXDjduUnXAKQ" + "\0".repeat(0);
|
||||
@@ -469,6 +569,17 @@ public class AesProbe {
|
||||
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.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "13.4.22", 0x10101010L);
|
||||
// SDID 作 s5 (36B 证书族)
|
||||
p.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0x12345678L);
|
||||
p.callOtpReal("3251699", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0x12345678L);
|
||||
p.callOtpReal("", 0L, "", "", "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0x12345678L);
|
||||
p.callOtpReal("1", 1L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0x12345678L);
|
||||
// nonce=0 + 2024-08-24 时间戳 era + SDID 族
|
||||
p.callOtpReal("1724464550000", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0L);
|
||||
p.callOtpReal("1724464550", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0L);
|
||||
p.callOtpReal("1724464550000", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T", 0L);
|
||||
p.callOtpReal("1724464550000", 0L, "hy_300023887", "772ed992b0e161276f44ec63671e60155c506294", "1e8bdf7d4f7a01d3", 0L);
|
||||
p.callOtp("LogLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
// SDID 块直解 (36B 证书的两个16B块 × 全部钥)
|
||||
p.callDecodeAes("859acf6fad86b24ac479361c2de5132f", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
|
||||
Reference in New Issue
Block a user