diff --git a/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py b/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py index 22b9fd47..8c8f9641 100644 --- a/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py +++ b/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py @@ -1,13 +1,13 @@ """ unicorn_dumper_gdb.py - + When run with GDB sitting at a debug breakpoint, this dumps the current state (registers/memory/etc) of - the process to a directory consisting of an index - file with register and segment information and + the process to a directory consisting of an index + file with register and segment information and sub-files containing all actual process memory. - - The output of this script is expected to be used + + The output of this script is expected to be used to initialize context for Unicorn emulation. ----------- @@ -44,6 +44,7 @@ MAX_SEG_SIZE = 128 * 1024 * 1024 # Name of the index file INDEX_FILE_NAME = "_index.json" + #---------------------- #---- Helper Functions @@ -59,14 +60,14 @@ def map_arch(): return "arm64be" elif 'armeb' in arch: # check for THUMB mode - cpsr = get_register('cpsr') + cpsr = get_register('$cpsr') if (cpsr & (1 << 5)): return "armbethumb" else: return "armbe" elif 'arm' in arch: # check for THUMB mode - cpsr = get_register('cpsr') + cpsr = get_register('$cpsr') if (cpsr & (1 << 5)): return "armlethumb" else: @@ -88,19 +89,15 @@ def dump_regs(): reg_state = {} for reg in current_arch.all_registers: reg_val = get_register(reg) - # current dumper script looks for register values to be hex strings -# reg_str = "0x{:08x}".format(reg_val) -# if "64" in get_arch(): -# reg_str = "0x{:016x}".format(reg_val) -# reg_state[reg.strip().strip('$')] = reg_str reg_state[reg.strip().strip('$')] = reg_val + return reg_state def dump_process_memory(output_dir): # Segment information dictionary final_segment_list = [] - + # GEF: vmmap = get_process_maps() if not vmmap: @@ -110,7 +107,7 @@ def dump_process_memory(output_dir): for entry in vmmap: if entry.page_start == entry.page_end: continue - + seg_info = {'start': entry.page_start, 'end': entry.page_end, 'name': entry.path, 'permissions': { "r": entry.is_readable() > 0, "w": entry.is_writable() > 0, @@ -129,7 +126,7 @@ def dump_process_memory(output_dir): compressed_seg_content = zlib.compress(seg_content) md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin" seg_info["content_file"] = md5_sum - + # Write the compressed contents to disk out_file = open(os.path.join(output_dir, md5_sum), 'wb') out_file.write(compressed_seg_content) @@ -143,12 +140,27 @@ def dump_process_memory(output_dir): # Add the segment to the list final_segment_list.append(seg_info) - + return final_segment_list +#--------------------------------------------- +#---- ARM Extention (dump floating point regs) + +def dump_float(rge=32): + reg_convert = "" + if map_arch() == "armbe" or map_arch() == "armle" or map_arch() == "armbethumb" or map_arch() == "armbethumb": + reg_state = {} + for reg_num in range(32): + value = gdb.selected_frame().read_register("d" + str(reg_num)) + reg_state["d" + str(reg_num)] = int(str(value["u64"]), 16) + value = gdb.selected_frame().read_register("fpscr") + reg_state["fpscr"] = int(str(value), 16) + + return reg_state + #---------- -#---- Main - +#---- Main + def main(): print("----- Unicorn Context Dumper -----") print("You must be actively debugging before running this!") @@ -159,32 +171,32 @@ def main(): print("!!! GEF not running in GDB. Please run gef.py by executing:") print('\tpython execfile ("/gef.py")') return - + try: - + # Create the output directory timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S') output_path = "UnicornContext_" + timestamp if not os.path.exists(output_path): os.makedirs(output_path) print("Process context will be output to {}".format(output_path)) - + # Get the context context = { "arch": dump_arch_info(), - "regs": dump_regs(), + "regs": dump_regs(), + "regs_extended": dump_float(), "segments": dump_process_memory(output_path), } # Write the index file index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w') index_file.write(json.dumps(context, indent=4)) - index_file.close() + index_file.close() print("Done.") - + except Exception as e: print("!!! ERROR:\n\t{}".format(repr(e))) - + if __name__ == "__main__": main() - diff --git a/unicorn_mode/helper_scripts/unicorn_dumper_ida.py b/unicorn_mode/helper_scripts/unicorn_dumper_ida.py index 6cf9f30f..3f955a5c 100644 --- a/unicorn_mode/helper_scripts/unicorn_dumper_ida.py +++ b/unicorn_mode/helper_scripts/unicorn_dumper_ida.py @@ -206,4 +206,4 @@ def main(): print("!!! ERROR:\n\t{}".format(str(e))) if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/unicorn_mode/helper_scripts/unicorn_loader.py b/unicorn_mode/helper_scripts/unicorn_loader.py index adf21b64..1914a83d 100644 --- a/unicorn_mode/helper_scripts/unicorn_loader.py +++ b/unicorn_mode/helper_scripts/unicorn_loader.py @@ -1,8 +1,8 @@ """ unicorn_loader.py - - Loads a process context dumped created using a - Unicorn Context Dumper script into a Unicorn Engine + + Loads a process context dumped created using a + Unicorn Context Dumper script into a Unicorn Engine instance. Once this is performed emulation can be started. """ @@ -26,6 +26,13 @@ from unicorn.arm64_const import * from unicorn.x86_const import * from unicorn.mips_const import * +# If Capstone libraries are availible (only check once) +try: + from capstone import * + CAPSTONE_EXISTS = 1 +except: + CAPSTONE_EXISTS = 0 + # Name of the index file INDEX_FILE_NAME = "_index.json" @@ -86,7 +93,7 @@ class UnicornSimpleHeap(object): total_chunk_size = UNICORN_PAGE_SIZE + ALIGN_PAGE_UP(size) + UNICORN_PAGE_SIZE # Gross but efficient way to find space for the chunk: chunk = None - for addr in xrange(self.HEAP_MIN_ADDR, self.HEAP_MAX_ADDR, UNICORN_PAGE_SIZE): + for addr in range(self.HEAP_MIN_ADDR, self.HEAP_MAX_ADDR, UNICORN_PAGE_SIZE): try: self._uc.mem_map(addr, total_chunk_size, UC_PROT_READ | UC_PROT_WRITE) chunk = self.HeapChunk(addr, total_chunk_size, size) @@ -97,7 +104,7 @@ class UnicornSimpleHeap(object): continue # Something went very wrong if chunk == None: - return 0 + return 0 self._chunks.append(chunk) return chunk.data_addr @@ -112,8 +119,8 @@ class UnicornSimpleHeap(object): old_chunk = None for chunk in self._chunks: if chunk.data_addr == ptr: - old_chunk = chunk - new_chunk_addr = self.malloc(new_size) + old_chunk = chunk + new_chunk_addr = self.malloc(new_size) if old_chunk != None: self._uc.mem_write(new_chunk_addr, str(self._uc.mem_read(old_chunk.data_addr, old_chunk.data_size))) self.free(old_chunk.data_addr) @@ -184,39 +191,27 @@ class AflUnicornEngine(Uc): # Load the registers regs = context['regs'] reg_map = self.__get_register_map(self._arch_str) - for register, value in regs.iteritems(): - if debug_print: - print("Reg {0} = {1}".format(register, value)) - if not reg_map.has_key(register.lower()): - if debug_print: - print("Skipping Reg: {}".format(register)) - else: - reg_write_retry = True - try: - self.reg_write(reg_map[register.lower()], value) - reg_write_retry = False - except Exception as e: - if debug_print: - print("ERROR writing register: {}, value: {} -- {}".format(register, value, repr(e))) + self.__load_registers(regs, reg_map, debug_print) + # If we have extra FLOATING POINT regs, load them in! + if 'regs_extended' in context: + if context['regs_extended']: + regs_extended = context['regs_extended'] + reg_map = self.__get_registers_extended(self._arch_str) + self.__load_registers(regs_extended, reg_map, debug_print) + + # For ARM, sometimes the stack pointer is erased ??? (I think I fixed this (issue with ordering of dumper.py, I'll keep the write anyways) + if self.__get_arch_and_mode(self.get_arch_str())[0] == UC_ARCH_ARM: + self.reg_write(UC_ARM_REG_SP, regs['sp']) - if reg_write_retry: - if debug_print: - print("Trying to parse value ({}) as hex string".format(value)) - try: - self.reg_write(reg_map[register.lower()], int(value, 16)) - except Exception as e: - if debug_print: - print("ERROR writing hex string register: {}, value: {} -- {}".format(register, value, repr(e))) - # Setup the memory map and load memory content self.__map_segments(context['segments'], context_directory, debug_print) - + if enable_trace: self.hook_add(UC_HOOK_BLOCK, self.__trace_block) self.hook_add(UC_HOOK_CODE, self.__trace_instruction) self.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.__trace_mem_access) self.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, self.__trace_mem_invalid_access) - + if debug_print: print("Done loading context.") @@ -228,7 +223,7 @@ class AflUnicornEngine(Uc): def get_arch_str(self): return self._arch_str - + def force_crash(self, uc_error): """ This function should be called to indicate to AFL that a crash occurred during emulation. You can pass the exception received from Uc.emu_start @@ -253,21 +248,76 @@ class AflUnicornEngine(Uc): for reg in sorted(self.__get_register_map(self._arch_str).items(), key=lambda reg: reg[0]): print(">>> {0:>4}: 0x{1:016x}".format(reg[0], self.reg_read(reg[1]))) + def dump_regs_extended(self): + """ Dumps the contents of all the registers to STDOUT """ + try: + for reg in sorted(self.__get_registers_extended(self._arch_str).items(), key=lambda reg: reg[0]): + print(">>> {0:>4}: 0x{1:016x}".format(reg[0], self.reg_read(reg[1]))) + except Exception as e: + print("ERROR: Are extended registers loaded?") + # TODO: Make this dynamically get the stack pointer register and pointer width for the current architecture """ def dump_stack(self, window=10): + arch = self.get_arch() + mode = self.get_mode() + # Get stack pointers and bit sizes for given architecture + if arch == UC_ARCH_X86 and mode == UC_MODE_64: + stack_ptr_addr = self.reg_read(UC_X86_REG_RSP) + bit_size = 8 + elif arch == UC_ARCH_X86 and mode == UC_MODE_32: + stack_ptr_addr = self.reg_read(UC_X86_REG_ESP) + bit_size = 4 + elif arch == UC_ARCH_ARM64: + stack_ptr_addr = self.reg_read(UC_ARM64_REG_SP) + bit_size = 8 + elif arch == UC_ARCH_ARM: + stack_ptr_addr = self.reg_read(UC_ARM_REG_SP) + bit_size = 4 + elif arch == UC_ARCH_ARM and mode == UC_MODE_THUMB: + stack_ptr_addr = self.reg_read(UC_ARM_REG_SP) + bit_size = 4 + elif arch == UC_ARCH_MIPS: + stack_ptr_addr = self.reg_read(UC_MIPS_REG_SP) + bit_size = 4 + print("") print(">>> Stack:") stack_ptr_addr = self.reg_read(UC_X86_REG_RSP) for i in xrange(-window, window + 1): addr = stack_ptr_addr + (i*8) print("{0}0x{1:016x}: 0x{2:016x}".format( \ - 'SP->' if i == 0 else ' ', addr, \ + 'SP->' if i == 0 else ' ', addr, \ struct.unpack('>> Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value)) else: - print(" >>> Read: addr=0x{0:016x} size={1}".format(address, size)) + print(" >>> Read: addr=0x{0:016x} size={1}".format(address, size)) def __trace_mem_invalid_access(self, uc, access, address, size, value, user_data): if access == UC_MEM_WRITE_UNMAPPED: print(" >>> INVALID Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value)) else: - print(" >>> INVALID Read: addr=0x{0:016x} size={1}".format(address, size)) + print(" >>> INVALID Read: addr=0x{0:016x} size={1}".format(address, size)) + def bit_size_arch(self): + arch = self.get_arch() + mode = self.get_mode() + # Get bit sizes for given architecture + if arch == UC_ARCH_X86 and mode == UC_MODE_64: + bit_size = 8 + elif arch == UC_ARCH_X86 and mode == UC_MODE_32: + bit_size = 4 + elif arch == UC_ARCH_ARM64: + bit_size = 8 + elif arch == UC_ARCH_ARM: + bit_size = 4 + elif arch == UC_ARCH_MIPS: + bit_size = 4 + return bit_size