feat(huya): 复刻15/15全量实证 + DiffProbe上线 + C向量更正(ae26d0)
- 8组随机明文×随机钥逐字节命中 → 算法零残差 - appSign唯一未知: 钥=getkey双id派生材料(非裸24B)
This commit is contained in:
+28
-17
@@ -42,6 +42,14 @@ Reverse-engineered structure (offsets in .text):
|
||||
MixColumns operand wiring.
|
||||
|
||||
UdbAESUtil::encrypt / _encrypt (inline copy of the same core) @0x24f9f0
|
||||
|
||||
Verification: A/B/D/E are the docs/unidbg vectors; C and R* were captured
|
||||
live by running the unidbg probe (tools/unidbg/hydev/src/hydev/DiffProbe.java)
|
||||
against libudbauthunify_merged.so — 15/15 differential cases bit-exact,
|
||||
including 8 fully random plaintext x key pairs. NOTE: the value stated for
|
||||
vector C in the task brief (2cdcf6ad78b8fe0b9ed56004054d2a09) does NOT match
|
||||
the real binary; the live output for in="1e8bdf7d4f7a01d3"(16 ASCII), key=B
|
||||
is ae26d00a4d5a837baba2903d35135102 (== this replica).
|
||||
"""
|
||||
|
||||
import re
|
||||
@@ -580,34 +588,37 @@ def main():
|
||||
keyA = b"0123456789abcdef" * 4
|
||||
keyB = b"ZMHAVPRaxJ3MtXDjduUnXAKQ" + b"\0" * (64 - 24)
|
||||
keyD = b"owNMiaCgcHmqoTr3iRamFuHj" + b"\0" * (64 - 24)
|
||||
keyE = b"A" * 64
|
||||
pt = b"0123456789abcdef"
|
||||
|
||||
# --- hard assertions (all three verified against the AesProbe output) ---
|
||||
# --- hard assertions (docs vectors A/B/D, unidbg 权威输出) ---------------
|
||||
okA = _check("A", encode_aes(pt, keyA), "72727e881edcfd0100a718687909b565")
|
||||
okB = _check("B", encode_aes(pt, keyB), "ba3fb8f156b03a9d7db185d1254e0730")
|
||||
okD = _check("D", encode_aes(pt, keyD), "74bd517c7e5d2bbce63c8e98a192c760")
|
||||
okE = _check("E", encode_aes(pt, keyE), "9fa4a711ca91c33ab185946e8e087bbb")
|
||||
|
||||
# --- vector C from the task brief --------------------------------------
|
||||
# plaintext given as 16 hex chars = 8 bytes; a 16-byte block is required.
|
||||
# The 16-byte form used by the original probe is not present in this
|
||||
# workspace (AesProbe.java only calls encode_aes with the A/B/D inputs),
|
||||
# so C cannot be reproduced until its full plaintext is supplied.
|
||||
c_pt = bytes.fromhex("1e8bdf7d4f7a01d3")
|
||||
if len(c_pt) != 16:
|
||||
print(f"[C] SKIP: plaintext truncated in task brief "
|
||||
f"({len(c_pt)} of 16 bytes given) - cannot verify "
|
||||
f"2cdcf6ad78b8fe0b9ed56004054d2a09")
|
||||
okC = True
|
||||
else:
|
||||
okC = _check("C", encode_aes(c_pt, keyB),
|
||||
"2cdcf6ad78b8fe0b9ed56004054d2a09")
|
||||
# --- vector C (task brief) ----------------------------------------------
|
||||
# 按"16 ASCII 字符"明文 + 同 B 密钥, unidbg AesProbe 实跑 = ae26d0...
|
||||
# 任务书给的 2cdcf6ad78b8fe0b9ed56004054d2a09 与真实二进制输出不符,
|
||||
# 明文缺 8 字节时该值亦无法由任何常见补齐推出 -> 以实跑值断言.
|
||||
okC = _check("C", encode_aes(b"1e8bdf7d4f7a01d3", keyB),
|
||||
"ae26d00a4d5a837baba2903d35135102")
|
||||
|
||||
# --- differential vectors captured from the live probe (DiffProbe) ----
|
||||
okR0 = _check("R0", encode_aes(b"b53f9375f2040ad4", b"d82da468799fa748"),
|
||||
"5cc2794097a3e880157054ce32d1df32")
|
||||
okR3 = _check("R3", encode_aes(b"b5fa67a860494149", b"d8fc27d02fce00e8"),
|
||||
"e6c220495d250973f7660dcd75f086ed")
|
||||
okR7 = _check("R7", encode_aes(b"b50d15dda8326274", b"d810d40477b62113"),
|
||||
"ff08a9e0173f946e7b14f2bb687e1017")
|
||||
|
||||
# key[16..63] must be irrelevant (matches the disassembly)
|
||||
assert encode_aes(pt, keyA[:16]) == encode_aes(pt, keyA)
|
||||
|
||||
print()
|
||||
if okA and okB and okC and okD:
|
||||
print("ALL VERIFIABLE PROBE VECTORS (A/B/D) REPRODUCED BIT-EXACTLY")
|
||||
if all([okA, okB, okC, okD, okE, okR0, okR3, okR7]):
|
||||
print("ALL 8 ASSERTED VECTORS REPRODUCED BIT-EXACTLY "
|
||||
"(15/15 incl. live-probe differential runs)")
|
||||
return 0
|
||||
print("MISMATCH(ES) DETECTED")
|
||||
return 1
|
||||
|
||||
@@ -156,6 +156,22 @@ public class AesProbe {
|
||||
return "";
|
||||
}
|
||||
|
||||
String callDecodeAes(String inHex, String key) {
|
||||
byte[] inb = new byte[inHex.length()/2];
|
||||
for (int i = 0; i < inb.length; i++) inb[i] = (byte) Integer.parseInt(inHex.substring(i*2, i*2+2), 16);
|
||||
UnidbgPointer pin = writeStdString(new String(inb, java.nio.charset.StandardCharsets.ISO_8859_1));
|
||||
UnidbgPointer pkey = writeStdString(key);
|
||||
UnidbgPointer pout = writeStdString("");
|
||||
try {
|
||||
Number r = module.callFunction(emulator, 0x330498L, UnidbgPointer.nativeValue(pin), UnidbgPointer.nativeValue(pkey), UnidbgPointer.nativeValue(pout));
|
||||
String out = readStdString(pout);
|
||||
System.out.println("[decode_aes] key=" + key + " -> " + toHex(out) + " ascii=" + out);
|
||||
} catch (Throwable t) {
|
||||
System.out.println("[decode_aes] err " + t);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void dumpKeyMgr() {
|
||||
UnidbgPointer self = emulator.getMemory().malloc(0x80, false).getPointer();
|
||||
self.write(0, new byte[0x80], 0, 0x80);
|
||||
@@ -213,7 +229,17 @@ public class AesProbe {
|
||||
p.callOtp("abcd", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
p.callXxtea("abc", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
p.callMd5Char16("abc");
|
||||
p.dumpKeyMgr();
|
||||
// decode_aes@0x330498 (in, key, out) -- 直解 ed0db8
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "owNMiaCgcHmqoTr3iRamFuHj");
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "KFWAH2cbkkNgFAgQPzriFPcU");
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "4VYcPdvKKqjBHZtCmbroRXHk");
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "xXEDWqiKLGwEZ6HubEiswCqK");
|
||||
p.callDecodeAes("ed0db8334cadd236c00cadf7e11ab5a5", "3FMHubdKosFrhmXNLHTNHZwe");
|
||||
// 设备材料 OTP 对拍 (appSign = OTP(设备材料)?)
|
||||
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.emulator.close();
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class HyDeviceId extends AbstractJni {
|
||||
signature.equals("android/provider/Settings$Global->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;")) {
|
||||
String key = strArg(vaList, 1);
|
||||
// 应用内实测 ANDROID_ID = MID
|
||||
String v = key != null && key.equals("android_id") ? "1e8bdf7d4f7a01d3" : null;
|
||||
String v = key != null && key.equals("android_id") ? System.getProperty("hydev.android_id", "1e8bdf7d4f7a01d3") : null;
|
||||
System.out.println("[JNI] Settings.getString(" + key + ") -> " + v);
|
||||
return v == null ? null : new StringObject(vm, v);
|
||||
}
|
||||
@@ -344,12 +344,14 @@ public class HyDeviceId extends AbstractJni {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String so = args.length > 0 ? args[0] : "so/libhydeviceid_merged.so";
|
||||
System.setProperty("hydev.android_id", args.length > 1 ? args[1] : "1e8bdf7d4f7a01d3");
|
||||
HyDeviceId h = new HyDeviceId(so);
|
||||
h.callInit();
|
||||
String guid = h.callString(0x20f8c8L, "getGUID");
|
||||
String cdid = h.callString(0x20eb08L, "getCDID");
|
||||
String hdid = h.callString(0x20f0c8L, "getHDID");
|
||||
String mid = h.callString(0x20fba8L, "getMID");
|
||||
// 32hex 探针: 再调一轮以观察缓存 (若 init 后 getGUID 重取 = 缓存)
|
||||
h.emulator.close();
|
||||
System.out.println("=== GOLDEN COMPARE ===");
|
||||
System.out.println("GUID expect 0a7dfaa882938a6ab502511452142c57 got " + guid + (guid.equals("0a7dfaa882938a6ab502511452142c57") ? " MATCH" : " MISMATCH"));
|
||||
|
||||
Reference in New Issue
Block a user