ollvm反混淆实战

[原创]记一次基于unidbg模拟执行的去除ollvm混淆-Android安全-看雪-安全社区|安全招聘|kanxue.com

参考上面的博客进行操作。这里记录操作细节

配置unidbg框架

git clone --recursive https://github.com/zhaoboy9692/unidbgweb.git

 然后在unidbg-android中添加自定义类

文中需要添加5个system的lib,和5个lib64,因为文中的代码说了androidapi=26,所以从下面谷歌官网下载android8.0的镜像。 

 https://developers.google.com/android/ota?hl=zh-cn#sailfish

 https://dl.google.com/dl/android/aosp/sailfish-ota-opr3.170623.013-3c966cab.zip?hl=zh-cn

下载之后里面是一个payload.bin,使用payload-dumperX64.exe对这个文件解密,用7zip打开解压出来的system.img,找到需要的这10个文件,放到lib和libs两个文件夹中。

测试jni调用记录

import capstone.Capstone;
import capstone.api.Instruction;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;


public class AntiOllvm1 {
    private AndroidEmulator emulator;
    private VM vm;
    private DalvikModule dm;
    private Module module;
    private long start= 0x5E388;
    private long end = 0x5E7A0;
    private static final String inName = "C:\\Users\\qd_er\\Desktop\\Android\\ollvm\\deobfuscation\\libtprt-1.so";
    private static final String outName = "C:\\Users\\qd_er\\Desktop\\Android\\ollvm\\deobfuscation\\libtprt-dumped.so";
    private static final long dispatcher = 0x5E46C;
    private static final long toend = 0x5E6BC;
    public AntiOllvm1()
    {
        //创建模拟器
        emulator = AndroidEmulatorBuilder
                .for64Bit()
                .addBackendFactory(new Unicorn2Factory(true))
                .setProcessName("com.example.antiollvm")
                .build();
        Memory memory = emulator.getMemory();
        //设置andorid系统库版本
        memory.setLibraryResolver(new AndroidResolver(26));
        //创建虚拟机
        vm = emulator.createDalvikVM();
        vm.setVerbose(true);
        //加载动态库
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libc.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libm.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libstdc++.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\ld-android.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libdl.so"),false);
        dm = vm.loadLibrary(new File(inName), false);
        module = dm.getModule();
    }

    public static void main(String[] args) {
        AntiOllvm1 ao = new AntiOllvm1();
        ao.callJniOnload();
    }

    public void callJniOnload() {
        dm.callJNI_OnLoad(emulator);
    }

}

成功记录所有的jni调用


模拟执行记录

import capstone.Capstone;
import capstone.api.Instruction;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;


public class AntiOllvm2 {
    private AndroidEmulator emulator;
    private VM vm;
    private DalvikModule dm;
    private Module module;
    private static final String inName = "C:\\Users\\qd_er\\Desktop\\Android\\ollvm\\deobfuscation\\libtprt-1.so";

    public AntiOllvm2()
    {
        //创建模拟器
        emulator = AndroidEmulatorBuilder
                .for64Bit()
                .addBackendFactory(new Unicorn2Factory(true))
                .setProcessName("com.example.antiollvm")
                .build();
        Memory memory = emulator.getMemory();
        //设置andorid系统库版本
        memory.setLibraryResolver(new AndroidResolver(26));
        //创建虚拟机
        vm = emulator.createDalvikVM();
        vm.setVerbose(true);
        //加载动态库
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libc.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libm.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libstdc++.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\ld-android.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libdl.so"),false);
        dm = vm.loadLibrary(new File(inName), false);
        module = dm.getModule();
    }

    public static void main(String[] args) {
        AntiOllvm2 ao = new AntiOllvm2();
        ao.logIns();
        ao.callJniOnload();
    }

    public void callJniOnload() {
        dm.callJNI_OnLoad(emulator);
    }

    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, module.base+module.size, null);
    }
}

