Exemplo n.º 1
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)
Exemplo n.º 2
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)
Exemplo n.º 3
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)
Exemplo n.º 4
0
def get_msr(uc: Uc, scratch: int, msr: int) -> int:
    """
    fetch the contents of the given model-specific register (MSR).
    this will clobber some memory at the given scratch address, as it emits some code.
    """
    # save clobbered registers
    orax = uc.reg_read(UC_X86_REG_RAX)
    ordx = uc.reg_read(UC_X86_REG_RDX)
    orcx = uc.reg_read(UC_X86_REG_RCX)
    orip = uc.reg_read(UC_X86_REG_RIP)

    # x86: rdmsr
    buf = b"\x0f\x32"
    uc.mem_write(scratch, buf)
    uc.reg_write(UC_X86_REG_RCX, msr & 0xFFFFFFFF)
    uc.emu_start(scratch, scratch + len(buf), count=1)
    eax = uc.reg_read(UC_X86_REG_EAX)
    edx = uc.reg_read(UC_X86_REG_EDX)

    # restore clobbered registers
    uc.reg_write(UC_X86_REG_RAX, orax)
    uc.reg_write(UC_X86_REG_RDX, ordx)
    uc.reg_write(UC_X86_REG_RCX, orcx)
    uc.reg_write(UC_X86_REG_RIP, orip)

    return (edx << 32) | (eax & 0xFFFFFFFF)
Exemplo n.º 5
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}")
Exemplo n.º 6
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)
Exemplo n.º 7
0
 def set_exits(self, uc: Uc, base_address: int, exits: List[int]):
     """
     We replace all hooks and exits with syscalls since they should be rare in kernel code.
     Then, when we encounter a syscall, we figure out if a syscall or exit occurred.
     This can also be used to add additional hooks in the future.
     :param uc: Unicorn instance
     :param exits: The exit counts
     :param base_address: the address we're mapping
     """
     arch = self.arch
     # TODO: This only works for X64!
     if exits is None:
         self._deferred_exits.append(base_address)
     else:
         if len(exits) <= 1:
             # No need to patch anything, uc supports a single exit.
             return
         if len(exits) > 1:
             if arch == X64:
                 for end_addr in exits:
                     if self.get_base(end_addr) == base_address:
                         print(
                             "[*] Setting exit 0x{:016x}".format(end_addr))
                         # Abusing the syscall opcode for exits.
                         # Just make sure the whole opcode will fit here by trying to map the end addr.
                         self.map_page(
                             uc, end_addr + len(x64utils.SYSCALL_OPCODE))
                         uc.mem_write(end_addr, x64utils.SYSCALL_OPCODE)
             else:
                 raise (ValueError(
                     "Multiple exits are not yet supported for arch {} - {}"
                     .format(arch, exits)))
Exemplo n.º 8
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)
Exemplo n.º 9
0
def place_input(ucf: Unicorefuzz, uc: Uc, input: bytes) -> None:
    """
    Places the input in memory and alters the input.
    This is an example for sk_buff in openvsswitch
    """
    if len(input) < 1500:
        import os
        os._exit(0)
    rdx = uc.reg_read(UC_X86_REG_RDX)  # struct sk_buff* skb
    ucf.map_page(uc, rdx)  # ensure sk_buf is mapped
    data_ptr = struct.unpack("<Q", uc.mem_read(rdx + 0xD0, 8))[0]
    ucf.map_page(uc, data_ptr)  # ensure the buffer is mapped
    uc.mem_write(data_ptr, input)  # insert afl input
