feat(huya): 里程碑 - UdbAESUtil纯Python复刻成功(A/B/D三向量逐字节) + 登录帧t1=ProtoInfo(appSign槽=HDID32)
- tools/huya_aes_replica.py 617行逐指令复刻, AES-128变异(64B钥材用[0:16]+列链式扩展+移位吸收)
- 结构定案: t1 = {0:appSign=ed0db8! 1:appVer 2:sdkVer 3:lcid 4:clientIp 5:channel 6:countryCode}
- APP_SIGN错误名=appSign字段校验失败; 下步InvCipher直解明文
This commit is contained in:
@@ -156,6 +156,27 @@ public class AesProbe {
|
||||
return "";
|
||||
}
|
||||
|
||||
void dumpKeyMgr() {
|
||||
UnidbgPointer self = emulator.getMemory().malloc(0x80, false).getPointer();
|
||||
self.write(0, new byte[0x80], 0, 0x80);
|
||||
module.callFunction(emulator, 0x26fcc8L, UnidbgPointer.nativeValue(self));
|
||||
System.out.println("[keymgr] C2 done. object 0x80B:");
|
||||
byte[] buf = new byte[0x80];
|
||||
self.read(0, buf, 0, 0x80);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 0x80; i++) {
|
||||
byte b = buf[i];
|
||||
if (b >= 0x20 && b < 0x7f) sb.append((char) b); else sb.append('.');
|
||||
if ((i + 1) % 16 == 0) sb.append('\n');
|
||||
}
|
||||
System.out.println(sb.toString());
|
||||
// 也尝试 getkey(1,0) 返回值 (x0)
|
||||
try {
|
||||
Number r = module.callFunction(emulator, 0x26871cL, UnidbgPointer.nativeValue(self), 1L, 0L);
|
||||
System.out.println("[keymgr] getkey(1,0) ret-x0 = " + Long.toHexString(r == null ? -1 : r.longValue()));
|
||||
} catch (Throwable t) { System.out.println("[keymgr] getkey err " + t); }
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -192,6 +213,7 @@ public class AesProbe {
|
||||
p.callOtp("abcd", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
p.callXxtea("abc", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
|
||||
p.callMd5Char16("abc");
|
||||
p.dumpKeyMgr();
|
||||
p.emulator.close();
|
||||
p.emulator.close();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
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.VM;
|
||||
import com.github.unidbg.memory.Memory;
|
||||
import com.github.unidbg.pointer.UnidbgPointer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 差分探针: 与 huya_aes_replica.py 对拍 encode_aes@0x330218
|
||||
* 1) C-vector: in="1e8bdf7d4f7a01d3"(ASCII 16B) key=ZMHAV..+40NUL -> ?
|
||||
* 2) 随机差分: 若干 16B 明文 x 64B 钥
|
||||
*/
|
||||
public class DiffProbe {
|
||||
|
||||
private final AndroidEmulator emulator;
|
||||
private final Module module;
|
||||
|
||||
DiffProbe(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 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();
|
||||
}
|
||||
|
||||
UnidbgPointer writeStdString(String s) {
|
||||
Memory mem = emulator.getMemory();
|
||||
byte[] data = s.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||
int len = data.length;
|
||||
UnidbgPointer p;
|
||||
if (len <= 22) {
|
||||
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 {
|
||||
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 enc(String in, String key) {
|
||||
UnidbgPointer pin = writeStdString(in);
|
||||
UnidbgPointer pkey = writeStdString(key);
|
||||
UnidbgPointer pout = writeStdString("");
|
||||
module.callFunction(emulator, 0x330218L, UnidbgPointer.nativeValue(pin),
|
||||
UnidbgPointer.nativeValue(pkey), UnidbgPointer.nativeValue(pout));
|
||||
String out = readStdString(pout);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : out.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1))
|
||||
sb.append(String.format("%02x", b));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static String hexBytes(int n, long seed) {
|
||||
// deterministic "random" 16-byte hex string
|
||||
StringBuilder sb = new StringBuilder();
|
||||
java.util.Random r = new java.util.Random(seed);
|
||||
for (int i = 0; i < n; i++) sb.append(String.format("%02x", r.nextInt(256)));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String so = args.length > 0 ? args[0] : "so/libudbauthunify_merged.so";
|
||||
DiffProbe p = new DiffProbe(so);
|
||||
String kzm = "ZMHAVPRaxJ3MtXDjduUnXAKQ" + "\0".repeat(40);
|
||||
String k00 = "0123456789abcdef".repeat(4);
|
||||
|
||||
System.out.println("[C1] ascii-16 in=1e8bdf7d4f7a01d3 keyB -> " + p.enc("1e8bdf7d4f7a01d3", kzm));
|
||||
System.out.println("[C2] flag+15 in=\\u0020+0123456789abcde keyB -> " + p.enc("\u00200123456789abcde", kzm));
|
||||
System.out.println("[C3] hexbytes8pad in=1e8bdf7d4f7a01d3+0000000000000000 keyB -> " + p.enc("1e8bdf7d4f7a01d3\0\0\0\0\0\0\0\0", kzm));
|
||||
System.out.println("[X0] in=0123456789abcdef keyB -> " + p.enc("0123456789abcdef", kzm));
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
long seed = 1000 + i;
|
||||
String in = hexBytes(16, seed);
|
||||
String key = hexBytes(64, seed ^ 0x5a5a);
|
||||
System.out.println("[R" + i + "] in=" + in + " key=" + key + " -> " + p.enc(in, key));
|
||||
}
|
||||
// 用 64 个 'A' 测试纯 64B ASCII 钥
|
||||
System.out.println("[KAA] in=0123456789abcdef key='A'*64 -> " + p.enc("0123456789abcdef", "A".repeat(64)));
|
||||
// 24B 短钥 (short string, flag 参与装载?)
|
||||
System.out.println("[K24] in=0123456789abcdef key=0123456789abcdef(16B short) -> " + p.enc("0123456789abcdef", "0123456789abcdef"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user