处理间接调用

处理间接调用导致的JNI_Onload函数无法F5的问题。

这里一直往下翻,找到RET,IDA也会标注出来这是从JNI_Onload的开始starts的。61F14这个位置就是JNI_Onload,如下面三张图所示。

这个ret也可以通过在模拟执行的时候的log的最后看到

  

 这里我定位到ret的时候其实是看了他代码里的偏移量

    private long start= 0x5E388;
    private long end = 0x5E7A0;

这里因为作者自己没有放出来so文件,用的下面一个评论当中的so,这里偏移的大小是不变的。大小也刚好吻合,所以确定设置方法。

这里重新设置start和end

    private long start= 0x61f14;
    private long end = 0x6232c;

这里要把两个变量初始化,一开始忘了初始化,报的错误和应该修复的地方毫不相干。 

 之后运行代码,获得patch之后的so文件。可以看到整体的结构已经出来了,但是还有几个地方需要进行修复(说明刚才的策略还有问题,这里因为已经能够看到大体的结构,所以去做修复)

 可以很明显看到最顶上的块存在问题。F5看一下

 这几个都有问题

原因是因为在模拟执行的是后并没有走遍所有的分支,我们只是将走过的分支处理了。

接下来需要在jumpout的地方手动修改寄存器的值,来控制他的走势,让unidbg最终走完所有分支。这里下面有3个BR,上面还有3个对应异常的B。这里我们要对这里的寄存器进行控制。

 比如这个BR X9的分支,在默认情况下的模拟执行,并没有跑到这个位置。(术语讲,就是代码覆盖率不高,没有覆盖到这个位置)。所以我们需要在模拟执行的时候,控制这里的W12,让他执行到没解析到的块。

这里用一种不是很优雅的方式解决,也就是逐个手动选取地址修复。

更优雅的方式应该是自动发现这种没有恢复出来的BR X9的块,然后向前推导对应没有执行到的条件,并且再进行一次程序运行,对每个BR X9块进行修复。修复过程中注意路径爆炸问题和死循环问题。路径爆炸问题可以结合实际功能复杂度设计调用深度,死循环可以设置强行patch位,当发现程序又走了一次之后,直接强行patch掉。

可以看到这一处的跳转和w12有关,我们找到给W8赋值为W12的地方。使用下面的idapython脚本进行查找。

import re
import idaapi

# 定义你的正则表达式
pattern = re.compile(r".*W8.*W12.*")

# 定义起始和结束地址
start_addr =  0x61f14
end_addr =  0x6232c
print("vvvvvvvvvvvv")

# 遍历每个地址
for ea in range(start_addr, end_addr, 4):
    # 获取该地址的汇编指令
    disasm = idaapi.generate_disasm_line(ea, 0)
    # 使用你的正则表达式匹配指令
    if pattern.search(disasm):
        # 如果匹配,打印出地址
        print(hex(ea),disasm)

print("^^^^^^^^^^^^^")

在给定范围内找到了唯一的赋值指令。发现这个唯一的语句就在这两个块的地址连续区。

 B.LT是带符号数小于。

所以我们跟踪0x62138时的W8和0的CMP,以及对下面B.LT前面的CMP的影响。

public void do_processbr()
    {
        Instruction ins = instructions.peek().getIns();
        if(instructions.peek().getAddr() - module.base == 0x62138){
            System.out.print("0X62138: W8 =");
            System.out.println(getRegValue("w8",instructions.peek().getRegs()).intValue());
            //emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_W8, 1);
        }
        if(instructions.peek().getAddr() - module.base == 0x62144){
            System.out.print("0X62144: W8 =");
            System.out.println(getRegValue("w8",instructions.peek().getRegs()).intValue());
            System.out.print("0X62144: W12=");
            System.out.println(getRegValue("w12",instructions.peek().getRegs()).intValue());
        }
......
    }

 

 显然只执行了一次,然后漏掉了一个分支。

