项目地址:b72K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6m8j5i4t1H3L8U0x3&6x3o6k6Q4x3V1k6m8L8Y4c8A6i4K6u0V1b7W2u0Q4x3X3c8a6j5X3j5`.
许多去除BR寄存器混淆的脚本都是使用汇编中的立即数进行计算,进而判断CSEL中的分支结构,于是本人就想着使用模拟执行走完真假分支从而省去计算的步骤,使得去除混淆的效率更高更方便 (应该
对libtprt.so中的JNI_Onload函数进行去混淆

可以发现在函数后方使用了BR X9作为间接跳转,IDA无法分析控制流了,因为在此处X9为寄存器,在未执行时不知道寄存器的值为多少,所以静态看我们无法了解程序往哪走

F5反编译后可以看到jni->GetEnv函数后,执行BR X9后就无法看到其余逻辑了

在JNI_Onload下方还能看到许多对寄存器操作的汇编代码,猜测下方的汇编也为JNI_Onload执行的一部分
在这段混淆中我们使用模拟执行对函数进行去混淆
直接在项目的unidbg-android/src/test/java目录下建立我们的模拟执行类:AntiOllvm
加载动态库==>
加载后需要先执行jni_onload,而DalvikModule(dm)这个类已经实现了callJNI_OnLoad方法,我们直接调用即可

可以看到在0x87670处进行了RegisterNative,注册的函数名为:initialize,地址在0x86e34
到这一步我们成功完成了使用Unidbg对安卓动态库的运行,并且正常运行了动态库的Jni_Onload函数
我们使用hook将每一步运行过的指令都打印出来

我们可以看到br x9往后执行的指令就是汇编代码中BR之后的指令
这段代码在unidbg中的作用是为指定模块的代码段添加指令级动态跟踪钩子,其效果是实时反汇编并打印该模块每条执行指令的详细信息。
核心功能解析
钩子注册
指令反汇编
输出格式

X27的值由MOV和MOVK分别赋值8位和16位的值,固定为 ==> 0x84FA7910

X24的值是一个数组

数组里面分别存了很多指令的地址,用于后续跳转使用
整体逻辑就是每次根据比较结果在数组中选择一个offset,然后用offset + base,得到真实的跳转地址

CMP W8, W25中的W8和W25的数值也是写死的


W8:0x3202B1A5

W25:0x58F48322
以上方代码为例
当CC条件满足时,X21的值赋给X9作为一个offset,在LDR X9,[X24,X9]中使用X24的数组+偏移
根据CSEL的CC条件有两个分支如下:
True Addr: (*(X24+X21) + X27)
False Addr: (*(X24+X25) + X27)
那么我们可以根据CMP的结果使用BCC / BLO和B对True Addr和False Addr进行跳转
替换后的汇编如下
这样的间接跳转都变为了直接跳转,ida内就可以继续分析了,并且地址也没有变化,因为寄存器的值已知,我们只是其他将他计算出来再跳转而已。
代码的核心目标是自动化修复一种特定的代码混淆技术。这种混淆使用 ARM64 的 csel (条件选择) 指令和 br (间接跳转) 指令来隐藏真实的跳转目标。
原始混淆代码:
修复后代码:
为了安全准确地找到 <目标地址1> (T) 和 <目标地址2> (F),代码采用了双模拟器的方法。
阶段 1: 发现与收集混淆特征 (使用主模拟器 emulator)
阶段 2: 分支模拟与 Patch 生成 (使用临时模拟器 tmpEmulator)
阶段 3: 应用 Patch
将 patches 列表中的code写入文件缓冲区的对应位置。
将修改后的数据写入新的 .so-patch文件。
tmpEmulator, MainEmulator: 临时模拟器及其相关组件。用于安全地执行分支模拟。为什么需要两个? 避免在主模拟器运行时进行分支模拟可能导致的状态污染(寄存器、内存、Hook 状态被意外修改)。在写这段代码的时候尝试使用一个emulator,但很容易在patch后往下走的分支造成非法内存访问,所以我选择使用两个emu分别进行特征收集和patch执行,这样代码的健壮性会高很多。
insStack: Deque<InstructionContext>。存储最近执行的指令及其执行前的寄存器状态。为什么需要? 当遇到 br 时,需要回溯查找之前的 csel,并且需要知道 csel 执行前的状态才能正确模拟。
cselInfoMap: Map<Long, CselInfo>。存储遇到的 csel 指令的详细信息,以其相对地址作为 Key,方便快速查找。
DeOllvmBr_TwoEmus():
setupMainEmulatorHooks():
processInstruction():
handleConditionalSelect():
handleBranchInstruction():
解析 br 指令,获取目标寄存器名。
回溯 insStack: 查找最近执行的指令。
检查历史指令是否是 cselInfoMap 中记录的 csel。
如果找到 csel,并且其目标寄存器与 br 使用的寄存器匹配:
performSimulationsOnTmpEmu():
performSingleSimulation():
generatePatch():
辅助方法:
其实是单独使用一个模拟器容易冲突实力不够:(

public AntiOllvm() {
emulator = AndroidEmulatorBuilder
.for64Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.example.antiollvm")
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM();
vm.setVerbose(true);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libc.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libm.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libdl.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libstdcpp.so"),false);
dm = vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/test/resources/AntiOllvm/libtprt.so"), false);
module = dm.getModule();
}
public AntiOllvm() {
emulator = AndroidEmulatorBuilder
.for64Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.example.antiollvm")
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM();
vm.setVerbose(true);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libc.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libm.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libdl.so"),false);
vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/main/resources/android/sdk23/lib64/libstdcpp.so"),false);
dm = vm.loadLibrary(new File("D:/unidbg/unidbg-android/src/test/resources/AntiOllvm/libtprt.so"), false);
module = dm.getModule();
}
public void callJniOnload(){
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
AntiOllvm AO = new AntiOllvm();
AO.callJniOnload();
}
public void callJniOnload(){
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
AntiOllvm AO = new AntiOllvm();
AO.callJniOnload();
}
public void logIns()
{
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
System.out.printf("%x:%s %s\n",address-module.base ,disasm[0].getMnemonic(),disasm[0].getOpStr());
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base+start, module.base+end, null);
}
public void logIns()
{
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64,Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
System.out.printf("%x:%s %s\n",address-module.base ,disasm[0].getMnemonic(),disasm[0].getOpStr());
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base+start, module.base+end, null);
}
emulator.getBackend().hook_add_new(new CodeHook() { ... }, module.base, module.base+module.size, null);
emulator.getBackend().hook_add_new(new CodeHook() { ... }, module.base, module.base+module.size, null);
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM);
byte[] bytes = emulator.getBackend().mem_read(address, 4);
Instruction[] disasm = capstone.disasm(bytes, 0);
System.out.printf("%x:%s %s\n", address - module.base, disasm[0].getMnemonic(), disasm[0].getOpStr());
System.out.printf("%x:%s %s\n", address - module.base, disasm[0].getMnemonic(), disasm[0].getOpStr());
private final Deque<InstructionContext> insStack = new ArrayDeque<>(128);
private final Deque<InstructionContext> insStack = new ArrayDeque<>(128);
private final Map<Long, CselInfo> cselInfoMap = new HashMap<>();
private final Map<Long, CselInfo> cselInfoMap = new HashMap<>();
private void setupMainEmulatorHooks() {
if (this.mainHook != null) {
this.mainHook.unhook();
this.mainHook = null;
}
System.out.println(" [Hook管理] 正在添加主模拟器 Hook...");
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
long relativeAddr = address - module.base;
if (relativeAddr >= START_ADDR && relativeAddr <= END_ADDR) {
if (!patchedAddresses.contains(relativeAddr)) {
processInstruction(address, size, backend);
}
}
}
@Override
public void onAttach(UnHook unHook) {
System.out.println(" [Hook管理] 主模拟器 Hook 已附加。");
DeOllvmBr_TwoEmus.this.mainHook = unHook;
}
@Override
public void detach() {
System.out.println(" [Hook管理] 主模拟器 Hook 已分离。");
}
}, module.base + START_ADDR, module.base + END_ADDR, null);
}
private void setupMainEmulatorHooks() {
if (this.mainHook != null) {
this.mainHook.unhook();
this.mainHook = null;
}
System.out.println(" [Hook管理] 正在添加主模拟器 Hook...");
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
long relativeAddr = address - module.base;
if (relativeAddr >= START_ADDR && relativeAddr <= END_ADDR) {
if (!patchedAddresses.contains(relativeAddr)) {
processInstruction(address, size, backend);
}
}
}
@Override
public void onAttach(UnHook unHook) {
System.out.println(" [Hook管理] 主模拟器 Hook 已附加。");
DeOllvmBr_TwoEmus.this.mainHook = unHook;
}
@Override
public void detach() {
System.out.println(" [Hook管理] 主模拟器 Hook 已分离。");
}
}, module.base + START_ADDR, module.base + END_ADDR, null);
}
private void processInstruction(long absAddress, int size, Backend backend) {
try {
long relativeAddr = absAddress - module.base;
if (patchedAddresses.contains(relativeAddr)) {
return;
}
List<Number> currentRegisters = saveRegisters(backend);
byte[] code = backend.mem_read(absAddress, size);
Instruction[] insns = capstone.disasm(code, absAddress, 1);
if (insns == null || insns.length == 0) return;
Instruction ins = insns[0];
InstructionContext context = new InstructionContext(relativeAddr, ins, currentRegisters);
insStack.push(context);
if (insStack.size() > 100) insStack.pollLast();
System.out.printf("[MainEmu 执行] 0x%x (Rel: 0x%x): %s %s%n",
ins.getAddress(), relativeAddr, ins.getMnemonic(), ins.getOpStr());
if ("csel".equalsIgnoreCase(ins.getMnemonic())) {
handleConditionalSelect(context);
} else if ("br".equalsIgnoreCase(ins.getMnemonic())) {
handleBranchInstruction(context);
}
} catch (Exception e) {
System.err.printf("处理主模拟器指令错误 @ 0x%x: %s%n", absAddress, e.getMessage());
e.printStackTrace();
}
}
private void processInstruction(long absAddress, int size, Backend backend) {
try {
long relativeAddr = absAddress - module.base;
if (patchedAddresses.contains(relativeAddr)) {
return;
}
List<Number> currentRegisters = saveRegisters(backend);
byte[] code = backend.mem_read(absAddress, size);
Instruction[] insns = capstone.disasm(code, absAddress, 1);
if (insns == null || insns.length == 0) return;
Instruction ins = insns[0];
InstructionContext context = new InstructionContext(relativeAddr, ins, currentRegisters);
insStack.push(context);
if (insStack.size() > 100) insStack.pollLast();
System.out.printf("[MainEmu 执行] 0x%x (Rel: 0x%x): %s %s%n",
ins.getAddress(), relativeAddr, ins.getMnemonic(), ins.getOpStr());
if ("csel".equalsIgnoreCase(ins.getMnemonic())) {
handleConditionalSelect(context);
} else if ("br".equalsIgnoreCase(ins.getMnemonic())) {
handleBranchInstruction(context);
}
} catch (Exception e) {
System.err.printf("处理主模拟器指令错误 @ 0x%x: %s%n", absAddress, e.getMessage());
e.printStackTrace();
}
}
private void handleConditionalSelect(InstructionContext currentContext) {
Instruction ins = currentContext.instruction;
long relativeAddr = currentContext.relativeAddr;
String opStr = ins.getOpStr();
String[] ops = opStr.split(",\\s*");
if (ops.length < 4) return;
String destReg = ops[0].trim();
String trueReg = ops[1].trim();
String falseReg = ops[2].trim();
String condition = ops[3].trim().toLowerCase();
List<Number> registersBeforeCsel = currentContext.registers;
try {
long trueSourceValue = getRegisterValue(trueReg, registersBeforeCsel);
long falseSourceValue = getRegisterValue(falseReg, registersBeforeCsel);
CselInfo info = new CselInfo(relativeAddr, destReg, condition, trueReg, falseReg, trueSourceValue, falseSourceValue);
cselInfoMap.put(relativeAddr, info);
System.out.printf("[MainEmu CSEL 发现] @0x%x: %s = %s ? %s(0x%x) : %s(0x%x). Cond: %s%n",
relativeAddr, destReg, condition, trueReg, trueSourceValue, falseReg, falseSourceValue, condition);
} catch (IllegalArgumentException e) {
System.err.printf("[MainEmu CSEL 错误] @0x%x: %s%n", relativeAddr, e.getMessage());
}
}
private void handleConditionalSelect(InstructionContext currentContext) {
Instruction ins = currentContext.instruction;
long relativeAddr = currentContext.relativeAddr;
String opStr = ins.getOpStr();
String[] ops = opStr.split(",\\s*");
if (ops.length < 4) return;
String destReg = ops[0].trim();
String trueReg = ops[1].trim();
String falseReg = ops[2].trim();
String condition = ops[3].trim().toLowerCase();
List<Number> registersBeforeCsel = currentContext.registers;
try {
long trueSourceValue = getRegisterValue(trueReg, registersBeforeCsel);
long falseSourceValue = getRegisterValue(falseReg, registersBeforeCsel);
CselInfo info = new CselInfo(relativeAddr, destReg, condition, trueReg, falseReg, trueSourceValue, falseSourceValue);
cselInfoMap.put(relativeAddr, info);
System.out.printf("[MainEmu CSEL 发现] @0x%x: %s = %s ? %s(0x%x) : %s(0x%x). Cond: %s%n",
relativeAddr, destReg, condition, trueReg, trueSourceValue, falseReg, falseSourceValue, condition);
} catch (IllegalArgumentException e) {
System.err.printf("[MainEmu CSEL 错误] @0x%x: %s%n", relativeAddr, e.getMessage());
}
}
private void handleBranchInstruction(InstructionContext brContext) {
Instruction brIns = brContext.instruction;
long brRelativeAddr = brContext.relativeAddr;
String brReg = brIns.getOpStr().trim();
System.out.printf("[MainEmu BR 发现] @0x%x: br %s. 查找匹配 CSEL...%n", brRelativeAddr, brReg);
int searchDepth = 0;
int maxSearchDepth = 30;
Iterator<InstructionContext> it = insStack.iterator();
if (it.hasNext()) it.next();
while (it.hasNext() && searchDepth < maxSearchDepth) {
InstructionContext prevContext = it.next();
long prevRelativeAddr = prevContext.relativeAddr;
if (cselInfoMap.containsKey(prevRelativeAddr)) {
CselInfo cselInfo = cselInfoMap.get(prevRelativeAddr);
if (cselInfo.destinationRegister.equalsIgnoreCase(brReg)) {
System.out.printf(" [MainEmu BR 匹配] CSEL @0x%x. 创建模拟任务...%n", prevRelativeAddr);
InstructionContext cselContext = findInstructionContext(prevRelativeAddr);
if (cselContext == null) {
System.err.printf(" [MainEmu 错误] 无法找到 CSEL @0x%x 的上下文! 跳过任务创建.%n", prevRelativeAddr);
return;
}
List<Number> registersBeforeCsel = cselContext.registers;
SimulationTask task = new SimulationTask(
cselInfo,
brRelativeAddr,
registersBeforeCsel,
module.base + cselInfo.cselAddress,
module.base + brRelativeAddr
);
simulationTasks.add(task);
System.out.printf(" [MainEmu 任务已添加] CSEL 0x%x -> BR 0x%x%n", cselInfo.cselAddress, brRelativeAddr);
return;
}
}
searchDepth++;
}
}
private void handleBranchInstruction(InstructionContext brContext) {
Instruction brIns = brContext.instruction;
long brRelativeAddr = brContext.relativeAddr;
String brReg = brIns.getOpStr().trim();
System.out.printf("[MainEmu BR 发现] @0x%x: br %s. 查找匹配 CSEL...%n", brRelativeAddr, brReg);
int searchDepth = 0;
int maxSearchDepth = 30;
Iterator<InstructionContext> it = insStack.iterator();
if (it.hasNext()) it.next();
while (it.hasNext() && searchDepth < maxSearchDepth) {
InstructionContext prevContext = it.next();
long prevRelativeAddr = prevContext.relativeAddr;
if (cselInfoMap.containsKey(prevRelativeAddr)) {
CselInfo cselInfo = cselInfoMap.get(prevRelativeAddr);
if (cselInfo.destinationRegister.equalsIgnoreCase(brReg)) {
System.out.printf(" [MainEmu BR 匹配] CSEL @0x%x. 创建模拟任务...%n", prevRelativeAddr);
InstructionContext cselContext = findInstructionContext(prevRelativeAddr);
if (cselContext == null) {
System.err.printf(" [MainEmu 错误] 无法找到 CSEL @0x%x 的上下文! 跳过任务创建.%n", prevRelativeAddr);
return;
}
List<Number> registersBeforeCsel = cselContext.registers;
SimulationTask task = new SimulationTask(
cselInfo,
brRelativeAddr,
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-8-11 09:55
被Aar0n编辑
,原因: