feat(huya): 💥 XXTEA变体识别+harness直调sub_24D58运行成功 (R25)

- mfa JNI表11方法全解 + b87_解包链语义 (XXTEA+inflate+'resp')
- sub_24D58=XXTEA变体 (0x9E3779B9+掩码混淆) 反编译+harness直调验证
- harness: 导入补丁115/构建器语义构建/日志桩/双后端
- 下一步: key槽验证+b87_全链直调+a87_加密侧
This commit is contained in:
yml2213
2026-08-29 07:34:23 +08:00
parent 276a7782a0
commit 44948277d4
2 changed files with 143 additions and 12 deletions
+115 -12
View File
@@ -19,13 +19,61 @@ public class TuringProbe {
private final AndroidEmulator emulator;
private final VM vm;
static final long[] IMPORT_SLOTS = {0x45908, 0x45910, 0x45918, 0x45920, 0x45928, 0x45930, 0x45938, 0x45940, 0x45948, 0x45950, 0x45958, 0x45960, 0x45968, 0x45970, 0x45978, 0x45980, 0x45988, 0x45990, 0x45998, 0x459a0, 0x459a8, 0x459b0, 0x459b8, 0x459c0, 0x459c8, 0x459d0, 0x459d8, 0x459e0, 0x459e8, 0x459f0, 0x459f8, 0x45a00, 0x45a08, 0x45a10, 0x45a18, 0x45a20, 0x45a28, 0x45a30, 0x45a38, 0x45a40, 0x45a48, 0x45a50, 0x45a58, 0x45a60, 0x45a68, 0x45a70, 0x45a78, 0x45a80, 0x45a88, 0x45a90, 0x45a98, 0x45aa0, 0x45aa8, 0x45ab0, 0x45ab8, 0x45ac0, 0x45ac8, 0x45ad0, 0x45ad8, 0x45ae0, 0x45ae8, 0x45af0, 0x45af8, 0x45b00, 0x45b08, 0x45b10, 0x45b18, 0x45b20, 0x45b28, 0x45b30, 0x45b38, 0x45b40, 0x45b48, 0x45b50, 0x45b58, 0x45b60, 0x45b68, 0x45b70, 0x45b78, 0x45b80, 0x45b88, 0x45b90, 0x45b98, 0x45ba0, 0x45ba8, 0x45bb0, 0x45bb8, 0x45bc0, 0x45bc8, 0x45bd0, 0x45bd8, 0x45be0, 0x45be8, 0x45bf0, 0x45bf8, 0x45c00, 0x45c08, 0x45c10, 0x45c18, 0x45c20, 0x45c28, 0x45c30, 0x45c38, 0x45c40, 0x45c48, 0x45c50, 0x45c58, 0x45c60, 0x45c68, 0x45c70, 0x45c78, 0x45c80, 0x45c88, 0x45c90, 0x45c98, 0x45ca0, 0x45ca8, 0x45cb0, 0x45cb8, 0x45cc0, 0x45cc8};
static final String[] IMPORT_NAMES = {"popen", "deflateEnd", "pthread_mutex_destroy", "pthread_create", "strtoull", "realloc", "open", "pthread_key_create", "inflate", "pthread_attr_setdetachstate", "__cxa_finalize", "ftell", "regcomp", "mkdir", "calloc", "fread", "strncmp", "read", "ASensor_getVendor", "strtoul", "fstat", "pthread_mutex_init", "vsnprintf", "closedir", "strlcpy", "lstat", "qsort", "__cxa_guard_acquire", "__errno", "srand", "ASensor_getName", "snprintf", "_ZdlPv", "dl_iterate_phdr", "deflateInit_", "sscanf", "pthread_mutexattr_settype", "deflate", "write", "getenv", "strcasecmp", "strrchr", "access", "time", "rand", "regfree", "memcmp", "fclose", "mincore", "rewind", "__stack_chk_fail", "fgets", "_Znwm", "pthread_attr_init", "gettimeofday", "feof", "pthread_mutexattr_init", "__cxa_guard_release", "strtok_r", "ioctl", "pthread_setspecific", "strncpy", "opendir", "inet_ntop", "recv", "__android_log_print", "unlink", "inflateEnd", "pthread_attr_destroy", "strchr", "tolower", "fseek", "strcmp", "flock", "_Znam", "pipe2", "pthread_mutexattr_destroy", "syscall", "regexec", "sprintf", "real_syscall", "send", "pthread_mutex_lock", "mmap", "pthread_getspecific", "inflateInit_", "memmove", "if_indextoname", "strsep", "ASensor_getType", "ASensorManager_getInstance", "getuid", "readlink", "strstr", "getpagesize", "strdup", "usleep", "readdir", "fdopen", "strlen", "close", "remove", "stat", "ASensorManager_getSensorList", "rename", "munmap", "ASensor_getResolution", "malloc", "memcpy", "statfs", "memset", "fopen", "socket", "pclose", "strtol", "free", "strcpy", "__system_property_get", "pthread_mutex_unlock", "__cxa_atexit", "_ZdaPv"};
/** 全量补 JUMP_SLOT 导入槽 (懒绑定未解析) */
void patchImports(com.github.unidbg.Module m) {
int ok = 0, miss = 0;
for (int i = 0; i < IMPORT_SLOTS.length; i++) {
String name = IMPORT_NAMES[i];
try {
com.github.unidbg.pointer.UnidbgPointer slot = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, m.base + IMPORT_SLOTS[i]);
if (slot.getLong(0) != 0) continue; // already resolved
com.github.unidbg.Symbol sym = null;
for (com.github.unidbg.Module md : emulator.getMemory().getLoadedModules()) {
try { sym = md.findSymbolByName(name, false); } catch (Throwable t) {}
if (sym != null && sym.getAddress() != 0) break;
}
// __system_property_get 在 libc 里是核心符号
if (sym == null && name.equals("__system_property_get")) {
try {
com.github.unidbg.Module libc = emulator.getMemory().findModule("libc.so");
if (libc != null) sym = libc.findSymbolByName("__system_property_get", false);
} catch (Throwable t) {}
}
if (sym != null && sym.getAddress() != 0) {
slot.setPointer(0, com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, sym.getAddress()));
ok++;
} else miss++;
} catch (Throwable t) { miss++; }
}
System.out.println("[TP] imports patched ok=" + ok + " miss=" + miss);
}
TuringProbe() {
com.github.unidbg.arm.backend.BackendFactory backend = new com.github.unidbg.arm.backend.Unicorn2Factory(true);
com.github.unidbg.arm.backend.BackendFactory backend;
try {
backend = new com.github.unidbg.arm.backend.DynarmicFactory(false);
} catch (Throwable t) { backend = new com.github.unidbg.arm.backend.Unicorn2Factory(true); }
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName("com.duowan.kiwi")
.addBackendFactory(backend)
.build();
emulator.getMemory().setLibraryResolver(new AndroidResolver(23));
// 系统属性钩子 (turing 可能读取)
try {
com.github.unidbg.linux.android.SystemPropertyHook propHook = new com.github.unidbg.linux.android.SystemPropertyHook(emulator);
java.util.Map<String, String> props = new java.util.LinkedHashMap<>();
String[][] propArr = {
{"ro.build.version.sdk","30"}, {"ro.hardware","qcom"}, {"ro.product.board","thyme"},
{"ro.product.brand","Xiaomi"}, {"ro.product.device","thyme"}, {"ro.product.manufacturer","Xiaomi"},
{"ro.product.model","M2102J2SC"}, {"ro.product.name","thyme"}, {"ro.build.type","user"},
};
for (String[] kv : propArr) props.put(kv[0], kv[1]);
propHook.setPropertyProvider(pname -> props.getOrDefault(pname, ""));
emulator.getMemory().addHookListener(propHook);
} catch (Throwable t) { System.out.println("[TP] prophook EXC " + t); }
vm = emulator.createDalvikVM();
vm.setJni(new AbstractJni() {});
vm.setVerbose(true);
@@ -82,16 +130,30 @@ public class TuringProbe {
} catch (Throwable t2) {
System.out.println("[TP] gap map EXC " + t2);
}
// 全局槽基础补丁: base+0x48a90 读取为 NULL -> 塞一个 8B dummy 对象 (引用计数目标)
// 全局字符串构建器补丁: 0x48A90=alloc基址(rc@0 len@8 data@16), 0x48A98=data
try {
long slot = m.base + 0x48a90;
com.github.unidbg.pointer.UnidbgPointer dummyPtr = emulator.getMemory().mmap(8, 7);
dummyPtr.setMemory(0, 8, (byte) 0);
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, slot).setPointer(0, dummyPtr);
System.out.println("[TP] global slot 0x48a90 <- " + Long.toHexString(dummyPtr.peer));
com.github.unidbg.pointer.UnidbgPointer alloc = emulator.getMemory().mmap(24 + 64, 7);
alloc.setMemory(0, 24 + 64, (byte) 0);
alloc.setLong(0, 1L); // rc = 1
alloc.setLong(8, 0L); // len = 0
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, m.base + 0x48a90).setPointer(0, alloc);
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, m.base + 0x48a98).setPointer(0, com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, alloc.peer + 16));
System.out.println("[TP] builder alloc=" + Long.toHexString(alloc.peer));
} catch (Throwable t4) {
System.out.println("[TP] slot patch EXC " + t4);
System.out.println("[TP] builder patch EXC " + t4);
}
// 若有旧 dummy 会覆盖 builder, 故只保留 patchImports
try { patchImports(m); } catch (Throwable t4) { System.out.println("[TP] imports EXC " + t4); }
// 桩掉日志链 sub_11890/11928 (mov w0,#0; ret) — 注册才是主线
try {
for (long stubOf : new long[]{0x11890, 0x11928, 0x3e80, 0x3f70}) {
com.github.unidbg.pointer.UnidbgPointer lp = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, m.base + stubOf);
lp.setInt(0, 0x52800000); // mov w0, #0
lp.setInt(4, 0xd65f03c0); // ret
}
com.github.unidbg.pointer.UnidbgPointer chk = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, m.base + 0x11890);
System.out.println("[TP] log stubs @" + String.format("%08x", chk.getInt(0)) + "/" + String.format("%08x", chk.getInt(4)));
} catch (Throwable t5) { System.out.println("[TP] log stub EXC " + t5); }
try {
dm.callJNI_OnLoad(emulator);
System.out.println("[TP] libturingmfa JNI_OnLoad OK");
@@ -155,11 +217,20 @@ public class TuringProbe {
}
// 尝试读 x0 处内存
try {
long x0 = be.reg_read(unicorn.Arm64Const.UC_ARM64_REG_X0).longValue();
com.github.unidbg.pointer.UnidbgPointer pp = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, x0);
long x19 = be.reg_read(unicorn.Arm64Const.UC_ARM64_REG_X19).longValue();
com.github.unidbg.pointer.UnidbgPointer pp = com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, x19);
byte[] b = pp.getByteArray(0, 32);
System.out.println("[TP] *X0 = " + java.util.HexFormat.of().formatHex(b));
} catch (Throwable t4) { System.out.println("[TP] *X0 unreadable " + t4); }
System.out.println("[TP] *X19 = " + java.util.HexFormat.of().formatHex(b));
} catch (Throwable t4) { System.out.println("[TP] *X19 unreadable " + t4); }
// 打印 0x123d8000 附近的映射区域
try {
for (com.github.unidbg.Module md : emulator.getMemory().getLoadedModules()) {
for (com.github.unidbg.memory.MemRegion r : md.getRegions()) {
if (r.begin > 0x123d9000 || r.end < 0x123d7000) continue;
System.out.println("[TP] region " + Long.toHexString(r.begin) + "-" + Long.toHexString(r.end));
}
}
} catch (Throwable t6) { System.out.println("[TP] regions EXC " + t6); }
} catch (Throwable t2) { System.out.println("[TP] regDump fail " + t2); }
}
@@ -205,10 +276,42 @@ public class TuringProbe {
}
}
/** xtest: 直调 sub_24D58 (XXTEA变体) 解样本密文 @ blob 文件 */
void xtest(String blobPath, String outPath) {
try {
DalvikModule dm = vm.loadLibrary(new File("/Users/yml/codes/douyu_login_py/evidence/dfp_live/libturingmfa.so"), false);
com.github.unidbg.Module m = dm.getModule();
long gapBase = m.base + 0x45000;
try { emulator.getMemory().mmap2(gapBase, 0x648, 7, 0x32, -1, 0); } catch (Throwable t) {}
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
// 密文词数
int n = blob.length / 4;
com.github.unidbg.pointer.UnidbgPointer vp = emulator.getMemory().mmap(blob.length + 32, 7);
for (int i=0;i<blob.length;i++) vp.setByte(i, blob[i]);
// key @ 3DF88 (LOAD-1 identity fileoff==vaddr)
long keyPtr = m.base + 0x3DF88;
System.out.println("[TP] xtest calling sub_24D58 n=" + n + " key@" + Long.toHexString(keyPtr));
Number ret = m.callFunction(emulator, 0x24D58, vp.peer, -n, keyPtr);
System.out.println("[TP] xtest ret=" + ret);
byte[] out = vp.getByteArray(0, Math.min(blob.length, 64));
System.out.println("[TP] xtest out head: " + java.util.HexFormat.of().formatHex(out));
if (outPath != null) {
byte[] full = vp.getByteArray(0, blob.length);
java.io.FileOutputStream fos = new java.io.FileOutputStream(outPath);
fos.write(full); fos.close();
System.out.println("[TP] xtest saved " + outPath);
}
} catch (Throwable t) {
System.out.println("[TP] xtest EXC " + t);
t.printStackTrace(System.out);
}
}
public static void main(String[] args) {
TuringProbe p = new TuringProbe();
String which = args.length > 0 ? args[0] : "both";
p.preResolveClasses();
if (which.equals("xtest")) { p.xtest(args[1], args.length > 2 ? args[2] : null); return; }
if (which.equals("call")) {
p.callEnc(args.length > 1 ? args[1] : "/tmp/dfp_str2.bin");
} else {