这里可以看出,W8<-W11时,之后的W8<W12不成立。所以根据下一句CSEL ...,EQ的这个EQ条件取反,让W8!=0,就可以走到另一个路径上。

这里看着看着才明白,读编译器源码/LLVM及其pass源码的重要。

所以我们让W8=1;

public void do_processbr()
    {
        Instruction ins = instructions.peek().getIns();
        if(instructions.peek().getAddr() - module.base == 0x62138){
            emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_W8, 1);
        }
.......
    }

可以看出已经走到了另一个分支 

 因为改了分支之后,他原本的Jni_OnLoad函数就是会返回-1。但是unidbg源码里面如果jni_onload返回-1,就抛异常。这个可以修改BaseVM.java第210行那里,在jni_onload返回-1的时候,让他打一条log而不是抛异常

  进行修改:

 可以看到已经成功patch了一个分支。之后的分支需要采用同样的办法。

在逐个对每个JUMPOUT修复时,目前只能修一个,生成一次patched文件,其实是因为这个代码,运行一次之后,只过了一次程序流程,应该可以让每一个reg_write单独工作或者协同工作,这个如果后需要写自动化工具的话都需要注意。

到这里就可以恢复出f5的完整形状了

 


 对OLLVM结构进行恢复

考虑到编译的结果要在逻辑上和原来的代码完全一致,所以一个重要的结论是——真实的代码块一定在cmp eq 之后。(switch case 的一个case)

控制流平坦化对于顺序执行的处理比较简单,直接在前一个真实块的末尾,将索引值改为下一个真实块的索引,然后跳转到主分发器即可。

对于分支执行(判断,循环),在条件的部分会有一个条件选择指令CSEl,将索引寄存器的值根据条件结果设置为不同的索引,然后跳转到主分发器。

所以我们可以按照以下算法对控制流平坦化进行还原:

1.建立一个指令栈,每条指令执行前,保留该指令的地址,指令内容,当前所有寄存器的值。然后将当前指令的信息push进指令栈。

2.对指令进行回溯,如果栈顶指令是b.eq,则进行3(收集真实块),如果栈顶指令是直接跳转指令b,则进行5(处理分支块),否则继续执行下一条指令。

3.向上回溯指令栈,找到第一条cmp 指令,判断是否为与索引寄存器w8 比较,如果是,则进行4,否则继续执行下一条指令。

4.获取与w8比较的另一个寄存器的值,获取b.eq的目标地址,组成一个(索引,真实块)对。继续执行下一条指令。

5.判断上一条指令是否为CSEL W8。如果是,则记录一个(条件成立块索引,条件不成立块索引)对。否则,继续执行下一条指令。

同时,我们在主分发器处hook 指令,记录每次经过主分发器时的索引寄存器的值为索引值顺序。

在模拟执行收集完成信息之后,做以下处理:

1.去除索引值顺序中为条件块的索引。

2.获取索引值顺序中的第一个值,找到对应的真实块。将主分发器处patch为跳转向第一个真实块的跳转指令。

3.根据真实块结束时的新的索引值,寻找到对应的真实块,将结尾处patch为跳转到下一个真实块。

4.在CSEL指令处,根据条件成立块索引和条件不成立索引找到对应的真实块。将CSEL指令和后面的跳转向主分发器的指令patch为两条指令:

第一条为条件成立时的条件跳转,第二条为条件不成立时的直接跳转。

 

import capstone.Capstone;
import capstone.api.Instruction;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneEncoded;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;


public class AntiOllvm5 {
    private AndroidEmulator emulator;
    private VM vm;
    private DalvikModule dm;
    private Module module;
    private long start= 0x61f14;
    private long end = 0x6232c;
    private static final String inName = "C:\\Users\\qd_er\\Desktop\\Android\\ollvm\\deobfuscation\\libtprt-dumped-5.so";
    private static final String outName = "C:\\Users\\qd_er\\Desktop\\Android\\ollvm\\deobfuscation\\libtprt-repaired.so";


