Beispiel #1
0
    def load_test_case(self, test_case: TestCase) -> None:
        self.test_case = test_case

        # create and read a binary
        with open(test_case.bin_path, 'rb') as f:
            code = f.read()
        self.code_end = self.code_start + len(code)

        # initialize emulator in x86-64 mode
        emulator = Uc(uni.UC_ARCH_X86, uni.UC_MODE_64)

        try:
            # allocate memory
            emulator.mem_map(self.code_start, self.CODE_SIZE)
            emulator.mem_map(self.sandbox_base - self.WORKING_MEMORY_SIZE // 2,
                             self.WORKING_MEMORY_SIZE)

            # write machine code to be emulated to memory
            emulator.mem_write(self.code_start, code)

            # set up callbacks
            emulator.hook_add(uni.UC_HOOK_MEM_READ | uni.UC_HOOK_MEM_WRITE, self.trace_mem_access,
                              self)
            emulator.hook_add(uni.UC_HOOK_CODE, self.instruction_hook, self)

            self.emulator = emulator

        except UcError as e:
            LOGGER.error("[X86UnicornModel:load_test_case] %s" % e)
Beispiel #2
0
def runX86(code):
    output = []

    def hook_interrupt(uc, intno, user_data):
        from unicorn.x86_const import UC_X86_REG_EAX, UC_X86_REG_EBX
        from unicorn.x86_const import UC_X86_REG_ECX, UC_X86_REG_EDX
        if intno != 0x80:
            uc.emu_stop()
            raise Error("Unknwon Interrupt")
        eax = uc.reg_read(UC_X86_REG_EAX)
        if eax == 4:  # SYS_WRITE
            ebx = uc.reg_read(UC_X86_REG_EBX)
            ecx = uc.reg_read(UC_X86_REG_ECX)
            edx = uc.reg_read(UC_X86_REG_EDX)
            try:
                buf = uc.mem_read(ecx, edx)
                if ebx == 1:
                    user_data.extend(map(chr, buf))
                    uc.reg_write(UC_X86_REG_EAX, edx)
            except UcError:
                uc.emu_stop()
                raise Error("Segmentation fault")
        elif eax == 1:
            uc.emu_stop()
        else:
            raise Error("Unknown system call")

    mu = Uc(UC_ARCH_X86, UC_MODE_32)
    mu.mem_map(ADDRESS, 0x1000)
    mu.mem_write(ADDRESS, code)
    mu.hook_add(UC_HOOK_INTR, hook_interrupt, output)
    mu.emu_start(ADDRESS, ADDRESS + len(code))
    return ''.join(output)
Beispiel #3
0
def runmips(code):
    output = []

    def hook_interrupt(uc, intno, user_data):
        from unicorn.mips_const import UC_MIPS_REG_2, UC_MIPS_REG_4
        from unicorn.mips_const import UC_MIPS_REG_5, UC_MIPS_REG_6
        if intno != 17:
            uc.emu_stop()
            raise Exception("Unknwon Interrupt")
        v0 = uc.reg_read(UC_MIPS_REG_2)
        if v0 == 4004:  # SYS_WRITE
            a0 = uc.reg_read(UC_MIPS_REG_4)
            a1 = uc.reg_read(UC_MIPS_REG_5)
            a2 = uc.reg_read(UC_MIPS_REG_6)
            try:
                buf = uc.mem_read(a1, a2)
                if a0 == 1:
                    user_data.extend(map(chr, buf))
                    uc.reg_write(UC_MIPS_REG_2, a2)
            except UcError:
                uc.emu_stop()
                raise Error("Segmentation fault")
        elif v0 == 4001:
            uc.emu_stop()
        else:
            uc.emu_stop()
            raise Error("Unknown system call")

    mu = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
    mu.mem_map(ADDRESS, 0x1000)
    mu.mem_write(ADDRESS, code)
    mu.hook_add(UC_HOOK_INTR, hook_interrupt, output)
    mu.emu_start(ADDRESS, ADDRESS + len(code))
    return ''.join(output)
Beispiel #4
0
def run():
    print("Start emulate ARM...")
    try:
        # 创建虚拟机
        mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)

        # 分配内存
        ADDRESS = 0x10000
        mu.mem_map(ADDRESS, 0x1000)
        mu.mem_write(ADDRESS, ARM_CODE)

        # 写寄存器
        mu.reg_write(UC_ARM_REG_R0, 0x1234)
        mu.reg_write(UC_ARM_REG_R2, 0x6789)
        mu.reg_write(UC_ARM_REG_R3, 0x3333)

        # Hook 代码
        mu.hook_add(UC_HOOK_CODE, hook_code, None, ADDRESS, ADDRESS + 0x1000)

        # 启动虚拟机
        mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))

        # 获取结果
        r0 = mu.reg_read(UC_ARM_REG_R0)
        r1 = mu.reg_read(UC_ARM_REG_R1)
        print(f">>> R0 = 0x{r0:x}")
        print(f">>> R1 = 0x{r1:x}")

    except UcError as e:
        print(f"Emulate error: {e}")
