def uc_get_pc(self, uc) -> int: """ Gets the current pc from unicorn for this arch :param uc: the unicorn instance :return: value of the pc """ return uc_get_pc(uc, self.arch)
def uc_run(self, uc: Uc, entry_point: int, exit_point: int) -> None: """ Run initialized unicorn :param entry_point: The entry point :param exit_point: First final address. Hack something to get more exits :param uc: The unicorn instance to run """ try: uc.emu_start(begin=entry_point, until=exit_point, timeout=0, count=0) except UcError as e: print( "[!] Execution failed with error: {} at address {:x}".format( e, uc_get_pc(uc, self.arch) ) ) self.force_crash(e) # Exit without clean python vm shutdown: # "The os._exit() function can be used if it is absolutely positively necessary to exit immediately" # Many times faster! os._exit(0)
def uc_init( self, input_file, wait: bool = False, trace: bool = False, verbose: bool = False ) -> Tuple[Uc, int, List[int]]: """ Initializes unicorn with the given params :param input_file: input file to drop into the emulator with config.init_func :param wait: block until state dir becomes available :param trace: if we should add trace hooks to unicorn :param verbose: enables some more logging :return: Tuple of (unicorn, entry_point, exits) """ config = self.config uc = Uc(self.arch.unicorn_arch, self.arch.unicorn_mode) if trace: print("[+] Settings trace hooks") uc.hook_add(UC_HOOK_BLOCK, unicorn_debug_block) uc.hook_add(UC_HOOK_CODE, unicorn_debug_instruction, self) uc.hook_add( UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ | UC_HOOK_MEM_FETCH, unicorn_debug_mem_access, ) if wait: self.wait_for_probe_wrapper() if verbose: print("[*] Reading from file {}".format(input_file)) # we leave out gs_base and fs_base on x64 since they start the forkserver self.uc_load_registers(uc) # let's see if the user wants a change. config.init_func(self, uc) # get pc from unicorn state since init_func may have altered it. pc = uc_get_pc(uc, self.arch) exits = self.calculate_exits(pc) # mappings used in init_func didn't have the pc yet. for ex in self._deferred_exits: self.set_exits(uc, ex, exits) self.map_known_mem(uc) if not exits: raise ValueError( "No exits founds. Would run forever... Please set an exit address in config.py." ) entry_point = pc # On error: map memory, add exits. uc.hook_add(UC_HOOK_MEM_UNMAPPED, unicorn_debug_mem_invalid_access, self) if len(exits) > 1: # unicorn supports a single exit only (using the length param). # We'll path the binary on load if we have need to support more. if self.arch == X64: uc.hook_add( UC_HOOK_INSN, syscall_exit_hook, user_data=(exits, os._exit), arg1=UC_X86_INS_SYSCALL, ) else: # TODO: (Fast) solution for X86, ARM, ... raise Exception( "Multiple exits not yet supported for arch {}".format(self.arch) ) # starts the afl forkserver self.uc_start_forkserver(uc) input_file = open(input_file, "rb") # load afl's input input = input_file.read() input_file.close() try: config.place_input(self, uc, input) except Exception as ex: raise Exception( "[!] Error setting testcase for input {}: {}".format(input, ex) ) return uc, entry_point, exits