Exemplo n.º 10
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
Exemplo n.º 11
0
def place_input_skb(ucf: Unicorefuzz, uc: Uc, input: bytes) -> None:
    """
    Places the input in memory and alters the input.
    This is an example for sk_buff in openvsswitch
    """

    if len(input) > 1500:
        import os

        os._exit(0)  # too big!

    # read input to the correct position at param rdx here:
    rdx = uc.reg_read(UC_X86_REG_RDX)
    rdi = uc.reg_read(UC_X86_REG_RDI)
    ucf.map_page(uc, rdx)  # ensure sk_buf is mapped
    bufferPtr = struct.unpack("<Q", uc.mem_read(rdx + 0xD8, 8))[0]
    ucf.map_page(uc, bufferPtr)  # ensure the buffer is mapped
    uc.mem_write(rdi, input)  # insert afl input
    uc.mem_write(rdx + 0xC4, b"\xdc\x05")  # fix tail
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    def speculate_instruction(emulator: Uc, address, size, model) -> None:
        # reached max spec. window? skip
        if len(model.checkpoints) >= model.nesting:
            return

        if model.previous_store[0]:
            store_addr = model.previous_store[0]
            old_value = bytes(model.previous_store[2])
            new_is_signed = model.previous_store[3] < 0
            new_value = (model.previous_store[3]). \
                to_bytes(model.previous_store[1], byteorder='little', signed=new_is_signed)

            # store a checkpoint
            model.checkpoint(emulator, address)

            # cancel the previous store but preserve its value
            emulator.mem_write(store_addr, old_value)
            model.store_logs[-1].append((store_addr, new_value))
        model.previous_store = (0, 0, 0, 0)
Exemplo n.º 14
0
def load_mem(mu: Uc, addr, size):
    filename = "7.4AT-02+V2.001.EFM.bin"
    data = None
    with open(filename, "rb") as fh:
        fh.seek(addr)
        data = fh.read(size)

    _addr = addr & PAGE_MASK
    _size = ((size) & PAGE_MASK) + PAGE_SIZE

    print(f"[+] Mapping Memory:\n\tAddress: {_addr:08X}\n\tSize: {_size:X}")
    mu.mem_map(_addr, _size)

    load_addr = addr
    load_size = size
    print(
        f"[>] Loading Memory:\n\tAddress: {load_addr:08X}\n\tSize: {load_size:X}"
    )
    mu.mem_write(load_addr, data)
Exemplo n.º 15
0
 def map_page(self, uc: Uc, addr: int) -> None:
     """
     Maps a page at addr in the harness, asking probe_wrapper.
     :param uc: The unicore
     :param addr: The address
     """
     page_size = self.config.PAGE_SIZE
     base_address = self.get_base(addr)
     if base_address not in self._mapped_page_cache.keys():
         input_file_name = os.path.join(self.requestdir,
                                        "{:016x}".format(addr))
         dump_file_name = os.path.join(self.statedir,
                                       "{:016x}".format(base_address))
         if os.path.isfile(dump_file_name + REJECTED_ENDING):
             print("CAN I HAZ EXPLOIT?")
             os.kill(os.getpid(), signal.SIGSEGV)
         if not os.path.isfile(dump_file_name):
             open(input_file_name, "a").close()
         print("mapping {}".format(hex(base_address)))
         while 1:
             try:
                 if os.path.isfile(dump_file_name + REJECTED_ENDING):
                     print("CAN I HAZ EXPLOIT?")
                     os.kill(os.getpid(), signal.SIGSEGV)
                 with open(dump_file_name, "rb") as f:
                     content = f.read()
                     if len(content) < page_size:
                         time.sleep(0.001)
                         continue
                     self._mapped_page_cache[base_address] = content
                     uc.mem_map(base_address, len(content))
                     uc.mem_write(base_address, content)
                     self.set_exits(uc, base_address, self.exits)
                     return
             except IOError:
                 pass
             except UcError as ex:
                 return
             except Exception as ex:  # todo this should never happen if we don't map like idiots
                 print(
                     "map_page_blocking failed: base address=0x{:016x} ({})"
                     .format(base_address, ex))