Beispiel #5
0
    def test_multiprocess(self):
        record = []

        def hook(uc, address, size, userdata):
            record.append(address)

        uc1 = Uc(UC_ARCH_X86, UC_MODE_32)
        uc1.hook_add(UC_HOOK_CODE, hook)
        uc1.mem_map(0, 0x2000)

        uc2 = Uc(UC_ARCH_X86, UC_MODE_32)
        uc2.hook_add(UC_HOOK_CODE, hook)
        uc2.mem_map(0, 0x2000)

        uc1.emu_start(0x1000, 0x1006)
        self.assertListEqual(record, [0x1000, 0x1002, 0x1004])

        uc2.emu_start(0x1000, 0x1006)
        self.assertListEqual(record,
                             [0x1000, 0x1002, 0x1004, 0x1000, 0x1002, 0x1004])

        uc1.reg_write(UC_X86_REG_EAX, 5)
        context1 = uc1.context_save()

        uc2.reg_write(UC_X86_REG_EAX, 6)
        context2 = uc2.context_save()

        self.assertEqual(uc1.reg_read(UC_X86_REG_EAX), 5)
        self.assertEqual(uc2.reg_read(UC_X86_REG_EAX), 6)

        uc2.context_restore(context1)
        uc1.context_restore(context2)
        self.assertEqual(uc1.reg_read(UC_X86_REG_EAX), 6)
        self.assertEqual(uc2.reg_read(UC_X86_REG_EAX), 5)
Beispiel #6
0
def runX86_64(code):
    from unicorn.x86_const import UC_X86_INS_SYSCALL
    output = []

    def hook_syscall(uc, user_data):
        from unicorn.x86_const import UC_X86_REG_RAX, UC_X86_REG_RDI
        from unicorn.x86_const import UC_X86_REG_RSI, UC_X86_REG_RDX
        rax = uc.reg_read(UC_X86_REG_RAX)
        if rax == 1:
            rdi = uc.reg_read(UC_X86_REG_RDI)
            rsi = uc.reg_read(UC_X86_REG_RSI)
            rdx = uc.reg_read(UC_X86_REG_RDX)
            try:
                buf = uc.mem_read(rsi, rdx)
                if rdi == 1:
                    user_data.extend(map(chr, buf))
                    uc.reg_write(UC_X86_REG_RAX, rdx)
            except UcError:
                uc.emu_stop()
                raise Error("Segmentation fault")
        elif rax == 60:
            uc.emu_stop()
        else:
            raise Error("Unknown system call")

    mu = Uc(UC_ARCH_X86, UC_MODE_64)
    mu.mem_map(ADDRESS, 0x1000)
    mu.mem_write(ADDRESS, code)
    mu.hook_add(UC_HOOK_INSN, hook_syscall, output, 1, 0, UC_X86_INS_SYSCALL)
    mu.emu_start(ADDRESS, ADDRESS + len(code))
    return ''.join(output)
Beispiel #7
0
def runarm64(code):
    output = []

    def hook_interrupt(uc, intno, user_data):
        from unicorn.arm64_const import UC_ARM64_REG_X0, UC_ARM64_REG_X1
        from unicorn.arm64_const import UC_ARM64_REG_X2, UC_ARM64_REG_X8
        if intno != 2:
            uc.emu_stop()
            raise Exception("Unknwon Interrupt")
        x8 = uc.reg_read(UC_ARM64_REG_X8)
        if x8 == 64:  # SYS_WRITE
            x0 = uc.reg_read(UC_ARM64_REG_X0)
            x1 = uc.reg_read(UC_ARM64_REG_X1)
            x2 = uc.reg_read(UC_ARM64_REG_X2)
            try:
                buf = uc.mem_read(x1, x2)
                if x0 == 1:
                    user_data.extend(map(chr, buf))
                    uc.reg_write(UC_ARM64_REG_X0, x2)
            except UcError:
                uc.emu_stop()
                raise Error("Segmentation fault")
        elif x8 == 93:
            uc.emu_stop()
        else:
            uc.emu_stop()
            raise Error("Unknown system call")

    mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
    mu.mem_map(ADDRESS, 0x1000)
    mu.mem_write(ADDRESS, code)
    mu.hook_add(UC_HOOK_INTR, hook_interrupt, output)
    mu.emu_start(ADDRESS, ADDRESS + len(code))
    return ''.join(output)
Beispiel #8
0
    def test_hooks(self):
        record = []

        def hook(uc, address, size, userdata):
            record.append(address)

        uc = Uc(UC_ARCH_X86, UC_MODE_32)
        uc.mem_map(0, 0x2000)
        uc.hook_add(UC_HOOK_CODE, hook)
        uc.emu_start(0x1000, 0x1006)
        self.assertListEqual(record, [0x1000, 0x1002, 0x1004])
Beispiel #9
0
def emulate(data, mode):
    try:
        stack = 0x90000
        uc = Uc(UC_ARCH_X86, mode)
        uc.mem_map(stack, 0x1000 * 10)
        uc.mem_map(shellcode_code_base, 0x100000)
        uc.mem_write(shellcode_code_base, data)
        uc.reg_write(UC_X86_REG_ESP, stack + 0x1000)
        uc.hook_add(UC_HOOK_CODE, hook_instr, user_data=mode)
        uc.emu_start(shellcode_code_base, shellcode_code_base + len(data), 0,
                     shellcode_limit)
    except unicorn.UcError:
        pass
