首页
社区
课程
招聘
[原创]使用Unidbg模拟执行去除OLLVM-BR混淆
发表于: 2025-7-7 13:13 6436

[原创]使用Unidbg模拟执行去除OLLVM-BR混淆

2025-7-7 13:13
6436

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

对libtprt.so中的JNI_Onload函数进行去混淆

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

image-20250324222522858

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

image-20250324222737563

在JNI_Onload下方还能看到许多对寄存器操作的汇编代码,猜测下方的汇编也为JNI_Onload执行的一部分

在这段混淆中我们使用模拟执行对函数进行去混淆

直接在项目的unidbg-android/src/test/java目录下建立我们的模拟执行类:AntiOllvm

加载动态库==>

加载后需要先执行jni_onload,而DalvikModule(dm)这个类已经实现了callJNI_OnLoad方法,我们直接调用即可

image-20250324224556059

可以看到在0x87670处进行了RegisterNative,注册的函数名为:initialize,地址在0x86e34

到这一步我们成功完成了使用Unidbg对安卓动态库的运行,并且正常运行了动态库的Jni_Onload函数

我们使用hook将每一步运行过的指令都打印出来

image-20250325010000849

我们可以看到br x9往后执行的指令就是汇编代码中BR之后的指令

这段代码在unidbg中的作用是为指定模块的代码段添加指令级动态跟踪钩子,其效果是实时反汇编并打印该模块每条执行指令的详细信息。

核心功能解析

钩子注册

指令反汇编

输出格式

image-20250325114221585

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

image-20250326002335785

X24的值是一个数组

image-20250326002507046

数组里面分别存了很多指令的地址,用于后续跳转使用

整体逻辑就是每次根据比较结果在数组中选择一个offset,然后用offset + base,得到真实的跳转地址

image-20250326002701682

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

image-20250326003109215

image-20250326003124020

W8:0x3202B1A5

image-20250326003207910

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 / BLOB对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():

辅助方法:

其实是单独使用一个模拟器容易冲突实力不够:(

image-20250707115957916

    public AntiOllvm() {
//        创建模拟器
        emulator = AndroidEmulatorBuilder
                .for64Bit()
                .addBackendFactory(new Unicorn2Factory(true))
                .setProcessName("com.example.antiollvm")
                .build();
        Memory memory = emulator.getMemory();
//        安卓SDK版本
        memory.setLibraryResolver(new AndroidResolver(23));
//        创建虚拟机
        vm = emulator.createDalvikVM();
        vm.setVerbose(true);
 
//        libtprt.so的依赖库
        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();
//        安卓SDK版本
        memory.setLibraryResolver(new AndroidResolver(23));
//        创建虚拟机
        vm = emulator.createDalvikVM();
        vm.setVerbose(true);
 
//        libtprt.so的依赖库
        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) {
            // 主模拟器的 Hook 逻辑
            long relativeAddr = address - module.base;
            if (relativeAddr >= START_ADDR && relativeAddr <= END_ADDR) {
                // 检查是否是已 Patch 地址 (基于最终 Patch 目标)
                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) {
            // 主模拟器的 Hook 逻辑
            long relativeAddr = address - module.base;
            if (relativeAddr >= START_ADDR && relativeAddr <= END_ADDR) {
                // 检查是否是已 Patch 地址 (基于最终 Patch 目标)
                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; // CSEL 执行前的状态
 
    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; // CSEL 执行前的状态
 
    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(); // Skip self
 
    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);
 
                // --- 关键:获取 CSEL 执行前的状态 ---
                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, // cselAbsAddr
                        module.base + brRelativeAddr      // brAbsAddr
                );
                simulationTasks.add(task);
                System.out.printf("  [MainEmu 任务已添加] CSEL 0x%x -> BR 0x%x%n", cselInfo.cselAddress, brRelativeAddr);
 
                // 可选:从 Map 中移除,防止一个 CSEL 被多个 BR 错误匹配
                // cselInfoMap.remove(prevRelativeAddr);
                return;
            }
        }
        searchDepth++;
    }
    // System.err.printf("[MainEmu BR 警告] @0x%x: 未找到 %s 的匹配 CSEL%n", brRelativeAddr, brReg);
}
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(); // Skip self
 
    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);
 
                // --- 关键:获取 CSEL 执行前的状态 ---
                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编辑 ,原因:
上传的附件:
收藏
免费 63
支持
分享
最新回复 (31)
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
图片都挂了? 
2025-7-7 13:59
0
雪    币: 11
活跃值: (720)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-7-7 14:04
0
雪    币: 289
活跃值: (2031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2025-7-7 14:12
0
雪    币: 2505
活跃值: (2208)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
图片没挂,多刷新试试
2025-7-7 14:17
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2025-7-7 14:31
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
666666
2025-7-7 16:14
0
雪    币: 104
活跃值: (7134)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
牛蛙
2025-7-7 17:03
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
牛p
2025-7-7 17:11
0
雪    币: 244
活跃值: (576)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
636
2025-7-7 17:36
0
雪    币: 156
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
太强了,学不明白,我只会用一种取巧的思路
2025-7-7 18:06
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
感谢分享
2025-7-8 03:36
0
雪    币: 227
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
感谢
????
2025-7-8 11:29
0
雪    币: 375
活跃值: (3186)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
14
感谢分享
2025-7-8 11:59
0
雪    币: 4834
活跃值: (4587)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
感谢分享
2025-7-8 12:11
0
雪    币: 3360
活跃值: (8228)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
16
666
2025-7-8 13:24
0
雪    币: 204
活跃值: (195)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
fffdfaf
2025-7-8 14:48
0
雪    币: 213
活跃值: (1306)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
666
2025-7-8 17:24
0
雪    币: 3654
活跃值: (5797)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
感谢分享
2025-7-8 17:49
0
雪    币: 208
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
感谢分享
2025-7-9 11:07
0
雪    币: 5436
活跃值: (2380)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
21

好文章学习了,顺便提一嘴:ARM64 没有Thumb

最后于 2025-7-14 23:15 被SleepAlone编辑 ,原因:
2025-7-14 23:06
0
雪    币: 2505
活跃值: (2208)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
22
SleepAlone 好文章学习了,顺便提一嘴:ARM64&nbsp;没有Thumb
感谢指出
2025-7-15 14:22
0
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
能私吗
2025-7-20 15:58
0
雪    币: 1559
活跃值: (2947)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
24
这种有个局限性,如果跳转地址是从内存里取的,要保存的上下文就很多了,没法快速恢复现场执行。。
2025-7-23 01:16
0
雪    币: 1559
活跃值: (2947)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
25
在找对应的 csel 指令的时候会不会倒着找更合理点,感觉 br 和 csel 不会隔太远的
2025-7-23 01:41
0
游客
登录 | 注册 方可回帖
返回