feat(huya): harness onLoad迭代修复 - 全局槽补丁过引用计数关 (R22)
- 0x48a90槽NULL崩溃 -> dummy对象补丁 -> 推进到vtable空跳 - 根因: turing类定义缺失(需ProxyClassFactory或C结构对象模型) - TuringProbe.java: gap+类+槽三级补丁可复现
This commit is contained in:
@@ -1227,3 +1227,22 @@ dfpReport: tReq = [10B 魔数 57 18 82 cf 66 4b b3 94 01 ee][3988B 加密采
|
||||
- 三种输入 (全POST/cipher-only/magic+cipher) × int{0,1} = 全部确定性 **ret=-1**
|
||||
- 判定: jobject 封送通; -1 = turing 全局初始化态缺失 (config/dat 未载入) 或 SparseArray 内容格式不符
|
||||
- 下一步: (a) onLoad 初始化链修复后直调 (x0 原子问题先解), (b) 或喂 SparseArray 事件内容探格式
|
||||
|
||||
---
|
||||
|
||||
## §11.52 R22: harness onLoad 迭代修复进展 (2026-08-29)
|
||||
|
||||
### 崩因链逐级突破 (每补一个槽位推进一段)
|
||||
1. **0x48a90 全局槽 NULL → 引用计数崩溃** (0x12b6c ldxr x0):
|
||||
- onLoad 的 init 函数 0x11690 先读该槽 (adrp 0x48000 + ldr #0xa90) 再 refcount
|
||||
- 真机上该槽在 init 链前序已赋值; harness 中静默为空
|
||||
- **补丁: 塞 8B dummy 对象指针 → 过此关!**
|
||||
2. 新崩溃: BR-x8 空跳 (X8=0x0, PC=0x0), X0=X1=0x1203ced5 (rodata 字符串)
|
||||
- 变体函数指针表空槽 / C++ 虚表未建 / PLT 未解析
|
||||
3. 根因深化: turing 类定义 (字段/方法) 缺失 — 预注册的空类无法支撑 JNI 静态字段链
|
||||
- unidbg VM 无 dex 加载 (仅 setDvmClassFactory/ProxyClassFactory 宿主代理)
|
||||
|
||||
### 状态判定
|
||||
- onLoad 全仿真 = 类定义工程 (ProxyClassFactory) 或连续空槽 wack-a-mole — 小时级
|
||||
- 替代: 跳过 onLoad, 直接按 JNI 表构造 b87_@0x25c98 的 C 结构对象模型直调
|
||||
- 进展固化: gap补映射 + 预注册类 + 全局槽补丁 = 已进 TuringProbe.java 可复现
|
||||
|
||||
@@ -82,11 +82,22 @@ public class TuringProbe {
|
||||
} catch (Throwable t2) {
|
||||
System.out.println("[TP] gap map EXC " + t2);
|
||||
}
|
||||
// 全局槽基础补丁: base+0x48a90 读取为 NULL -> 塞一个 8B dummy 对象 (引用计数目标)
|
||||
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));
|
||||
} catch (Throwable t4) {
|
||||
System.out.println("[TP] slot patch EXC " + t4);
|
||||
}
|
||||
try {
|
||||
dm.callJNI_OnLoad(emulator);
|
||||
System.out.println("[TP] libturingmfa JNI_OnLoad OK");
|
||||
} catch (Throwable t3) {
|
||||
System.out.println("[TP] onLoad EXC: " + t3);
|
||||
regDump();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -98,12 +109,20 @@ public class TuringProbe {
|
||||
} 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()));
|
||||
com.github.unidbg.arm.backend.Backend be = emulator.getBackend();
|
||||
int[] regs = {unicorn.Arm64Const.UC_ARM64_REG_PC,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X0,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X1,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X8,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X19,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_LR,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_SP};
|
||||
String[] names = {"PC","X0","X1","X8","X19","LR","SP"};
|
||||
for (int k=0;k<regs.length;k++) {
|
||||
long v = be.reg_read(regs[k]).longValue();
|
||||
System.out.println("[TP] " + names[k] + "=" + Long.toHexString(v)
|
||||
+ (v>=0x12000000L ? " (mod-rel " + Long.toHexString(v - 0x12012000L) + ")" : ""));
|
||||
}
|
||||
} catch (Throwable t2) {
|
||||
System.out.println("[TP] pc-dump fail " + t2);
|
||||
}
|
||||
@@ -111,6 +130,39 @@ public class TuringProbe {
|
||||
}
|
||||
}
|
||||
|
||||
void regDump() {
|
||||
try {
|
||||
com.github.unidbg.arm.backend.Backend be = emulator.getBackend();
|
||||
// 反汇编调用者区域 (LR-0x40 .. LR+0x60)
|
||||
try {
|
||||
long lr = be.reg_read(unicorn.Arm64Const.UC_ARM64_REG_LR).longValue();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
capstone.api.Instruction[] ins = emulator.disassemble(lr - 0x40, 0xa8, 0);
|
||||
for (capstone.api.Instruction i : ins) sb.append(String.format(" 0x%x: %s %s%n", i.getAddress(), i.getMnemonic(), i.getOpStr() == null ? "" : i.getOpStr()));
|
||||
String dasm = sb.toString();
|
||||
System.out.println("[TP] dasm@caller:\n" + dasm);
|
||||
} catch (Throwable t5) { System.out.println("[TP] dasm fail " + t5); }
|
||||
int[] regs = {unicorn.Arm64Const.UC_ARM64_REG_PC, unicorn.Arm64Const.UC_ARM64_REG_X0,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X1, unicorn.Arm64Const.UC_ARM64_REG_X2,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X8, unicorn.Arm64Const.UC_ARM64_REG_X19,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_X20, unicorn.Arm64Const.UC_ARM64_REG_LR,
|
||||
unicorn.Arm64Const.UC_ARM64_REG_SP};
|
||||
String[] names = {"PC","X0","X1","X2","X8","X19","X20","LR","SP"};
|
||||
for (int k=0;k<regs.length;k++) {
|
||||
long v = be.reg_read(regs[k]).longValue();
|
||||
String rel = (v>=0x12012000L && v<0x12018000L) ? " (rel 0x" + Long.toHexString(v - 0x12012000L) + ")" : "";
|
||||
System.out.println("[TP] " + names[k] + "=0x" + Long.toHexString(v) + rel);
|
||||
}
|
||||
// 尝试读 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);
|
||||
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); }
|
||||
} catch (Throwable t2) { System.out.println("[TP] regDump fail " + t2); }
|
||||
}
|
||||
|
||||
/** 直调 mfa 的 b87_ (impl@0x25c98) — JNI: (env, thiz, SparseArray, byte[], Map, int) -> SparseArray */
|
||||
void callEnc(String blobPath) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user