Exemplo n.º 16
0
def set_msr(uc: Uc, scratch: int, msr: int, val: int) -> None:
    """
    set the given model-specific register (MSR) to the given value.
    this will clobber some memory at the given scratch address, as it emits some code.
    """
    # save clobbered registers
    orax = uc.reg_read(UC_X86_REG_RAX)
    ordx = uc.reg_read(UC_X86_REG_RDX)
    orcx = uc.reg_read(UC_X86_REG_RCX)
    orip = uc.reg_read(UC_X86_REG_RIP)

    # x86: wrmsr
    uc.mem_write(scratch, INSN_WRMSR)
    uc.reg_write(UC_X86_REG_RAX, val & 0xFFFFFFFF)
    uc.reg_write(UC_X86_REG_RDX, (val >> 32) & 0xFFFFFFFF)
    uc.reg_write(UC_X86_REG_RCX, msr & 0xFFFFFFFF)
    uc.emu_start(scratch, scratch + len(INSN_WRMSR), count=1)

    # restore clobbered registers
    uc.reg_write(UC_X86_REG_RAX, orax)
    uc.reg_write(UC_X86_REG_RDX, ordx)
    uc.reg_write(UC_X86_REG_RCX, orcx)
    uc.reg_write(UC_X86_REG_RIP, orip)
Exemplo n.º 17
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)
Exemplo n.º 18
0
 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)
Exemplo n.º 19
0
        ret |= UC_PROT_WRITE
    if p_flags & P_FLAGS.PF_X != 0:
        ret |= UC_PROT_EXEC
    return ret


load_base = 0
emu = Uc(UC_ARCH_ARM, UC_MODE_LITTLE_ENDIAN)
load_segments = [
    x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD'
]
for segment in load_segments:
    fr_addr, size = align(load_base + segment.header.p_vaddr,
                          segment.header.p_memsz, segment.header.p_align)
    emu.mem_map(fr_addr, size, pflags2prot(segment.header.p_flags))
    emu.mem_write(load_base + segment.header.p_vaddr, segment.data())

STACK_ADDR = 0x7F000000
STACK_SIZE = 1024 * 1024
start_addr = None
emu.mem_map(STACK_ADDR, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE)

bss_section_header = elf.get_section_by_name('.bss').header
bss_section_start, bss_section_end = bss_section_header.sh_addr, bss_section_header.sh_addr + bss_section_header.sh_size
data_section_header = elf.get_section_by_name('.data').header
data_section_start, data_section_end = data_section_header.sh_addr, data_section_header.sh_addr + data_section_header.sh_size