Beispiel #10
0
    def test_setip_after_emustop(self):
        record = []
        uc = Uc(UC_ARCH_X86, UC_MODE_32)

        def hook(uc, address, size, userdata):
            record.append(address)

        def hook_stop(uc, address, size, userdata):
            if address == 0x1002:
                uc.emu_stop()
                uc.reg_write(UC_X86_REG_EIP, 0x1006)

        uc.mem_map(0, 0x2000)
        uc.hook_add(UC_HOOK_CODE, hook)
        uc.hook_add(UC_HOOK_CODE, hook_stop)
        uc.emu_start(0x1000, 0x1008)
        self.assertListEqual(record, [0x1000, 0x1002, 0x1006])
    def test_exception_in_hook(self):
        uc = Uc(UC_ARCH_X86, UC_MODE_64)
        uc.mem_map(CODE_ADDR, 0x1000)
        uc.mem_write(CODE_ADDR, CODE)

        counter = HookCounter()
        uc.hook_add(UC_HOOK_CODE,
                    counter.good_code_hook,
                    begin=CODE_ADDR,
                    end=CODE_ADDR + len(CODE))
        uc.hook_add(UC_HOOK_CODE,
                    counter.bad_code_hook,
                    begin=CODE_ADDR,
                    end=CODE_ADDR + len(CODE))

        self.assertRaises(ValueError, uc.emu_start, CODE_ADDR,
                          CODE_ADDR + len(CODE))
        # Make sure hooks calls finish before raising (hook_calls == 2)
        self.assertEqual(counter.hook_calls, 2)
Beispiel #12
0
    def test_x86_64_syscall(self):
        print("Emulate x86_64 code with 'syscall' instruction")
        ADDRESS = 0x1000000
        X86_CODE64_SYSCALL = b"\x0f\x05"  # SYSCALL
        # Initialize emulator in X86-64bit mode
        mu = Uc(UC_ARCH_X86, UC_MODE_64)

        # map 2MB memory for this emulation
        mu.mem_map(ADDRESS, 2 * 1024 * 1024)

        # write machine code to be emulated to memory
        mu.mem_write(ADDRESS, X86_CODE64_SYSCALL)

        def hook_syscall(mu, user_data):
            rax = mu.reg_read(UC_X86_REG_RAX)
            if rax == 0x100:
                mu.reg_write(UC_X86_REG_RAX, 0x200)
            else:
                print("ERROR: was not expecting rax=%d in syscall" % rax)

        # hook interrupts for syscall
        mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL)

        # syscall handler is expecting rax=0x100
        mu.reg_write(UC_X86_REG_RAX, 0x100)

        try:
            # emulate machine code in infinite time
            mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64_SYSCALL))
        except UcError as e:
            print("ERROR: %s" % e)

        # now print out some registers
        print(">>> Emulation done. Below is the CPU context")

        rax = mu.reg_read(UC_X86_REG_RAX)
        print(">>> RAX = 0x%x" % rax)
Beispiel #13
0
def setup_hooks(mu: Uc):
    # tracing all basic blocks
    # mu.hook_add(UC_HOOK_BLOCK, hook_block)

    # hook select instructions
    mu.hook_add(UC_HOOK_CODE, hook_instr)

    # trace all memory reads
    # mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read)
    # mu.hook_add(UC_HOOK_MEM_READ_AFTER, hook_mem_read_after)

    # mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)

    # hook unmapped memory operations
    mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped)
    mu.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_write_unmapped)
    mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, hook_mem_fetch_unmapped)
Beispiel #14
0
stack_size = 0x10000 * 3
stack_top = stack_base + stack_size - 0x4
mu.mem_map(stack_base, stack_size)
mu.reg_write(UC_ARM_REG_SP, stack_top)

# 分配数据内存
data_base = 0xF0000
data_size = 0x10000 * 3
mu.mem_map(data_base, data_size)
mu.mem_write(data_base, a1)
mu.reg_write(UC_ARM_REG_R0, data_base)

# 修复 Got 表
mu.mem_write(image_base + 0x1EDB0, b"\xD9\x98\x00\x00")

# 设置 Hook
mu.hook_add(UC_HOOK_CODE, hook_code, None)
mu.hook_add(UC_HOOK_MEM_UNMAPPED, hook_memory, None)

# 设置需要 Run 的函数地址
func_start = image_base + 0x9B68 + 0x1
func_end = image_base + 0x9C2C

try:
    mu.emu_start(func_start, func_end)
    r2 = mu.reg_read(UC_ARM_REG_R2)
    result = mu.mem_read(r2, 16)
    print(result.hex())
except UcError as e:
    print(f"UC run error {e}")
            # originalBB
            if access == UC_MEM_WRITE and size == 4 and value == 1:
                print("WRITE: address:0x{0:016x} is decryptStatus".format(
                    address))
                if address in decryptStatus:
                    decryptStatus.remove(address)
                uc.emu_stop()


def hook_rwdata_backup(uc: Uc, access, address, size, value, data):
    if data_section_start <= address < data_section_end and access == UC_MEM_WRITE:
        data_backup.append((address, uc.mem_read(address, size)))


# 添加内存读写hook
emu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_bss_access)
# 添加内存恢复hook
emu.hook_add(UC_HOOK_MEM_WRITE, hook_rwdata_backup)

cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
ks_thumb = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
ks_arm = Ks(KS_ARCH_ARM, KS_MODE_ARM)

