feat(huya): getkey=裸24B钥(堆节点取证推翻派生假设) + 明文族爆破零命中落档
- Unicorn2后端+全内存扫描: 0x127d* 堆节点含 ZMHAV/4VYc/xXED 裸钥 + 输入副本 - 下一步: OTP insert-pos 差分(内部副本) + hydeviceid_config 88B 解码路径
This commit is contained in:
@@ -726,3 +726,15 @@ UdbAESUtilC1(…) + encrypt ← 后段
|
||||
- 16B 密文 (encode_aes 形态) — 朴素明文组合 (aid/mid/DEVID40/版本/配置) × 6 钥族 × 转置/b64/md5 = 零命中
|
||||
- decode_aes 直解 (6 钥) = 32B 无明文 → **钥 = getkey(双 id) 派生材料** (非裸 24B)
|
||||
- 下步 (round10): 逆向 replaaica 得 decrypt → 或 DiffProbe 调 getkey 取派生钥 → 对拍
|
||||
|
||||
## §11.23 getkey 等于裸钥! 堆节点取证 + 明文族爆破零命中 (新目标 R1)
|
||||
|
||||
### AESkeyMgr 堆节点取证 (unidbg 全内存扫)
|
||||
- @0x127ddda0: `ZMHAV…24B` + len-0x18 = **getkey 返回的 = 裸 24B 钥串 (无派生!)**
|
||||
- @0x127ea000: ZMHAV + 4VYcPdv… + xXEDWq… 相邻节点 = 钥表顺序装载
|
||||
- @0x127ddf00: ZMHAV + "abc" (输入副本同址 = OTP 的 insert 目标 = 内部副本!)
|
||||
- 此前「派生钥」假设 → **推翻: 24B 钥直读直用**
|
||||
|
||||
### appSign (ed0db8) 明文族爆破 (复刻引擎 × 8 钥 × 19 材料 × 8 形态 = 零命中)
|
||||
- md5/sha1/sha256 摘要 16B、首尾 16B、双 md5、hex 编码等全灭
|
||||
- 结论: 明文 ≠ 简单材料哈希 — 可能是 config 解密产物 / 复合序列化 (下轮: OTP insert-pos 差分 + config 88B 解码路径)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user