def dbg_hook_memory_access(uc: Uc, access, address, size, value, data):
    if access == UC_MEM_WRITE:
        print("Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(
Exemplo n.º 20
0
    ARM_BYTECODE, _ = ks.asm(ARM_CODE)
    # convert the array of integers into bytes
    ARM_BYTECODE = bytes(ARM_BYTECODE)
    print(f"Code successfully assembled (length = {len(ARM_BYTECODE)})")
    print("ARM bytecode:", ARM_BYTECODE)
except KsError as e:
    print("Keystone Error: %s" % e)
    exit(1)

# memory address where emulation starts
ADDRESS = 0x1000000

print("Emulating the ARM code")
try:
    # Initialize emulator in ARM mode
    mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
    # 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, ARM_BYTECODE)
    # Set the r0 register in the code, let's calculate factorial(5)
    mu.reg_write(UC_ARM_REG_R0, 5)
    # emulate code in infinite time and unlimited instructions
    mu.emu_start(ADDRESS, ADDRESS + len(ARM_BYTECODE))
    # now print out the R0 register
    print("Emulation done. Below is the result")
    # retrieve the result from the R1 register
    r1 = mu.reg_read(UC_ARM_REG_R1)
    print(">>  R1 = %u" % r1)
except UcError as e:
    print("Unicorn Error: %s" % e)
Exemplo n.º 21
0
class Emulator:
    """
    :type mu Uc
    :type modules Modules
    :type memory Memory
    """
    def __init__(self, vfs_root=None, vfp_inst_set=False):
        # Unicorn.
        self.mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)

        if vfp_inst_set:
            self._enable_vfp()

        # Android
        self.system_properties = {"libc.debug.malloc.options": ""}

        # Stack.
        self.mu.mem_map(config.STACK_ADDR, config.STACK_SIZE)
        self.mu.reg_write(UC_ARM_REG_SP, config.STACK_ADDR + config.STACK_SIZE)

        # Executable data.
        self.modules = Modules(self)
        self.memory = Memory(self)

        # CPU
        self.interrupt_handler = InterruptHandler(self.mu)
        self.syscall_handler = SyscallHandlers(self.interrupt_handler)
        self.syscall_hooks = SyscallHooks(self.mu, self.syscall_handler)

        # File System
        if vfs_root is not None:
            self.vfs = VirtualFileSystem(vfs_root, self.syscall_handler)
        else:
            self.vfs = None

        # Hooker
        self.mu.mem_map(config.HOOK_MEMORY_BASE, config.HOOK_MEMORY_SIZE)
        self.hooker = Hooker(self, config.HOOK_MEMORY_BASE,
                             config.HOOK_MEMORY_SIZE)

        # JavaVM
        self.java_classloader = JavaClassLoader()
        self.java_vm = JavaVM(self, self.java_classloader, self.hooker)

        # Native
        self.native_memory = NativeMemory(self.mu, config.HEAP_BASE,
                                          config.HEAP_SIZE,
                                          self.syscall_handler)
        self.native_hooks = NativeHooks(self, self.native_memory, self.modules,
                                        self.hooker)

        # Tracer
        self.tracer = Tracer(self.mu, self.modules)

    # https://github.com/unicorn-engine/unicorn/blob/8c6cbe3f3cabed57b23b721c29f937dd5baafc90/tests/regress/arm_fp_vfp_disabled.py#L15
    def _enable_vfp(self):
        # MRC p15, #0, r1, c1, c0, #2
        # ORR r1, r1, #(0xf << 20)
        # MCR p15, #0, r1, c1, c0, #2
        # MOV r1, #0
        # MCR p15, #0, r1, c7, c5, #4
        # MOV r0,#0x40000000
        # FMXR FPEXC, r0
        code = '11EE501F'
        code += '41F47001'
        code += '01EE501F'
        code += '4FF00001'
        code += '07EE951F'
        code += '4FF08040'
        code += 'E8EE100A'
        # vpush {d8}
        code += '2ded028b'

        address = 0x1000
        mem_size = 0x1000
        code_bytes = bytes.fromhex(code)

        try:
            self.mu.mem_map(address, mem_size)
            self.mu.mem_write(address, code_bytes)
            self.mu.reg_write(UC_ARM_REG_SP, address + mem_size)

            self.mu.emu_start(address | 1, address + len(code_bytes))
        finally:
            self.mu.mem_unmap(address, mem_size)

    def _call_init_array(self):
        pass

    def load_library(self, filename, do_init=True):
        libmod = self.modules.load_module(filename)
        if do_init:
            logger.debug("Calling Init for: %s " % filename)
            for fun_ptr in libmod.init_array:
                logger.debug("Calling Init function: %x " % fun_ptr)
                self.call_native(fun_ptr)
        return libmod

    def call_symbol(self, module, symbol_name, *argv):
        symbol = module.find_symbol(symbol_name)

        if symbol is None:
            logger.error('Unable to find symbol \'%s\' in module \'%s\'.' %
                         (symbol_name, module.filename))
            return

        self.call_native(symbol.address, *argv)

    def call_native(self, addr, *argv):
        # Detect JNI call
        is_jni = False

        if len(argv) >= 1:
            is_jni = argv[0] == self.java_vm.address_ptr or argv[
                0] == self.java_vm.jni_env.address_ptr

        # TODO: Write JNI args to local ref table if jni.

        try:
            # Execute native call.

            native_write_args(self, *argv)
            stop_pos = randint(HOOK_MEMORY_BASE,
                               HOOK_MEMORY_BASE + HOOK_MEMORY_SIZE) | 1
            self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
            self.mu.emu_start(addr, stop_pos - 1)

            # Read result from locals if jni.
            if is_jni:
                result_idx = self.mu.reg_read(UC_ARM_REG_R0)
                result = self.java_vm.jni_env.get_local_reference(result_idx)

                if result is None:
                    return result

                return result.value
        finally:
            # Clear locals if jni.
            if is_jni:
                self.java_vm.jni_env.clear_locals()

    def dump(self, out_dir):
        os.makedirs(out_dir)

        for begin, end, prot in [reg for reg in self.mu.mem_regions()]:
            filename = "{:#010x}-{:#010x}.bin".format(begin, end)
            pathname = os.path.join(out_dir, filename)
            with open(pathname, "w") as f:
                f.write(
                    hexdump.hexdump(self.mu.mem_read(begin, end - begin),
                                    result='return'))