# 模拟执行各个函数
j_result = json.load(open('./result.json'))
for entryBB, decryptBB, originalBB, in j_result:
    emu.reg_write(UC_ARM_REG_SP, STACK_ADDR + STACK_SIZE)
    print("Emulating entryBB")
    stage = 0
    decryptStatus = set()
    data_backup = []
Beispiel #16
0
class emu:
    """
    Loads ELF file to unicorn, sets watchpoints and stdin
    """
    def __init__(self, fname, stdin, watchpoints=[], drcov=True, emulator_base=None, fw_entry_symbol="cont"):
        self.stdin = stdin
        self.exception = ""
        self.uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
        self.fname = fname
        self.fd = open(fname, "rb")
        self.elf = elffile.ELFFile(self.fd) 

        self.symbols = {}
        self.symbols_reverse = {}
        for i in range(self.elf.num_sections()):
            sec = self.elf.get_section(i)
            if sec.name == ".symtab":
                for sym in sec.iter_symbols():
                    self.symbols[sym.name] = sym.entry["st_value"] 
                    self.symbols_reverse[sym.entry["st_value"]] = sym.name

        self.results = []
        self.result_id = 0
        self.coverage_pc = set()
        self.coverage_bb = set()
        self.read = set()
        self.write = set()

        self.trace_initialized = False
        self.coverage_activity = {}
        self.read_activity = {}
        self.write_activity = {}

        self.stdout = ""
        self.stderr = ""

        self.emulator_base_start = None
        self.emulator_base_stop = None
        if fw_entry_symbol in self.symbols:
            self.fw_entry = self.symbols[fw_entry_symbol] # ignore everything until that symbol
        else:
            self.fw_entry = None

        #loading prog headrs
        self.state = []
        self.segments = []
        for i in range(self.elf.num_sections()):
            section = self.elf.get_section(i)
            if section.header["sh_flags"] & SH_FLAGS.SHF_ALLOC != 0:
                addr = section.header["sh_addr"]
                size = section.header["sh_size"]
                name = section.name

                #NOBITS sections contains no data in file
                #Will be initialized with zero
                if section.header["sh_type"] == "SHT_NOBITS":
                    data = b"\x00" * size
                else:
                    data = section.data()

                print("Found %s @ 0x%x - 0x%x (%d bytes)" % (name, addr, addr+len(data), len(data)))
                if emulator_base == addr:
                    self.emulator_base_start = emulator_base
                    self.emulator_base_stop = emulator_base + size

                self.segments += [(name, addr, size)]
                self.state += [(addr, size, data)]



        #compute memory map from sections
        self.maps = []
        if self.emulator_base_start is not None:
            self.maps += [(self.emulator_base_start, self.emulator_base_stop)]
        self.segments = sorted(self.segments, key=lambda x:x[0])
        for name, addr, size in self.segments:
            size += addr & 0x3ff
            addr = addr & (~0x3ff)
            altered = False
            for i in range(len(self.maps)):
                map_addr, map_size = self.maps[i]
                offset = addr - map_addr
                if addr >= map_addr and addr <= map_addr + map_size:
                    self.maps[i] = (map_addr, self.pageresize(max(map_size, offset+size)))
                    altered = True

            if not altered:
                self.maps += [(addr, self.pageresize(size))]


        for addr, size in self.maps:
            print("Mapping 0x%x - 0x%x (%d bytes)" % (addr, addr+size, size))
            self.uc.mem_map(addr, size, UC_PROT_ALL)


            
        for addr,size,data in self.state:
            print("Loading 0x%x - 0x%x (%d bytes)" % (addr, addr+len(data), len(data)))
            self.uc.mem_write(addr, data)

        #stack
        stack = 0xdead0000
        stack_size = 16384
        print("Mapping Stack 0x%x - 0x%x (%d bytes)" % (stack, stack+stack_size, stack_size))
        self.uc.mem_map(stack, stack_size, UC_PROT_ALL)
        self.uc.reg_write(arm_const.UC_ARM_REG_SP, stack + stack_size)

        #syscalls
        self.uc.hook_add(UC_HOOK_INTR, self.hook_intr, self)

        #tracing
        self.watchpoints = watchpoints
        self.uc.hook_add(UC_HOOK_CODE, self.hook_code, self)
        self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access, self)

        #prepare drcov file
        self.drcov = drcov
        if drcov:
            self.uc.hook_add(UC_HOOK_BLOCK, self.hook_bb, self)

    def pageresize(self, s, pagesize=1024):
        if s % pagesize == 0:
            return s
        return (int(s / pagesize) + 1) * pagesize

    """
    We need to emulate read and write for emulation
    """
    @staticmethod
    def hook_intr(uc, size, self):
        #print hex(uc.reg_read(arm_const.UC_ARM_REG_PC))
        pc = uc.reg_read(arm_const.UC_ARM_REG_PC)
        for name in ["read","write"]:
            if self.symbols[name] <= pc and  self.symbols[name] + 8 >= pc:
                #print name
                if name == "read":
                    fd = uc.reg_read(arm_const.UC_ARM_REG_R0)
                    target = uc.reg_read(arm_const.UC_ARM_REG_R1)
                    size = uc.reg_read(arm_const.UC_ARM_REG_R2)

                    data = self.stdin[:size]
                    self.stdin = self.stdin[size:]

                    uc.mem_write(target, data)
                    self.uc.reg_write(arm_const.UC_ARM_REG_R0, len(data))

                elif name == "write":
                    fd = uc.reg_read(arm_const.UC_ARM_REG_R0)
                    target = uc.reg_read(arm_const.UC_ARM_REG_R1)
                    size = uc.reg_read(arm_const.UC_ARM_REG_R2)

                    data = uc.mem_read(target, size)
                    if fd == 1:
                        self.stdout += data.decode("utf-8")
                        sys.stdout.write(data.decode("utf-8"))
                    else:
                        self.stderr += data.decode("utf-8")
                        sys.stderr.write(data.decode("utf-8"))

                else:
                    print("unknown intr")

    """
    Implement memory and code watchpoints
    """
    @staticmethod
    def hook_bb(uc, address, size, self):
        if self.emulator_base_start is not None:
            if address >= self.emulator_base_start and address < self.emulator_base_stop:
                return
        #print(hex(address))
        self.coverage_bb.add((address, size))

    @staticmethod
    def hook_code(uc, address, size, self):
        # Unicorn will for some reason giv old register values after a crash
        # The last update seems to be on the entry of the bb
        self.regs = {}
        self.regs["r0"] = self.uc.reg_read(arm_const.UC_ARM_REG_R0)
        self.regs["r1"] = self.uc.reg_read(arm_const.UC_ARM_REG_R1)
        self.regs["r2"] = self.uc.reg_read(arm_const.UC_ARM_REG_R2)
        self.regs["r3"] = self.uc.reg_read(arm_const.UC_ARM_REG_R3)
        self.regs["r4"] = self.uc.reg_read(arm_const.UC_ARM_REG_R4)
        self.regs["r5"] = self.uc.reg_read(arm_const.UC_ARM_REG_R5)
        self.regs["r6"] = self.uc.reg_read(arm_const.UC_ARM_REG_R6)
        self.regs["r7"] = self.uc.reg_read(arm_const.UC_ARM_REG_R7)
        self.regs["r8"] = self.uc.reg_read(arm_const.UC_ARM_REG_R8)
        self.regs["r9"] = self.uc.reg_read(arm_const.UC_ARM_REG_R9)
        self.regs["r10"] = self.uc.reg_read(arm_const.UC_ARM_REG_R10)
        self.regs["r11"] = self.uc.reg_read(arm_const.UC_ARM_REG_R11)
        self.regs["r12"] = self.uc.reg_read(arm_const.UC_ARM_REG_R12)
        self.regs["sp"] = self.uc.reg_read(arm_const.UC_ARM_REG_R13)
        self.regs["lr"] = self.uc.reg_read(arm_const.UC_ARM_REG_R14)
        self.regs["pc"] = self.uc.reg_read(arm_const.UC_ARM_REG_R15)

        if self.fw_entry is not None and address & 0xfffffffe == self.fw_entry & 0xfffffffe:
            self.trace_init_state()
        if self.fw_entry is None and not self.trace_initialized:
            self.trace_init_state()

        if self.emulator_base_start is not None:
            if address >= self.emulator_base_start and address < self.emulator_base_stop:
                return

        self.coverage_pc.add(address)
        if address in self.coverage_activity:
            self.coverage_activity[address] += 1
        else:
            self.coverage_activity[address] = 1

        if address in self.watchpoints or address^1 in self.watchpoints:
            self.trace_state_change("Execute")

    @staticmethod
    def hook_mem_access(uc, access, address, size, value, self):
        pc = self.uc.reg_read(arm_const.UC_ARM_REG_R15)
        if self.emulator_base_start is not None:
            if pc >= self.emulator_base_start and pc < self.emulator_base_stop:
                return
        if access == UC_MEM_WRITE:
            self.write.add((pc, address, value))
            if address in self.write_activity:
                self.write_activity[address] += 1
            else:
                self.write_activity[address] = 1
        else:
            self.read.add((pc, address))
            if address in self.read_activity:
                self.read_activity[address] += 1
            else:
                self.read_activity[address] = 1
        if address in self.watchpoints:
            if access == UC_MEM_WRITE:
                self.trace_state_change("Write 0x%x" % address)
            else:
                self.trace_state_change("Read 0x%x" % address)


    """
    For each tracepoint that was hit
        Dump Registers
        Do Memory Dump
    """
    def trace_init_state(self):
        self.state = []
        self.trace_initialized = True
        for name, addr, size in self.segments:
            data = self.uc.mem_read(addr, size)
            #data = list(map(chr, data))
            self.state += [(addr, size, data)]
        

    """
    Called if a tracepoint is hit
    Will save registers and analyzes changes made im memory
    """
    def trace_state_change(self, reason):
        print(reason)
        new_state = []
        memdiff = []
        for addr, size, data in self.state:
            new_data = self.uc.mem_read(addr, size)
            #new_data = list(map(chr, new_data))
            if data != new_data:
                new = old = ""
                for i in range(len(data)):
                    if data[i] != new_data[i]:
                        old += "%02x" % data[i]
                        new += "%02x" % new_data[i]
                    elif new != "":
                        memdiff += [(i+addr-len(new), old, new)]
                        new = old = ""

            new_state += [(addr, size, new_data)]

        #XXX
        memdif_rendered = self.render_mem_diff()
        sys.stderr.write(self.stderr)
        sys.stderr.write("\n"+memdif_rendered+"\n")

        self.state = new_state

        # disassemble current instruction
        try:
            pc = self.regs["pc"]
            md = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)
            instr = list(md.disasm(self.uc.mem_read(pc, 4), pc))[0]
            instr = instr.mnemonic + "   " + instr.op_str 
        except:
            import traceback; traceback.print_exc()
            instr = hexlify(self.uc.mem_read(pc, 4))

        # Save tracepoint object
        tp = {}
        tp["reason"] = reason
        tp["regs"] = self.regs
        tp["instr"] = instr
        tp["memdiff"] = memdiff
        tp["memdif_rendered"] = memdif_rendered
        tp["stdout"] = self.stdout
        tp["stderr"] = self.stderr
        tp["resid"] = self.result_id
        self.results += [tp]
        self.stdout = ""
        self.stderr = ""
        self.result_id += 1

        return [{"regs": self.regs, "memdiff": sorted(memdiff)}]

    def render_mem_diff(self, block_size=32):
        ret  = "----------" + ("-"*(3*block_size+1)) + "\n"
        ret += "         |\n"
        print_dots = False
        for addr, size, data in self.state:
            new_data = self.uc.mem_read(addr, size)
            #new_data = list(map(chr, new_data))
            current_offset = 0
            #print(len(data), len(new_data), size)

            #for each hexdump row
            while current_offset <  size:
                old_row = data[current_offset: current_offset+block_size]
                new_row = new_data[current_offset: current_offset+block_size]
                #ugly equal comparison
                equal = True
                for x,y in zip(new_row, old_row):
                    equal = equal and (x==y)

                if not equal:
                    hex_new = "%8x |  " % (addr + current_offset)
                    hex_old = "         |  "
                    symbols = ""

                    #render diff
                    for i in range(min(block_size, len(new_row))):
                        if new_row[i] == old_row[i]:
                            hex_new += "%02x " % new_row[i]
                            hex_old += "   "
                        else:
                            hex_new += "\033[;32m%02x\033[;00m " % new_row[i]
                            hex_old += "\033[;31m%02x\033[;00m " % old_row[i]

                        if (addr + current_offset + i) in self.watchpoints:
                            symbols += "         |  "
                            if len("Watchpoint") < 3*i - 1:
                                symbols += " " * (3*i - len("Watchpoint") - 1)
                                symbols += "\033[;33mWatchpoint ^^\033[;00m\n"
                            else:
                                symbols += "   " * i
                                symbols += "\033[;33m^^ Watchpoint\033[;00m\n"
                        elif (addr + current_offset + i) in self.symbols_reverse:
                            name = self.symbols_reverse[addr + current_offset + i]
                            symbols += "         |  "
                            if len(name) < 3*i - 1:
                                symbols += " " * (3*i - len(name) - 1)
                                symbols += "%s ^^\n" % name
                            else:
                                symbols += "   " * i
                                symbols += "^^ %s\n" % name

                    ret += hex_new + "\n" + hex_old + "\n"
                    if len(symbols) > 1:
                        ret += symbols
                    print_dots = True

                else:
                    if print_dots:
                        print_dots = False
                        ret += "         |\n"
                        ret += "         |" + ("-"*(3*block_size+1)) + "\n"
                        ret += "         |\n"
                    


                current_offset += block_size

        #cleanup end
        split = ret.split("\n")
        if len(split) <= 3:
            return ""
        ret = "\n".join(split[:-3]) + "\n"
        ret += "----------" + ("-"*(3*block_size+1)) + "\n"
        return ret


    """
    Run the Emulation
    """
    def run(self, timeout=300):
        try:
            print("running until exit @ 0x%x" % self.symbols["exit"])
            self.uc.emu_start(self.elf.header.e_entry, self.symbols["exit"], timeout=timeout*UC_SECOND_SCALE)
            self.trace_state_change("Exit")
        except KeyboardInterrupt:
            sys.exit(1)
        except Exception as e:
            self.exception = str(e)
            print(e)
            import traceback; traceback.print_exc()
            print(hex(self.uc.reg_read(arm_const.UC_ARM_REG_PC)))
            self.trace_state_change(str(e))


    # Seems to be broken n lighthouse
    def get_drcov(self):
        drcov = b"DRCOV VERSION: 2\nDRCOV FLAVOR: drcov\n"

        drcov += b"Module Table: version 2, count %d\n" % len(self.state)
        drcov += b"Columns: id, base, end, entry, path\n"
        for i in range(len(self.state)):
            addr, size, _ = self.state[i]
            drcov += b"%d, 0x%x, 0x%x, 0x%x, %s\n" % (i, addr, addr+size+1, addr, os.path.basename(self.fname).encode())

        drcov += b"BB Table: %d bbs\n" % len(self.coverage_bb)
        bb_table = b""
        for address, size in self.coverage_bb:
            for module_id in range(len(self.state)):
                base_addr, module_size, _ = self.state[module_id]
                if address >= base_addr and address <= base_addr + module_size:
                    bb_table += struct.pack("<Ihh", address - base_addr, size, module_id)
                    break

        return drcov + bb_table

    def get_tracefile(self):
        trace = ""
        for address in self.coverage_pc:
            trace += "0x%x\n" % address
        return trace.encode()
