feat(huya): unidbg encode_aes 探针上线 + 64B钥材料发现(AES变体非标)

- AesProbe.java: 直接调用 encode_aes, 权威差分源 (已知输入输出已记录)
- KeyExpansion 以16B步长读钥 → 64B材料; 标准AES对照 b1e3abcc≠ba3fb8f1 证变体
- docs §11.16
This commit is contained in:
yml2213
2026-08-29 00:01:00 +08:00
parent b62ba48db7
commit 7249dd6909
2 changed files with 163 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
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.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
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 java.io.File;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* unidbg 探针: 直接调用 libudbauthunify.so 的 encode_aes@0x330218 (IN, KEY, OUT)
* 与 UdbAESUtil 全家, 用可控输入差分出 32hex-HDID32 的 AES 算法细节.
*
* std::string ABI (NDK libc++):
* short: [0]=len<<1 (LSB=0), data@[1..24]
* long : [0..7]=cap(LSB=1), [8..15]=size, [16..23]=data-ptr
*/
public class AesProbe {
static final Map<String, String> PREFS = new LinkedHashMap<>();
static {
PREFS.put("hydeviceid_config", "2AQq9oUCCZ8MgS1qhJHFp+U7vBdmTBVkH8WlXondx64IE+kvc65rHJtuTazmCLT0Z7FYQEVuosfMmclJOeQrI7PLlAhhZYU5dXoYCKQJ7/ze1y5V6DvozlZZUQLG9jgoNJ4kdEyEi3QQavTQGdiokQ==");
}
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
AesProbe(String soPath) throws Exception {
BackendFactory backend = new DynarmicFactory(true);
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.duowan.kiwi").addBackendFactory(backend).build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM();
vm.setJni(new com.github.unidbg.linux.android.dvm.AbstractJni() {});
vm.setVerbose(false);
DalvikModule dm = vm.loadLibrary(new File(soPath), false);
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
System.out.println("[*] lib loaded base=0x" + Long.toHexString(module.base));
}
// 写一个 NDK-libc++ std::string (short 形式: len<=22 走 inline)
UnidbgPointer writeStdString(String s) {
Memory mem = emulator.getMemory();
byte[] data = s.getBytes();
int len = data.length;
UnidbgPointer p;
if (len <= 22) {
// short: [0] = len<<1, data @1
p = mem.malloc(24, false).getPointer();
byte[] buf = new byte[24];
buf[0] = (byte) (len << 1);
System.arraycopy(data, 0, buf, 1, len);
p.write(0, buf, 0, buf.length);
} else {
// long: [0..7]=cap(LSB=1 = 0xffff...f), [8..15]=size, [16..23]=ptr
UnidbgPointer heap = mem.malloc(len + 1, false).getPointer();
heap.write(0, data, 0, len);
heap.write(len, new byte[]{0}, 0, 1);
p = mem.malloc(24, false).getPointer();
byte[] cap = new byte[8]; Arrays.fill(cap, (byte) 0xff);
p.write(0, cap, 0, 8);
p.write(8, new byte[]{(byte) (len & 0xff), (byte) (len >> 8), 0, 0, 0, 0, 0, 0}, 0, 8);
byte[] ptrBytes = new byte[8];
long pv = UnidbgPointer.nativeValue(heap);
for (int i = 0; i < 8; i++) ptrBytes[i] = (byte) (pv >> (8 * i));
p.write(16, ptrBytes, 0, 8);
}
return p;
}
String readStdString(UnidbgPointer p) {
byte[] one = new byte[1];
p.read(0, one, 0, 1);
int first = one[0] & 0xff;
if ((first & 1) == 0) {
int len = first >> 1;
byte[] dat = new byte[len];
p.read(1, dat, 0, len);
return new String(dat, java.nio.charset.StandardCharsets.ISO_8859_1);
} else {
long size = 0;
byte[] szb = new byte[8];
p.read(8, szb, 0, 8);
for (int i = 0; i < 8; i++) size |= ((long) (szb[i] & 0xff)) << (8 * i);
byte[] ptrb = new byte[8];
p.read(16, ptrb, 0, 8);
long pv = 0;
for (int i = 0; i < 8; i++) pv |= ((long) (ptrb[i] & 0xff)) << (8 * i);
UnidbgPointer data = UnidbgPointer.pointer(this.emulator, pv);
byte[] dat2 = new byte[(int) size];
data.read(0, dat2, 0, (int) size);
return new String(dat2, java.nio.charset.StandardCharsets.ISO_8859_1);
}
}
String callEncodeAes(String in, String key) {
UnidbgPointer pin = writeStdString(in);
UnidbgPointer pkey = writeStdString(key);
UnidbgPointer pout = writeStdString("");
long base = module.base;
Number retN = module.callFunction(emulator, 0x330218L, UnidbgPointer.nativeValue(pin), UnidbgPointer.nativeValue(pkey), UnidbgPointer.nativeValue(pout));
long ret = retN == null ? 0 : retN.longValue();
String out = readStdString(pout);
System.out.println("[encode_aes] in=" + in + " key=" + key + " -> out=" + out + " hex=" + toHex(out) + " (ret=" + ret + ")");
return out;
}
static String toHex(String s) {
StringBuilder sb = new StringBuilder();
for (byte b : s.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1)) sb.append(String.format("%02x", b));
return sb.toString();
}
public static void main(String[] args) throws Exception {
String so = args.length > 0 ? args[0] : "so/libudbauthunify_merged.so";
AesProbe p = new AesProbe(so);
// 基准: 已知 16B 明文 + 24B 钥
// 64B 钥材料测试 (KeyExpansion 以 16B 步长读!!)
String k64_zmhav = "ZMHAVPRaxJ3MtXDjduUnXAKQ" + "\0".repeat(0);
k64_zmhav = String.format("%-64s", "ZMHAVPRaxJ3MtXDjduUnXAKQ").replace(' ', '\0');
String k64_ow = String.format("%-64s", "owNMiaCgcHmqoTr3iRamFuHj").replace(' ', '\0');
String k64_cfg0 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
p.callEncodeAes("0123456789abcdef", k64_zmhav);
p.callEncodeAes("0123456789abcdef", k64_ow);
p.callEncodeAes("0123456789abcdef", k64_cfg0);
p.callEncodeAes("1e8bdf7d4f7a01d3", k64_zmhav);
p.callEncodeAes("1e8bdf7d4f7a01d3", k64_cfg0);
p.emulator.close();
}
}