Exemplo n.º 22
0
class Emulator:
    """
    :type mu Uc
    :type modules Modules
    :type memory Memory
    """
    def __init__(self, vfs_root=None, vfp_inst_set=False):
        # Unicorn.
        self.mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)

        if vfp_inst_set:
            self._enable_vfp()

        # Stack.
        self.mu.mem_map(config.STACK_ADDR, config.STACK_SIZE)
        self.mu.reg_write(UC_ARM_REG_SP, config.STACK_ADDR + config.STACK_SIZE)

        # Executable data.
        self.modules = Modules(self)
        self.memory = Memory(self)

        # CPU
        self.interrupt_handler = InterruptHandler(self.mu)
        self.syscall_handler = SyscallHandlers(self.interrupt_handler)
        self.syscall_hooks = SyscallHooks(self.mu, self.syscall_handler)

        # File System
        if vfs_root is not None:
            self.vfs = VirtualFileSystem(vfs_root, self.syscall_handler)
        else:
            self.vfs = None

        # Hooker
        self.mu.mem_map(config.MEMORY_BASE, config.MEMORY_SIZE)
        self.hooker = Hooker(self, config.MEMORY_BASE, config.MEMORY_SIZE)

        # JavaVM
        self.java_classloader = JavaClassLoader()
        self.java_vm = JavaVM(self.java_classloader, self.hooker)

        # Native
        self.native_memory = NativeMemory(self.mu, config.MEMORY_DYN_BASE,
                                          config.MEMORY_DYN_SIZE,
                                          self.syscall_handler)
        self.native_hooks = NativeHooks(self.native_memory, self.modules,
                                        self.hooker)

    # https://github.com/unicorn-engine/unicorn/blob/8c6cbe3f3cabed57b23b721c29f937dd5baafc90/tests/regress/arm_fp_vfp_disabled.py#L15
    def _enable_vfp(self):
        # MRC p15, #0, r1, c1, c0, #2
        # ORR r1, r1, #(0xf << 20)
        # MCR p15, #0, r1, c1, c0, #2
        # MOV r1, #0
        # MCR p15, #0, r1, c7, c5, #4
        # MOV r0,#0x40000000
        # FMXR FPEXC, r0
        code = '11EE501F'
        code += '41F47001'
        code += '01EE501F'
        code += '4FF00001'
        code += '07EE951F'
        code += '4FF08040'
        code += 'E8EE100A'
        # vpush {d8}
        code += '2ded028b'

        address = 0x1000
        mem_size = 0x1000
        code_bytes = bytes.fromhex(code)

        try:
            self.mu.mem_map(address, mem_size)
            self.mu.mem_write(address, code_bytes)
            self.mu.reg_write(UC_ARM_REG_SP, address + mem_size)

            self.mu.emu_start(address | 1, address + len(code_bytes))
        finally:
            self.mu.mem_unmap(address, mem_size)

    def load_library(self, filename):
        return self.modules.load_module(filename)

    def call_symbol(self, module, symbol_name, *argv):
        symbol = module.find_symbol(symbol_name)

        if symbol is None:
            logger.error('Unable to find symbol \'%s\' in module \'%s\'.' %
                         (symbol_name, module.filename))
            return

        self.call_native(symbol.address, *argv)

    def call_native(self, addr, *argv):
        # Detect JNI call
        is_jni = False

        if len(argv) >= 1:
            is_jni = argv[0] == self.java_vm.address_ptr or argv[
                0] == self.java_vm.jni_env.address_ptr

        # TODO: Write JNI args to local ref table if jni.

        try:
            # Execute native call.
            native_write_args(self.mu, *argv)
            stop_pos = randint(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE) | 1
            self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
            self.mu.emu_start(addr, stop_pos - 1)

            # Read result from locals if jni.
            if is_jni:
                result_idx = self.mu.reg_read(UC_ARM_REG_R0)
                result = self.java_vm.jni_env.get_local_reference(result_idx)

                if result is None:
                    return result

                return result.value
        finally:
            # Clear locals if jni.
            if is_jni:
                self.java_vm.jni_env.clear_locals()