    private Stack<InsAndCtx> instructions;
    private List<PatchIns> patchs;
    private static final long dispatcher = 0x61FF8;
//    private static final long toend = 0x5E6BC;
//    记录真实块
    private List<TrueBlock>tbs;
//    记录条件块
    private List<selectBr> sbs ;
//    记录索引顺序
    private List<Long> indexOrder;

    public AntiOllvm5()
    {
        //创建模拟器
        instructions = new Stack<>();
        patchs = new ArrayList<>();
        tbs = new ArrayList<>();
        sbs = new ArrayList<>();
        indexOrder = new ArrayList<>();
        emulator = AndroidEmulatorBuilder
                .for64Bit()
                .addBackendFactory(new Unicorn2Factory(true))
                .setProcessName("com.example.antiollvm")
                .build();
        Memory memory = emulator.getMemory();
        //设置andorid系统库版本
        memory.setLibraryResolver(new AndroidResolver(26));
        //创建虚拟机
        vm = emulator.createDalvikVM();
        vm.setVerbose(true);
        //加载动态库
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libc.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libm.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libstdc++.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\ld-android.so"),false);
        vm.loadLibrary(new File("C:\\Users\\qd_er\\Desktop\\Android\\unidbg\\lib64\\libdl.so"),false);
        dm = vm.loadLibrary(new File(inName), false);
        module = dm.getModule();
    }

    public static void main(String[] args) {
        AntiOllvm5 ao = new AntiOllvm5();
        ao.processFlt();
        ao.callJniOnload();
        System.out.println("vvvvvvvvvvv");
        for (selectBr sb : ao.sbs) {
            System.out.println("insaddr: 0x" + Long.toHexString(sb.getInsaddr()));
            System.out.println("trueindex: 0x" + Long.toHexString(sb.getTrueindex()));
            System.out.println("falseindex: 0x" + Long.toHexString(sb.getFalseindex()));
            System.out.println("cond: " + sb.getCond());
            System.out.println();
        }
        for (TrueBlock tb : ao.tbs) {
            System.out.println("index: 0x" + Long.toHexString(tb.getIndex()));
            System.out.println("startAddr: 0x" + Long.toHexString(tb.getStartAddr()));
        }
        System.out.println("^^^^^^^^^");
        ao.reorderblock();
        ao.patch();
    }

    public void callJniOnload() {
        dm.callJNI_OnLoad(emulator);
    }

