feat(huya): turing JNI表解出+b87_@0x25c98 (R21)

- libturingmfa JNI 表: b87_(SparseArray,[B,Map,I)impl@0x25c98 + f87_ — 加密核心入口
- ELF 间隙洞察: LOAD-2 vaddr未4K对齐->unidbg映射崩因; mmap2补映射过导出
- TuringProbe.java: 装库+预注册类+gap补映射+直调框架(封送待办)
- 路线: 直调b87_喂真实blob->SparseArray明文; ga侧JNI表同法
This commit is contained in:
yml2213
2026-08-29 07:07:18 +08:00
parent 39c516b71b
commit dc109c5dba
7 changed files with 284 additions and 0 deletions
@@ -0,0 +1,163 @@
package hydev;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import java.io.File;
/**
* TuringProbe: unidbg 装载 turing 双库 (libturingga/libturingmfa) + JNI_OnLoad,
* 列出注册的原生方法 -> 定位加密入口偏移。
*
* 用法: java -cp $CP:out hydev.TuringProbe [ga|mfa|both]
*/
public class TuringProbe {
private final AndroidEmulator emulator;
private final VM vm;
TuringProbe() {
com.github.unidbg.arm.backend.BackendFactory backend = new com.github.unidbg.arm.backend.Unicorn2Factory(true);
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName("com.duowan.kiwi")
.addBackendFactory(backend)
.build();
vm = emulator.createDalvikVM();
vm.setJni(new AbstractJni() {});
vm.setVerbose(true);
if (!new File("/Users/yml/codes/douyu_login_py/evidence/dfp_live/turingga.so").exists()) {
System.out.println("[TuringProbe] missing libs in evidence/dfp_live/");
}
}
/** 预注册 turing 相关类 (JNI FindClass 需要) */
void preResolveClasses() {
String[] cls = {
"com/tencent/turingfd/sdk/ams/ga/native",
"com/tencent/turingfd/sdk/ams/ga/TNiT$aa",
"com/tencent/turingfd/sdk/ams/ga/TNiT$if",
"com/tencent/turingfd/sdk/ams/ga/Seedless",
"com/tencent/turingfd/sdk/ams/ga/ITuringDID",
"com/tencent/turingfd/sdk/ams/ga/TuringSDK",
"com/tencent/turingfd/sdk/ams/ga/TNative$aa",
"com/tencent/turingfd/sdk/ams/ga/TNative$if",
"com/tencent/turingfd/sdk/ams/ga/Lemon",
"com/tencent/turingfd/sdk/ams/ga/Pineapple",
"android/util/SparseArray",
};
for (String c : cls) {
try {
vm.resolveClass(c);
System.out.println("[TP] class ok " + c);
} catch (Throwable t) {
System.out.println("[TP] class '" + c + "' EXC " + t);
}
}
}
void load(String which) {
String base = "/Users/yml/codes/douyu_login_py/evidence/dfp_live/";
try {
switch (which) {
case "ga": {
DalvikModule dm = vm.loadLibrary(new File(base + "turingga.so"), false);
dm.callJNI_OnLoad(emulator);
System.out.println("[TP] libturingga JNI_OnLoad OK");
break;
}
case "mfa": {
DalvikModule dm = vm.loadLibrary(new File(base + "libturingmfa.so"), false);
com.github.unidbg.Module m = dm.getModule();
long gapBase = m.base + 0x45000;
try {
// mmap2(addr, len, PROT_READ|WRITE|EXEC=7, MAP_PRIVATE|ANON|FIXED=0x32, -1, 0)
emulator.getMemory().mmap2(gapBase, 0x648, 7, 0x32, -1, 0);
// 清零
com.github.unidbg.pointer.UnidbgPointer.pointer(emulator, gapBase).setMemory(0, 0x648, (byte) 0);
System.out.println("[TP] gap mapped @" + Long.toHexString(gapBase));
} catch (Throwable t2) {
System.out.println("[TP] gap map EXC " + t2);
}
try {
dm.callJNI_OnLoad(emulator);
System.out.println("[TP] libturingmfa JNI_OnLoad OK");
} catch (Throwable t3) {
System.out.println("[TP] onLoad EXC: " + t3);
}
break;
}
default: {
load("ga");
load("mfa");
}
}
} catch (Throwable t) {
System.out.println("[TP] load " + which + " EXC: " + t);
try {
com.github.unidbg.arm.ARM emu = (com.github.unidbg.arm.ARM) emulator;
java.lang.reflect.Field f = emulator.getClass().getSuperclass().getDeclaredField("backend");
f.setAccessible(true);
Object be = f.get(emulator);
java.lang.reflect.Method pc = be.getClass().getMethod("reg_read", int.class);
System.out.println("[TP] PC=" + Long.toHexString(((Number) pc.invoke(be, com.github.unidbg.arm.backend.Unicorn2Factory.class.getField("UC_ARM64_REG_PC").getLong(null))).longValue()));
} catch (Throwable t2) {
System.out.println("[TP] pc-dump fail " + t2);
}
t.printStackTrace(System.out);
}
}
/** 直调 mfa 的 b87_ (impl@0x25c98) — JNI: (env, thiz, SparseArray, byte[], Map, int) -> SparseArray */
void callEnc(String blobPath) {
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 t2) {}
byte[] blob = new java.io.FileInputStream(blobPath).readAllBytes();
// SparseArray obj
DvmObject<?> sa = vm.resolveClass("android/util/SparseArray").newObject(null);
DvmObject<?> dba = vm.resolveClass("[B").newObject(blob);
DvmObject<?> map = vm.resolveClass("java/util/HashMap").newObject(null);
// 直调 impl: module.base + 0x25c98; 参数: env, null, sa, dba, map, 1
long fn = m.base + 0x25c98;
System.out.println("[TP] calling b87_ @ " + Long.toHexString(fn) + " blob=" + blob.length + "B");
Number ret = m.callFunction(emulator, fn, vm.getJavaVM(), null, sa, dba, map, 1);
System.out.println("[TP] b87_ ret=" + ret);
} catch (Throwable t) {
System.out.println("[TP] callEnc EXC " + t);
t.printStackTrace(System.out);
}
}
void listNatives() {
try {
java.lang.reflect.Method m = BaseVM.class.getDeclaredMethod("listNativeMethods");
// 尝试不同的内部访问; 失败则走 vm 自省
System.out.println("[TP] vm class methods: ");
for (java.lang.reflect.Method mm : vm.getClass().getDeclaredMethods()) {
if (mm.getName().toLowerCase().contains("native") || mm.getName().toLowerCase().contains("register"))
System.out.println(" " + mm.getName() + " " + java.util.Arrays.toString(mm.getParameterTypes()));
}
} catch (Throwable t) {
System.out.println("[TP] listNatives EXC " + t);
}
}
public static void main(String[] args) {
TuringProbe p = new TuringProbe();
String which = args.length > 0 ? args[0] : "both";
p.preResolveClasses();
if (which.equals("call")) {
p.callEnc(args.length > 1 ? args[1] : "/tmp/dfp_str2.bin");
} else {
p.load(which);
p.listNatives();
}
System.out.println("[TP] done");
}
}