Exemplo n.º 23
0
def set_exit(uc: Uc, addr: int) -> None:
    """
    We use syscalls for this as unicorn offers hooks. 
    Could also throw an UB2 instead and catch.
    """
    uc.mem_write(addr, SYSCALL_OPCODE)
Exemplo n.º 24
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))
Exemplo n.º 25
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()
# memory address where emulation starts
ADDRESS = 0x1000000

try:
    # Initialize the disassembler in x86 mode
    md = Cs(CS_ARCH_X86, CS_MODE_64)
    # iterate over each instruction and print it
    for instruction in md.disasm(X86_MACHINE_CODE, 0x1000):
        print("0x%x:\t%s\t%s" %
              (instruction.address, instruction.mnemonic, instruction.op_str))
except CsError as e:
    print("Capstone Error: %s" % e)

try:
    # Initialize emulator in x86_64 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_MACHINE_CODE)
    # Set the r0 register in the code to the number of 7
    mu.reg_write(UC_X86_REG_RDI, 7)
    # emulate code in infinite time & unlimited instructions
    mu.emu_start(ADDRESS, ADDRESS + len(X86_MACHINE_CODE))
    # now print out the R0 register
    print("Emulation done. Below is the result")
    rax = mu.reg_read(UC_X86_REG_RAX)
    print(">>> RAX = %u" % rax)
except UcError as e:
    print("Unicorn Error: %s" % e)