    //遍历patch表,执行patch,生成新的so,使用Ketstone将汇编转为机器码。
    public void patch()    {
        try {
            System.out.println("Begin patch...");
            File f = new File(inName);
            FileInputStream fis = new FileInputStream(f);
            byte[] data = new byte[(int) f.length()];
            fis.read(data);
            fis.close();
            for(PatchIns pi:patchs)
            {
                System.out.println("procrss addr:"+Integer.toHexString((int) pi.addr)+",code:"+pi.getIns());
                Keystone ks = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
                KeystoneEncoded assemble = ks.assemble(pi.getIns());
                for(int i=0;i<assemble.getMachineCode().length;i++)
                {
                    data[(int) pi.addr+i] = assemble.getMachineCode()[i];
                }
            }
            File fo = new File(outName);
            FileOutputStream fos = new FileOutputStream(fo);
            fos.write(data);
            fos.flush();
            fos.close();
            System.out.println("finish");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    //用于记录条件块中,条件成立,条件不成立时对应的索引值,同时记录条件。
    class selectBr
    {
        long insaddr;
        long trueindex;
        long falseindex;
        String cond;
        public String getCond() {
            return cond;
        }
        public void setCond(String cond) {
            this.cond = cond;
        }
        public long getInsaddr() {
            return insaddr;
        }
        public void setInsaddr(long insaddr) {
            this.insaddr = insaddr;
        }
        public long getTrueindex() {
            return trueindex;
        }
        public void setTrueindex(long trueindex) {
            this.trueindex = trueindex;
        }
        public long getFalseindex() {
            return falseindex;
        }
        public void setFalseindex(long falseindex) {
            this.falseindex = falseindex;
        }
    }
    //真实块,索引值和开始地址
    class TrueBlock{

        long index;
        long startAddr;

        public TrueBlock(){}
        public TrueBlock(long l,long s)
        {
            index = l;
            startAddr = s;
        }

        public long getIndex() {
            return index;
        }

        public void setIndex(long index) {
            this.index = index;
        }

        public long getStartAddr() {
            return startAddr;
        }

        public void setStartAddr(long startAddr) {
            this.startAddr = startAddr;
        }
    }

    public void processFlt()
    {
        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());
                InsAndCtx iac = new InsAndCtx();
                iac.setIns(disasm[0]);
                iac.setRegs(saveRegs(backend));
                iac.setAddr(address);
                instructions.add(iac);
                do_processflt();
            }

            @Override
            public void onAttach(UnHook unHook) {
                System.out.println("attach in processFlt");
            }

            @Override
            public void detach() {
                System.out.println("detach in processFlt");
            }
        },module.base+start, module.base+end, null);
    }
    public void do_processflt()
    {
        if(instructions.empty())
        {
            return;
        }
        Instruction ins = instructions.peek().getIns();
        if(instructions.peek().getAddr() - module.base == dispatcher)
        {
            indexOrder.add(getRegValue("x8",instructions.peek().getRegs()).longValue());
        }
        if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("b.eq")) {
            InsAndCtx beq = instructions.peek();
            //等于跳转,检查是否为cmp x8,
            while (true)
            {
                if(instructions.empty())
                {
                    break;
                }
                instructions.pop();
                ins = instructions.peek().getIns();
                if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("cmp"))
                {
                    String[] sp = ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
                    if(sp[0].equals("w8"))
                    {
                        //找到一个真实块
                        TrueBlock tb = new TrueBlock();
                        long regValue = getRegValue(sp[1].trim(), instructions.peek().getRegs()).longValue();
                        long targetAddr = 0;
                        String offset = beq.getIns().getOpStr().toLowerCase(Locale.ROOT);
                        long offsetvalue = getLongFromOpConst(offset);
                        targetAddr = beq.getAddr() + offsetvalue - module.base;
                        tb.setIndex(regValue);
                        tb.setStartAddr(targetAddr);
                        tbs.add(tb);
                        break;
                    }
                }
            }
        }
        //处理分支块
        if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("b"))
        {
            long offset = getLongFromOpConst(ins.getOpStr());
            if(offset != 0)
            {
                long target = offset + instructions.peek().getAddr() - module.base;
                //直接跳向主发生器
                if(target == dispatcher)
                {
                    instructions.pop();
                    ins = instructions.peek().getIns();
                    if(ins.getMnemonic().toLowerCase(Locale.ROOT).equals("csel"))
                    {
                        String[] sp = ins.getOpStr().toLowerCase(Locale.ROOT).split(",");
                        if(sp[0].trim().equals("w8"))
                        {
                            String cond = sp[3].trim();
                            String reg1 = sp[1].trim();
                            String reg2 = sp[2].trim();
                            selectBr sb = new selectBr();
                            sb.setInsaddr(instructions.peek().getAddr() - module.base);
                            sb.setCond(cond);
                            sb.setTrueindex(getRegValue(reg1,instructions.peek().getRegs()).longValue());
                            sb.setFalseindex(getRegValue(reg2,instructions.peek().getRegs()).longValue());
                            sbs.add(sb);
                        }
                    }
                }
            }
        }
    }

    public long strToLong(String hexString)
    {
        BigInteger bi = new BigInteger(hexString,16);
        return bi.longValue();
    }

    public long getLongFromOpConst(String op)
    {
        if(op.startsWith("#0x"))
        {
            return strToLong(op.substring(3));
        }
        else if(op.startsWith("#"))
        {
            return strToLong(op.substring(1));
        }
        else
        {
            return 0;
        }
    }

    private void reorderblock()    {
//        tbs.add(new TrueBlock(0x6e142ec8L,0x5E6B0));
//        tbs.add(new TrueBlock(0xf07b1447L,0x5E608));
//        tbs.add(new TrueBlock(0x5d7b4e5aL,0x5E5E8));
//        tbs.add(new TrueBlock(0x5ad22f2fL,0x5E628));

        for(TrueBlock tb:tbs)
        {
            System.out.printf("true block index %x,addr %x\n",tb.getIndex(),tb.getStartAddr());
        }
        for(selectBr sb:sbs)
        {
            System.out.printf("select block inds addr: %x,cond: %s . true for %x,false for %x\n",sb.getInsaddr(),sb.getCond(),sb.getTrueindex(),sb.getFalseindex());
        }
        for(long l:indexOrder)
        {
            System.out.printf("index order:%x\n",l);
        }

        for(selectBr sb:sbs)
        {
            String ins1 = "b" + sb.getCond() + " 0x"+Integer.toHexString((int) (getIndexAddr(sb.getTrueindex()) -  sb.getInsaddr()));
            String ins2 = "b 0x"+ Integer.toHexString((int) (getIndexAddr(sb.getFalseindex())-sb.getInsaddr()-4));
            PatchIns pi1 = new PatchIns();
            pi1.setIns(ins1);
            pi1.setAddr(sb.getInsaddr());
            PatchIns pi2 = new PatchIns();
            pi2.setIns(ins2);
            pi2.setAddr(sb.getInsaddr() + 4);
            patchs.add(pi1);
            patchs.add(pi2);
        }

//        PatchIns pi = new PatchIns();
//        pi.setAddr(dispatcher);
//        pi.setIns("b 0x"+Integer.toHexString((int) (getIndexAddr(0x22f0693f)-dispatcher)));
//        patchs.add(pi);
//        PatchIns pie = new PatchIns();
//        pie.setAddr(toend);
//        pie.setIns("b 0x"+Integer.toHexString((int) (getIndexAddr(0x83a9af56L)- toend)));
//        patchs.add(pie);
//        PatchIns pie1 = new PatchIns();
//        pie1.setAddr(0x5E674L);
//        pie1.setIns("b 0x"+Integer.toHexString((int) (getIndexAddr(0x83a9af56L) - 0x5E674L)));
//        patchs.add(pie1);
    }

    private long getIndexAddr(long index)
    {
        for(TrueBlock tb:tbs)
        {
            if(tb.getIndex() == index)
            {
                return tb.getStartAddr();
            }
        }
        System.out.printf("not found addr for index:%x,result may be wrong!\n",index);
        return -1;
    }

    //保存指令和寄存器环境类:
    class InsAndCtx
    {
        long addr;
        Instruction ins;
        List<Number> regs;

        public long getAddr() {
            return addr;
        }

        public void setAddr(long addr) {
            this.addr = addr;
        }

        public void setIns(Instruction ins) {
            this.ins = ins;
        }

        public Instruction getIns() {
            return ins;
        }

        public void setRegs(List<Number> regs) {
            this.regs = regs;
        }

        public List<Number> getRegs() {
            return regs;
        }
    }

    //patch类
    class PatchIns{
        long addr;//patch 地址
        String ins;//patch的指令

        public long getAddr() {
            return addr;
        }

        public void setAddr(long addr) {
            this.addr = addr;
        }

        public String getIns() {
            return ins;
        }

        public void setIns(String ins) {
            this.ins = ins;
        }
    }

    //保存指令寄存器环境
    public List<Number> saveRegs(Backend bk)
    {
        List<Number> nb = new ArrayList<>();
        for(int i=0;i<29;i++)
        {
            nb.add(bk.reg_read(i+Arm64Const.UC_ARM64_REG_X0));
        }
        nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_FP));
        nb.add(bk.reg_read(Arm64Const.UC_ARM64_REG_LR));
        return nb;
    }

    public Number getRegValue(String reg,List<Number> regsaved)
    {
        if(reg.equals("xzr"))
        {
            return 0;
        }
        return regsaved.get(Integer.parseInt(reg.substring(1)));
    }
    public long readInt64(Backend bk,long addr)
    {
        byte[] bytes = bk.mem_read(addr, 8);
        long res = 0;
        for (int i=0;i<bytes.length;i++)
        {
            res =((bytes[i]&0xffL) << (8*i)) + res;
        }
        return res;
    }

}

