feat(huya): getkey=裸24B钥(堆节点取证推翻派生假设) + 明文族爆破零命中落档

- Unicorn2后端+全内存扫描: 0x127d* 堆节点含 ZMHAV/4VYc/xXED 裸钥 + 输入副本
- 下一步: OTP insert-pos 差分(内部副本) + hydeviceid_config 88B 解码路径
This commit is contained in:
yml2213
2026-08-29 01:18:53 +08:00
parent b2bf747e7a
commit a8aebfed40
3 changed files with 182 additions and 6 deletions
+128 -3
View File
@@ -3,7 +3,7 @@ package hydev;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.BackendFactory;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
@@ -11,6 +11,7 @@ import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import unicorn.Arm64Const;
import java.io.File;
import java.util.Arrays;
@@ -37,7 +38,7 @@ public class AesProbe {
private final Module module;
AesProbe(String soPath) throws Exception {
BackendFactory backend = new DynarmicFactory(true);
BackendFactory backend = new Unicorn2Factory(true);
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.duowan.kiwi").addBackendFactory(backend).build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
@@ -149,7 +150,13 @@ public class AesProbe {
UnidbgPointer.nativeValue(pb), UnidbgPointer.nativeValue(pc), UnidbgPointer.nativeValue(pd),
3L, 4L, UnidbgPointer.nativeValue(pout));
String out = readStdString(pout);
// 读回输入串 (native insert 后 = in+getkey钥!)
String pinAfter = readStdString(pa);
System.out.println("[otp] a=" + a + " b=" + b + " -> hex=" + toHex(out));
System.out.println("[otp-insert] a-after=" + pinAfter + " hex=" + toHex(pinAfter));
System.out.println("[otp-insert] b-after=" + readStdString(pb) + " hex=" + toHex(readStdString(pb)));
System.out.println("[otp-insert] c-after=" + readStdString(pc) + " hex=" + toHex(readStdString(pc)));
System.out.println("[otp-insert] d-after=" + readStdString(pd) + " hex=" + toHex(readStdString(pd)));
} catch (Throwable t) {
System.out.println("[otp] err " + t);
}
@@ -172,6 +179,124 @@ public class AesProbe {
return "";
}
void scanMemoryForKeys() {
byte[] needle = "ZMHAVPRaxJ3MtXDjduUnXAKQ".getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
long[] ranges = {0x0L, 0x1000000L, 0x40000000L, 0x80000000L, 0x100000000L, 0x300000000L, 0x70200000L};
long STEP = 0x10000;
int found = 0;
byte[] buf = new byte[(int) STEP];
for (long rb : ranges) {
long win = rb == 0x0L ? 0x1000000L : 0x20000000L;
for (long off = 0; off < win && found < 8; off += STEP) {
long addr = rb + off;
UnidbgPointer pp = UnidbgPointer.pointer(emulator, addr);
if (pp == null) continue;
try { pp.read(0, buf, 0, (int) STEP); } catch (Throwable t) { continue; }
for (int i = 0; i + needle.length <= buf.length; i++) {
boolean ok = true;
for (int j = 0; j < needle.length; j++) if (buf[i+j] != needle[j]) { ok = false; break; }
if (ok) {
System.out.println("[scan] KEY ZMHAV @ " + String.format("0x%x", addr + i));
byte[] seg = new byte[80];
try { UnidbgPointer.pointer(emulator, addr + i).read(0, seg, 0, 80); } catch (Throwable t) {}
StringBuilder hx = new StringBuilder();
for (byte b : seg) hx.append(String.format("%02x", b & 0xff));
System.out.println("[scan] ctx: " + hx);
found++;
}
}
}
}
System.out.println("[scan] done found=" + found);
}
void hookGetkeyReturn() {
// OTP 内部 getkey 返回点 0x32fb80 (32fa24+0x15c): 读 x0/x1/x2 SSO
final boolean[] done = {false};
com.github.unidbg.arm.backend.CodeHook hook = 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 @" + String.format("0x%x", address));
if (done[0]) return; done[0] = true;
try {
long x0 = backend.reg_read(Arm64Const.UC_ARM64_REG_X0).longValue();
long x1 = backend.reg_read(Arm64Const.UC_ARM64_REG_X1).longValue();
long x2 = backend.reg_read(Arm64Const.UC_ARM64_REG_X2).longValue();
System.out.println("[hook-getkey] x0=" + Long.toHexString(x0) + " x1=" + Long.toHexString(x1) + " x2=" + Long.toHexString(x2));
long flag = x0 & 1;
if (flag == 0) {
long len = (x0 >> 1) & 0x7f;
StringBuilder hx = new StringBuilder();
for (int i = 0; i < len; i++) hx.append(String.format("%02x", (x0 >> (8 + i * 8)) & 0xff));
System.out.println("[hook-getkey] SSO len=" + len + " hex=" + hx);
} else {
UnidbgPointer pp = UnidbgPointer.pointer(emulator, x2);
byte[] seg = new byte[32];
if (pp != null) { pp.read(0, seg, 0, 32);
StringBuilder hx = new StringBuilder();
for (byte b : seg) hx.append(String.format("%02x", b & 0xff));
System.out.println("[hook-getkey] LONG size=" + x1 + " data=" + hx); }
else System.out.println("[hook-getkey] LONG null ptr");
}
} catch (Throwable t) { System.out.println("[hook-getkey] err " + t); }
}
public void onAttach(com.github.unidbg.arm.backend.UnHook unHook) {}
public void detach() {}
};
try {
emulator.getBackend().hook_add_new(hook, module.base + 0x32fb78L, module.base + 0x32fb88L, null);
System.out.println("[hook] installed at 0x32fb80");
} catch (Throwable t) { System.out.println("[hook] install err " + t); }
// 触发一次 OTP (内部会走 getkey)
callOtp("abc", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
}
void scanBss() {
long base = module.base;
long[] range = {0x491000L, 0x491c00L};
byte[] buf = new byte[(int) (range[1] - range[0])];
try {
UnidbgPointer p0 = UnidbgPointer.pointer(emulator, base + range[0]);
if (p0 != null) p0.read(0, buf, 0, buf.length); else System.out.println("[bss] null ptr");
} catch (Throwable t) { System.out.println("[bss] read err " + t); return; }
// 找 ASCII 串 (可打印, 长度>=8)
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() >= 8) {
System.out.println("[bss] @" + String.format("0x%x", range[0] + start) + " = " + cur);
}
cur.setLength(0); start = -1;
}
}
if (cur.length() >= 8) System.out.println("[bss] @" + String.format("0x%x", range[0] + start) + " = " + cur);
// 特定 key 串的原始字节
for (String k : new String[]{"ZMHAV", "HuyaUdb", "owNMia", "KFWAH"}) {
int idx = indexOfAscii(buf, k);
System.out.println("[bss] key-" + k + " @" + (idx >= 0 ? String.format("0x%x", range[0] + idx) : "not-found"));
if (idx >= 0) {
byte[] seg = new byte[64];
UnidbgPointer pk = UnidbgPointer.pointer(emulator, base + range[0] + idx);
if (pk != null) pk.read(0, seg, 0, seg.length);
StringBuilder hx = new StringBuilder();
for (byte x : seg) hx.append(String.format("%02x", x & 0xff));
System.out.println("[bss] context: " + hx);
}
}
}
static int indexOfAscii(byte[] buf, String needle) {
byte[] nb = needle.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
outer:
for (int i = 0; i + nb.length <= buf.length; i++) {
for (int j = 0; j < nb.length; j++) if (buf[i+j] != nb[j]) continue outer;
return i;
}
return -1;
}
void dumpKeyMgr() {
UnidbgPointer self = emulator.getMemory().malloc(0x80, false).getPointer();
self.write(0, new byte[0x80], 0, 0x80);
@@ -240,7 +365,7 @@ public class AesProbe {
p.callOtp("1e8bdf7d4f7a01d3" + "13.4.22" + "1.0.80138" + "127.0.0.1" + "xiaomi", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("5008" + "13.4.22" + "1.0.80138", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("ed0db8334cadd236c00cadf7e11ab5a5", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.emulator.close();
p.scanMemoryForKeys();
p.emulator.close();
}
}
+42 -3
View File
@@ -13,6 +13,7 @@ import com.github.unidbg.pointer.UnidbgPointer;
import java.io.File;
import java.util.Arrays;
import unicorn.Arm64Const;
import unicorn.UnicornConst;
/**
@@ -136,6 +137,41 @@ public class DiffProbe {
probeGetkey(args.length > 0 ? args[0] : "so/libudbauthunify_merged.so");
}
static String bytesToHex(byte[] b) {
StringBuilder sb = new StringBuilder();
for (byte x : b) sb.append(String.format("%02x", x & 0xff));
return sb.toString();
}
static byte[] keyFromSso(long x0, long x1, long x2, DiffProbe p) throws Exception {
long flag = x0 & 1;
if (flag == 0) {
long len = (x0 >> 1) & 0x7f;
byte[] out = new byte[(int) len];
long v = x0 >> 8;
for (int i = 0; i < (int) len; i++) {
int off = (i < 7) ? 8 + i * 8 : (i - 7) * 8;
int sh = (i < 7) ? 0 : 16;
out[i] = (byte) ((x0 >> (8 + i * 8)) & 0xff);
}
return out;
} else {
// long form: x1 = size, x2 = ptr
long size = x1, ptr = x2;
byte[] out = new byte[(int) size];
UnidbgPointer pp = UnidbgPointer.pointer(p.emulator, ptr);
if (pp == null) return new byte[0];
pp.read(0, out, 0, out.length);
return out;
}
}
static String reconstruct(byte[] b) {
StringBuilder sb = new StringBuilder();
for (byte x : b) { char c = (char) (x & 0xff); sb.append(c >= 32 && c < 127 ? c : '.'); }
return sb.toString();
}
static void probeGetkey(String soPath) throws Exception {
DiffProbe p = new DiffProbe(soPath);
com.github.unidbg.memory.Memory mem = p.emulator.getMemory();
@@ -147,9 +183,12 @@ public class DiffProbe {
for (int id1 = 0; id1 <= 4; id1++) {
try {
p.module.callFunction(p.emulator, 0x26871cL, UnidbgPointer.nativeValue(self), (long) id1, 0L);
long x0 = p.emulator.getBackend().reg_read(UnicornConst.UC_ARM64_REG_X0);
long x2 = p.emulator.getBackend().reg_read(UnicornConst.UC_ARM64_REG_X2);
System.out.println("[getkey] id1=" + id1 + " x0=" + Long.toHexString(x0) + " x2=" + Long.toHexString(x2));
long x0 = p.emulator.getBackend().reg_read(Arm64Const.UC_ARM64_REG_X0).longValue();
long x1 = p.emulator.getBackend().reg_read(Arm64Const.UC_ARM64_REG_X1).longValue();
long x2 = p.emulator.getBackend().reg_read(Arm64Const.UC_ARM64_REG_X2).longValue();
System.out.println("[getkey] id1=" + id1 + " x0=" + Long.toHexString(x0) + " x1=" + Long.toHexString(x1) + " x2=" + Long.toHexString(x2));
String kv = reconstruct(keyFromSso(x0, x1, x2, p));
System.out.println("[getkey] id1=" + id1 + " KEY=" + kv + " hex=" + bytesToHex(kv.getBytes("ISO-8859-1")));
} catch (Throwable t) { System.out.println("[getkey] id1=" + id1 + " err " + t); }
}
p.emulator.close();