Exemplo n.º 27
0
class Emulator:
    """
    :type mu Uc
    :type modules Modules
    """
    def __init__(self, vfs_root: str = None, vfp_inst_set: bool = False):
        # Unicorn.
        self.mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)

        if vfp_inst_set:
            self._enable_vfp()

        # Android
        self.system_properties = {"libc.debug.malloc.options": ""}

        # Stack.
        self.mu.mem_map(STACK_ADDR, STACK_SIZE)
        self.mu.reg_write(UC_ARM_REG_SP, STACK_ADDR + STACK_SIZE)

        # Executable data.
        self.modules = Modules(self)
        self.memory_manager = MemoryManager(self.mu)

        # CPU
        self.interrupt_handler = InterruptHandler(self.mu)
        self.syscall_handler = SyscallHandlers(self.interrupt_handler)
        self.syscall_hooks = SyscallHooks(self.mu, self.syscall_handler, self.modules)
        self.syscall_hooks_memory = SyscallHooksMemory(self.mu, self.memory_manager, self.syscall_handler)

        # File System
        if vfs_root is not None:
            self.vfs = VirtualFileSystem(vfs_root, self.syscall_handler)
        else:
            self.vfs = None

        # Hooker
        self.mu.mem_map(HOOK_MEMORY_BASE, HOOK_MEMORY_SIZE)
        self.hooker = Hooker(self, HOOK_MEMORY_BASE, HOOK_MEMORY_SIZE)

        # JavaVM
        self.java_classloader = JavaClassLoader()
        self.java_vm = JavaVM(self, self.java_classloader, self.hooker)

        # Native
        self.native_hooks = NativeHooks(self, self.memory_manager, self.modules, self.hooker)

        # Tracer
        self.tracer = Tracer(self.mu, self.modules)

        # Thread.
        self._setup_thread_register()

    # https://github.com/unicorn-engine/unicorn/blob/8c6cbe3f3cabed57b23b721c29f937dd5baafc90/tests/regress/arm_fp_vfp_disabled.py#L15
    def _enable_vfp(self):
        # MRC p15, #0, r1, c1, c0, #2
        # ORR r1, r1, #(0xf << 20)
        # MCR p15, #0, r1, c1, c0, #2
        # MOV r1, #0
        # MCR p15, #0, r1, c7, c5, #4
        # MOV r0,#0x40000000
        # FMXR FPEXC, r0
        code = '11EE501F'
        code += '41F47001'
        code += '01EE501F'
        code += '4FF00001'
        code += '07EE951F'
        code += '4FF08040'
        code += 'E8EE100A'
        # vpush {d8}
        code += '2ded028b'

        address = 0x1000
        mem_size = 0x1000
        code_bytes = bytes.fromhex(code)

        try:
            self.mu.mem_map(address, mem_size)
            self.mu.mem_write(address, code_bytes)
            self.mu.reg_write(UC_ARM_REG_SP, address + mem_size)

            self.mu.emu_start(address | 1, address + len(code_bytes))
        finally:
            self.mu.mem_unmap(address, mem_size)

    def _setup_thread_register(self):
        """
        Set up thread register.
        This is currently not accurate and just filled with garbage to ensure the emulator does not crash.

        https://developer.arm.com/documentation/ddi0211/k/system-control-coprocessor/system-control-coprocessor-register-descriptions/c13--thread-and-process-id-registers
        """
        thread_info_size = 64
        thread_info = self.memory_manager.allocate(thread_info_size * 5)

        thread_info_1 = thread_info + (thread_info_size * 0)
        thread_info_2 = thread_info + (thread_info_size * 1)
        thread_info_3 = thread_info + (thread_info_size * 2)
        thread_info_4 = thread_info + (thread_info_size * 3)
        thread_info_5 = thread_info + (thread_info_size * 4)

        # Thread name
        write_utf8(self.mu, thread_info_5, "AndroidNativeEmu")

        # R4
        self.mu.mem_write(thread_info_2 + 0x4, int(thread_info_5).to_bytes(4, byteorder='little'))
        self.mu.mem_write(thread_info_2 + 0xC, int(thread_info_3).to_bytes(4, byteorder='little'))

        # R1
        self.mu.mem_write(thread_info_1 + 0x4, int(thread_info_4).to_bytes(4, byteorder='little'))
        self.mu.mem_write(thread_info_1 + 0xC, int(thread_info_2).to_bytes(4, byteorder='little'))
        self.mu.reg_write(UC_ARM_REG_C13_C0_3, thread_info_1)

    def load_library(self, filename, do_init=True):
        libmod = self.modules.load_module(filename)
        if do_init:
            logger.debug("Calling init for: %s " % filename)
            for fun_ptr in libmod.init_array:
                logger.debug("Calling Init function: %x " % fun_ptr)
                self.call_native(fun_ptr, 0, 0, 0)
        return libmod

    def call_symbol(self, module, symbol_name, *argv, is_return_jobject=True):
        symbol = module.find_symbol(symbol_name)

        if symbol is None:
            logger.error('Unable to find symbol \'%s\' in module \'%s\'.' % (symbol_name, module.filename))
            return

        return self.call_native(symbol.address, *argv, is_return_jobject=is_return_jobject)

    def call_native(self, addr, *argv, is_return_jobject=True):
        # Detect JNI call
        is_jni = False

        if len(argv) >= 1:
            is_jni = argv[0] == self.java_vm.address_ptr or argv[0] == self.java_vm.jni_env.address_ptr

        # TODO: Write JNI args to local ref table if jni.

        try:
            # Execute native call.
            self.mu.reg_write(UC_ARM_REG_SP, STACK_ADDR + STACK_SIZE)
            native_write_args(self, *argv)
            stop_pos = randint(HOOK_MEMORY_BASE, HOOK_MEMORY_BASE + HOOK_MEMORY_SIZE) | 1
            self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
            self.mu.emu_start(addr, stop_pos - 1)

            # Read result from locals if jni.
            if is_jni and is_return_jobject:
                result_idx = self.mu.reg_read(UC_ARM_REG_R0)
                result = self.java_vm.jni_env.get_local_reference(result_idx)

                if result is None:
                    return result

                return result.value
            else:
                return self.mu.reg_read(UC_ARM_REG_R0)
        finally:
            # Clear locals if jni.
            if is_jni:
                self.java_vm.jni_env.clear_locals()

    def dump(self, out_dir):
        os.makedirs(out_dir)

        for begin, end, prot in [reg for reg in self.mu.mem_regions()]:
            filename = "{:#010x}-{:#010x}.bin".format(begin, end)
            pathname = os.path.join(out_dir, filename)
            with open(pathname, "w") as f:
                f.write(hexdump.hexdump(self.mu.mem_read(begin, end - begin), result='return'))