这样执行一段之后会报错。

true block index 22f0693f,addr 62134
true block index 6e221a17,addr 622f0
true block index 44b82855,addr 62098
true block index 3b21f150,addr 62268
true block index 30fae711,addr 621f0
true block index 83a9af56,addr 62308
select block inds addr: 6213c,cond: eq . true for 6e221a17,false for 6e142ec8
select block inds addr: 62300,cond: eq . true for f07b1447,false for 44b82855
select block inds addr: 620fc,cond: eq . true for 3b21f150,false for 5d7b4e5a
select block inds addr: 622cc,cond: eq . true for 30fae711,false for 5ad22f2f
index order:22f0693f
index order:6e221a17
index order:44b82855
index order:3b21f150
index order:30fae711
index order:83a9af56
not found addr for index:6e142ec8,result may be wrong!
not found addr for index:f07b1447,result may be wrong!
not found addr for index:5d7b4e5a,result may be wrong!
not found addr for index:5ad22f2f,result may be wrong!
Begin patch...
procrss addr:6213c,code:beq 0x1b4
procrss addr:62140,code:b 0xfff9debf
keystone.exceptions.AssembleFailedKeystoneException: Error while assembling `b 0xfff9debf` : OK (KS_ERR_OK)
	at keystone.Keystone.assemble(Keystone.java:101)
	at keystone.Keystone.assemble(Keystone.java:80)
	at AntiOllvm5.patch(AntiOllvm5.java:119)
	at AntiOllvm5.main(AntiOllvm5.java:99)

