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:
@@ -1285,3 +1285,31 @@ dfpReport: tReq = [10B 魔数 57 18 82 cf 66 4b b3 94 01 ee][3988B 加密采
|
||||
- **纯样本分析无法破** (无密钥派生链); 密钥源 = getDfpConfig 81B 服务端下发镜钥 + 设备材料
|
||||
- 破解必须回到加密内核: A (harness onLoad 空槽冲关) / B (ga JNI_OnLoad 注册表反汇编)
|
||||
- 已获价值: 报告时刻可判定 (头 byte6-7 ≈ uptime-ms), 版本/重注册模式可区分
|
||||
|
||||
---
|
||||
|
||||
## §11.55 R25: 💥 加密器识别 — turing mfa XXTEA 变体 + harness 直调运行 (2026-08-29)
|
||||
|
||||
### 关键突破 (IDA 导出 + unidbg 双线合流)
|
||||
1. **JNI 表全解 (11 方法)**: a87_@0x25680 b87_@0x25c98 c87_@0x262a8 d87_@0x26418 e87_@0x26768
|
||||
f87_@0x26edc g87_@0x271d0 h87_@0x274c0 i87_@0x276d0 j87_@0x27a78 k87_@0x27b74
|
||||
(+ onServiceConnected@0x37324) — 类 TNative$aa (com/tencent/turingface/sdk/mfa)
|
||||
2. **b87_ 语义**: JNI 分发壳 (a6==1 → sub_2E4A4!) — **sub_2E4A4 = 解包链**:
|
||||
sub_2E250 { sub_252EC [拷贝 + **sub_24D58 = XXTEA变体解密** + 尾4B长度校验] + sub_34A00 [inflate!] }
|
||||
-> "resp" 存 SparseArray — **输入 = [XOR/XXTEA密文][4B len] → inflate → 明文!!**
|
||||
3. **sub_24D58 反编译 = XXTEA 变体**: 负长度参数 + 0x9E3779B9(2B16!) + 掩码混淆运算
|
||||
(constant: 6CC17688/933E8977/D3115A55/5613AC09/A9EC53F6/B13795CC/4EC86A33/73067D6A/8CF98295)
|
||||
key索引 = (sum>>2&3 变体) — 自定义变体非标准XXTEA
|
||||
4. **harness 直调 sub_24D58 成功**: (v-ptr, -n, key@3DF88) — n=977/976/978 全量运行无崩!
|
||||
输出非zlib — key槽或输入格式待定
|
||||
|
||||
### harness 进展
|
||||
- 导入补丁 115/121 解析 (ASensor 族缺 libandroid)
|
||||
- 0x48A90/0x48A98 构建器全局按语义手工构建 (rc@0 len@8 data@16!)
|
||||
- 日志链桩 (11890/11928/3e80/3f70 = mov-w0-#0;ret)
|
||||
- Unicorn2 = ldxr 原子缺陷; Dynarmic = 过 init 到注册段 (JNI-Env 回调 abort); 三后端均可跑纯函数
|
||||
|
||||
### 下一步 (解密侧续攻)
|
||||
- key 槽验证: 3DF88 结构内偏移 / 或 key 由 config 派生
|
||||
- b87_ 全链 harness 直调 (喂真实捕获密文 → inflate → 明文!)
|
||||
- a87_@0x25680 = 加密侧 (sub_27DA4 链) — "纯代码铸造" 的核心
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user