Exemplo n.º 28
0
def stat_to_memory(uc: Uc, buf_ptr, stat, write_times):
    uc.mem_write(buf_ptr, stat['st_dev'].to_bytes(8, byteorder='little'))
    uc.mem_write(buf_ptr + 8, int(0).to_bytes(4, byteorder='little'))  # PAD 4
    uc.mem_write(buf_ptr + 12, stat['__st_ino'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 16, stat['st_mode'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 20, stat['st_nlink'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 24, stat['st_uid'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 28, stat['st_gid'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 32, stat['st_rdev'].to_bytes(8, byteorder='little'))
    uc.mem_write(buf_ptr + 40, int(0).to_bytes(4, byteorder='little'))  # PAD 4
    uc.mem_write(buf_ptr + 44, int(0).to_bytes(4, byteorder='little'))  # PAD 4
    uc.mem_write(buf_ptr + 48, stat['st_size'].to_bytes(8, byteorder='little'))
    uc.mem_write(buf_ptr + 56, stat['st_blksize'].to_bytes(4, byteorder='little'))
    uc.mem_write(buf_ptr + 60, int(0).to_bytes(4, byteorder='little'))  # PAD 4
    uc.mem_write(buf_ptr + 64, stat['st_blocks'].to_bytes(8, byteorder='little'))

    if write_times:
        uc.mem_write(buf_ptr + 72, stat['st_atime'].to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 76, stat['st_atime_ns'].to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 80, stat['st_mtime'].to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 84, stat['st_mtime_ns'].to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 88, stat['st_ctime'].to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 92, stat['st_ctime_ns'].to_bytes(4, byteorder='little'))
    else:
        uc.mem_write(buf_ptr + 72, int(0).to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 76, int(0).to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 80, int(0).to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 84, int(0).to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 88, int(0).to_bytes(4, byteorder='little'))
        uc.mem_write(buf_ptr + 92, int(0).to_bytes(4, byteorder='little'))

    uc.mem_write(buf_ptr + 96, stat['st_ino'].to_bytes(8, byteorder='little'))
Exemplo n.º 29
0
def hook_memory(uc, access, address, size, value, user_data):
    pc = uc.reg_read(UC_ARM_REG_PC)
    print(f">>> Memory err pc:0x{pc:x} address:0x{address:x}, size:0x{size:x}")


a1 = b'123'
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)

# 分配 so 内存
image_base = 0x0
image_size = 0x10000 * 8
mu.mem_map(image_base, image_size)
with open("libnative-lib.so", "rb") as f:
    sofile = f.read()
    mu.mem_write(image_base, sofile)

# 分配 Stack 内存
stack_base = 0xA0000
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)
Exemplo n.º 30
0
def place_input(ucf: Unicorefuzz, uc: Uc, input: bytes) -> None:
    rax = uc.reg_read(UC_X86_REG_RAX)
    # make sure the parameter memory is mapped
    ucf.map_page(uc, rax)
    uc.mem_write(rax, input)  # insert afl input