发现有一些索引值没有找到对应的块:

查看他们的索引,发现刚好是条件块中的索引。这个也好理解,因为在模拟执行的时候我们只走了条件的一个分支,没有走另一个分支,所以就没有记录下对应的真实块。这里我们手动根据b.eq和寄存器的值,添加对应的真实块:

最后这块实在不知道toend和0x5E674L是怎么算的,实在无语,写博客也不详细一点,没有源文件,没有说明。。我到这里都没法分析了。。。

 PatchIns pi = new PatchIns();
        pi.setAddr(dispatcher);
        pi.setIns("b 0x" + Integer.toHexString((int) (getIndexAddr(0x22f0693f) - dispatcher)));//获取索引值顺序中的第一个值,找到对应的真实块。将主分发器处patch为跳转向第一个真实块的跳转指令。
        patchs.add(pi);
        PatchIns pie = new PatchIns();
        pie.setAddr(toend);
        pie.setIns("b 0x" + Integer.toHexString((int) (getIndexAddr(0x83a9af56L) - toend)));
        patchs.add(pie);
        PatchIns pie1 = new PatchIns();
        pie1.setAddr(0x5E674L);
        pie1.setIns("b 0x"+Integer.toHexString((int) (getIndexAddr(0x83a9af56L) - 0x5E674L)));
        patchs.add(pie1);

先留着 之后弄明白了再来写。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值