Beispiel #17
0
INT_0x80 = b'\xCD\x80'
X86_CODE32 = b''
trip0 = b'\0' * 3
from unicorn import Uc, UC_ARCH_X86, UC_MODE_32, UC_HOOK_INTR
from kernels.linux.x86_32.d7_26_2020.L2_6 import linux_kernel_2_6

mu = Uc(UC_ARCH_X86, UC_MODE_32)  # <-------|
#                                           |
Mem_size = 4 * 1024  #<--------------------------------- These are specific to the functionality surrounding assembly parsing and execution.
mu.mem_map(
    0, Mem_size
)  #                        | In this case, the kernel is emulated, the assembly pushs data onto a syscall that calls the kernel to
mu.hook_add(
    UC_HOOK_INTR, linux_kernel_2_6
)  #<---| then call the kernel function with the data and parameters passed to syscall at the assembly level.


def printf(chars_, *va_list):
    global mu, INT_0x80, trip0, linux_kernel_2_6
    entry_point = 0x0a00
    for a in chars_ % va_list:
        l = b'\xBA\1' + trip0 + b'\xB9' + eval(
            f"b'{chr(a)if hex(a)!='0xa'else''}'"
        ) + trip0 + b'\xBB\1' + trip0 + b'\xB8\4' + trip0 + INT_0x80
        mu.mem_write(entry_point, l)
        mu.emu_start(entry_point, entry_point + len(l))
        del l


