Пример #1
0
 def angr_load_mapped_pages(
     self, uc: Uc, state: angr.SimState
 ) -> List[Tuple[int, int, int]]:
     """
     Loads all currently mapped unicorn mem regions into angr
     :param uc: The uc instance to load from
     :param state: the angr instance to load to
     :returns Lst of pages mapped
     """
     mapped = []
     for begin, end, perms in uc.mem_regions():
         mapped += (begin, end - begin + 1, perms)
         angr_store_mem(state, begin, bytes(uc.mem_read(begin, end - begin + 1)))
     return mapped
Пример #2
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'))
Пример #3
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'))
Пример #4
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))