#del [list(map(lambda a: print(chr(a), end=''), chars_ % va_list)).pop()][0] # <-- This version of 'printf' is for when you don't need a kernel during runtime. Plus, it deletes itself after structuring and executing based on size of scope that needed to be addressed.
Beispiel #18
0
class CPU:
    def __init__(self,
                 firmware: Firmware = None,
                 state: CpuState = None,
                 verbose=0,
                 init=True):
        self.firmware = firmware
        self.uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
        self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
        self.cs.detail = True
        self.state = state
        self.has_error = None
        self.last_addr = None
        self.ready = False
        self.context = None
        self.verbose = verbose

        if init:
            self.init()

    def init(self):
        if self.firmware:
            self.firmware.refresh()
        self.state.verify()
        self.init_memory()
        self.init_hook()
        self.init_firmware()
        self.context = self.uc.context_save()
        self.reset()
        self.ready = True

    def init_firmware(self):
        if not self.firmware:
            raise Exception("firmware missing error")

        addr = MemoryMap.FLASH.address
        self.uc.mem_write(addr, self.firmware.buffer)

    def reset(self):
        addr = MemoryMap.FLASH.address
        self.uc.context_restore(self.context)
        self.uc.reg_write(UC_ARM_REG_PC,
                          from_bytes(self.uc.mem_read(addr + 4, 4)))

    def run(self):
        if not self.ready:
            raise Exception("init() does not called")

        INST_SIZE = 2

        if self.firmware:
            self.last_func = self.firmware.text_map[self.uc.reg_read(
                UC_ARM_REG_PC)]
            if self.verbose >= 2:
                print(self.last_func)

        try:
            while self.step():
                pass
        except UcError as e:
            print("ERROR:", e)
            addr = self.uc.reg_read(UC_ARM_REG_PC)
            self.debug_addr(addr - INST_SIZE * 8 - 2, count=7)
            print(">", end=" ")
            self.debug_addr(addr)
            self.debug_addr(addr + INST_SIZE, count=7)
            for reg in REGS:
                uc_value = self.uc.reg_read(reg)
                print(REGS_NAME[reg].ljust(5), hex32(uc_value), sep='\t')

            raise

    def step(self, count=None):
        addr = self.uc.reg_read(UC_ARM_REG_PC)
        cycle = self.state.cycle
        if count is not None:
            self.state.cycle = count

        try:
            self.uc.emu_start(addr | 1, MemoryMap.FLASH.address_until, 0,
                              self.state.cycle)
        finally:
            if count is not None:
                self.state.cycle = cycle

        if self.has_error:
            raise UcError(0)

        return True

    def init_memory(self):
        for region in MemoryMap:  # type: MemoryRegion
            self.uc.mem_map(region.address, region.size, region.uc_mode)

    def init_hook(self):
        peripheral = MemoryMap.PERIPHERAL

        self.uc.hook_add(
            UC_HOOK_MEM_READ,
            self.hook_peripheral_read,
            None,
            peripheral.address,
            peripheral.address_until,
        )

        self.uc.hook_add(UC_HOOK_MEM_WRITE, self.hook_peripheral_write, None,
                         peripheral.address, peripheral.address_until)

        self.uc.hook_add(
            UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED,
            self.hook_unmapped)

        self.uc.hook_add(
            UC_HOOK_INTR,
            self.hook_intr,
        )

        if self.verbose >= 2:
            self.uc.hook_add(UC_HOOK_CODE, self.hook_inst)

    def hook_intr(self, uc: Uc, intno, user_data):
        # self.debug_addr(uc.reg_read(UC_ARM_REG_PC) - 40, 40)
        if intno == 2:
            swi = from_bytes(uc.mem_read(uc.reg_read(UC_ARM_REG_PC) - 2, 1))
            r0 = uc.reg_read(UC_ARM_REG_R0)
            r1 = uc.reg_read(UC_ARM_REG_R1)
            r2 = uc.reg_read(UC_ARM_REG_R2)
            r3 = uc.reg_read(UC_ARM_REG_R3)

            if swi == 0:
                print("done?")
                print(intno, swi, ":", uc.reg_read(UC_ARM_REG_R0),
                      uc.reg_read(UC_ARM_REG_R1), uc.reg_read(UC_ARM_REG_R2),
                      uc.reg_read(UC_ARM_REG_R3))
                uc.reg_write(UC_ARM_REG_R0, 16)
                uc.reg_write(UC_ARM_REG_R1, 32)
                uc.reg_write(UC_ARM_REG_R2, 48)
                uc.reg_write(UC_ARM_REG_R3, 64)
            elif swi == 1:
                # TODO: address and size vaild required?
                buffer = uc.mem_read(r0, r1).decode('utf-8', 'replace')
                if self.state.write_to_stdout:
                    print("API_REQ", buffer)
                self.api_response("hello")
                self.uc.emu_stop()
            else:
                self.has_error = True

            self.uc.emu_stop()

    def api_response(self, *args):
        bufs = json.dumps(args)
        buf = bufs.encode("utf-8")
        if self.state.write_to_stdout:
            print("API_RES", buf)
        self.uc.mem_write(MemoryMap.SYSCALL_BUFFER.address, buf)
        self.uc.mem_write(MemoryMap.SYSCALL_BUFFER.address + len(buf), b'\0')
        self.uc.reg_write(UC_ARM_REG_R0, MemoryMap.SYSCALL_BUFFER.address)
        self.uc.reg_write(UC_ARM_REG_R1, len(buf))

    def hook_peripheral_read(self, uc: Uc, access, address, size, value, data):
        if address == PeripheralAddress.OP_CON_RAM_SIZE:
            uc.mem_write(address, to_bytes(self.state.ram_size))
        elif address == PeripheralAddress.OP_IO_RXR:
            if self.state.input_buffer:
                uc.mem_write(address, to_bytes(self.state.input_buffer.pop(0)))
            else:
                uc.mem_write(address, to_bytes(0))
        elif address == PeripheralAddress.OP_RTC_TICKS_MS:
            pass
            # uc.mem_write(address, to_bytes(int((time.time() - self.state.epoch) * 1000)))
        else:
            if self.verbose >= 1:
                print("read", access, hex(address), size, value, data)

    def hook_peripheral_write(self, uc: Uc, access, address, size, value,
                              data):
        if address == PeripheralAddress.OP_CON_PENDING:
            if self.verbose >= 1:
                print("OPENPYTHON_CONTROLLER_PENDING", value)
        elif address == PeripheralAddress.OP_CON_EXCEPTION:
            if self.verbose >= 1:
                print("OPENPYTHON_CONTROLLER_EXCEPTION", value)
        elif address == PeripheralAddress.OP_CON_INTR_CHAR:
            if self.verbose >= 1:
                print("OPENPYTHON_CONTROLLER_INTR_CHAR", value)
        elif address == PeripheralAddress.OP_IO_TXR:
            self.state.output_storage.append(value)
            if self.state.write_to_stdout:
                print(chr(value), end="")
            sys.stdout.flush()
        else:
            if self.verbose >= 1:
                print("write", access, hex(address), size, value, data)

    def hook_unmapped(self, uc: Uc, access, address, size, value, data):
        print("unmapped:", access, hex(address), size, value, data)
        uc.emu_stop()
        self.has_error = True

    def hook_inst(self, uc: Uc, address, size, data):
        func = None
        if self.firmware:
            func = self.firmware.text_map[address]
            if func in HELPER_FUNCTIONS:
                return

        if self.last_func != func:
            self.last_func = func
            print("#inst", hex(address), func)

        self.last_addr = address

    def report_memory(self):
        total_size = 0
        for mem_start, mem_end, perm in self.uc.mem_regions():
            total_size += mem_end - mem_start
            print("memory:", hex(mem_start), hex(mem_end - mem_start), perm)
        print("memory total:", total_size / 1024, "kb")

    INST_SIZE = 2

    def debug_addr(self, addr, count=1, *, end="\n"):
        INST_SIZE = 4
        try:
            for inst in self.cs.disasm(
                    self.uc.mem_read(addr, INST_SIZE * count), addr,
                    count):  # type: CsInsn
                if self.firmware:
                    print(self.firmware.text_map[inst.address], end=" ")

                print(hex(inst.address),
                      hex(from_bytes(inst.bytes)),
                      inst.mnemonic,
                      inst.op_str,
                      end=end)
        except UcError as exc:
            if exc.errno == UC_ERR_READ_UNMAPPED:
                print("fail to read memory", hex(addr))

    def debug_addr_bin(self, addr, count=1):
        INST_SIZE = 4
        try:
            for inst in self.cs.disasm(
                    self.uc.mem_read(addr, INST_SIZE * count), addr,
                    count):  # type: CsInsn
                if self.firmware:
                    print(self.firmware.text_map[inst.address], end=" ")

                if len(inst.bytes) != 2:
                    raise Exception(
                        f"len(inst) != 2; {inst.bytes} => {inst.mnemonic} {inst.op_str}"
                    )

                bcode = bin(from_bytes(inst.bytes))[2:].zfill(16)
                print(hex(inst.address), bcode[0:4], bcode[4:8], bcode[8:12],
                      bcode[12:16], inst.mnemonic, inst.op_str)
        except UcError as exc:
            if exc.errno == UC_ERR_READ_UNMAPPED:
                print("fail to read memory", hex(addr))