diff options
Diffstat (limited to 'unicorn_mode')
-rw-r--r-- | unicorn_mode/UNICORNAFL_VERSION | 2 | ||||
-rwxr-xr-x | unicorn_mode/build_unicorn_support.sh | 8 | ||||
-rw-r--r-- | unicorn_mode/helper_scripts/unicorn_dumper_gdb.py | 10 | ||||
-rw-r--r-- | unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py | 30 | ||||
-rw-r--r-- | unicorn_mode/helper_scripts/unicorn_loader.py | 41 | ||||
m--------- | unicorn_mode/unicornafl | 0 |
6 files changed, 63 insertions, 28 deletions
diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION index 1c8e571f..da17452d 100644 --- a/unicorn_mode/UNICORNAFL_VERSION +++ b/unicorn_mode/UNICORNAFL_VERSION @@ -1 +1 @@ -f2cede37 +764b66b2 diff --git a/unicorn_mode/build_unicorn_support.sh b/unicorn_mode/build_unicorn_support.sh index d3d16ad5..097a2dc9 100755 --- a/unicorn_mode/build_unicorn_support.sh +++ b/unicorn_mode/build_unicorn_support.sh @@ -14,7 +14,7 @@ # <andreafioraldi@gmail.com> # # Copyright 2017 Battelle Memorial Institute. All rights reserved. -# Copyright 2019-2023 AFLplusplus Project. All rights reserved. +# Copyright 2019-2024 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -198,10 +198,12 @@ $MAKECMD -j1 || exit 1 echo "[+] Build process successful!" echo "[*] Installing Unicorn python bindings..." +XOPT= +$PYTHONBIN -m pip install --help 2>/dev/null | grep -q break-system-packages && XOPT=--break-system-packages cd unicorn/bindings/python || exit 1 if [ -z "$VIRTUAL_ENV" ]; then echo "[*] Info: Installing python unicornafl using --user" - THREADS=$CORES $PYTHONBIN -m pip install --user --force .|| exit 1 + THREADS=$CORES $PYTHONBIN -m pip install --user $XOPT --force .|| exit 1 else echo "[*] Info: Installing python unicornafl to virtualenv: $VIRTUAL_ENV" THREADS=$CORES $PYTHONBIN -m pip install --force .|| exit 1 @@ -211,7 +213,7 @@ echo "[*] Installing Unicornafl python bindings..." cd bindings/python || exit 1 if [ -z "$VIRTUAL_ENV" ]; then echo "[*] Info: Installing python unicornafl using --user" - THREADS=$CORES $PYTHONBIN -m pip install --user --force .|| exit 1 + THREADS=$CORES $PYTHONBIN -m pip install --user $XOPT --force .|| exit 1 else echo "[*] Info: Installing python unicornafl to virtualenv: $VIRTUAL_ENV" THREADS=$CORES $PYTHONBIN -m pip install --force .|| exit 1 diff --git a/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py b/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py index 1ac4c9f3..a202ac0e 100644 --- a/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py +++ b/unicorn_mode/helper_scripts/unicorn_dumper_gdb.py @@ -89,8 +89,8 @@ def dump_arch_info(): def dump_regs(): reg_state = {} - for reg in current_arch.all_registers: - reg_val = get_register(reg) + for reg in gef.arch.registers: + reg_val = gef.arch.register(reg) reg_state[reg.strip().strip("$")] = reg_val return reg_state @@ -101,7 +101,9 @@ def dump_process_memory(output_dir): final_segment_list = [] # GEF: - vmmap = get_process_maps() + vmmap = gef.memory.maps + memory = GefMemoryManager() + if not vmmap: print("No address mapping information found") return final_segment_list @@ -126,7 +128,7 @@ def dump_process_memory(output_dir): if entry.is_readable() and not "(deleted)" in entry.path: try: # Compress and dump the content to a file - seg_content = read_memory(entry.page_start, entry.size) + seg_content = memory.read(entry.page_start, entry.size) if seg_content == None: print( "Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format( diff --git a/unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py b/unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py index eccbc8bf..76eaf54f 100644 --- a/unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py +++ b/unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py @@ -40,10 +40,10 @@ import gdb pwndbg_loaded = False try: - import pwndbg.arch - import pwndbg.regs - import pwndbg.vmmap - import pwndbg.memory + import pwndbg.gdblib.arch + import pwndbg.gdblib.regs + import pwndbg.gdblib.vmmap + import pwndbg.gdblib.memory pwndbg_loaded = True @@ -64,7 +64,7 @@ INDEX_FILE_NAME = "_index.json" def map_arch(): - arch = pwndbg.arch.current # from PWNDBG + arch = pwndbg.gdblib.arch.current # from PWNDBG if "x86_64" in arch or "x86-64" in arch: return "x64" elif "x86" in arch or "i386" in arch: @@ -74,9 +74,9 @@ def map_arch(): elif "aarch64_be" in arch: return "arm64be" elif "arm" in arch: - cpsr = pwndbg.regs["cpsr"] + cpsr = pwndbg.gdblib.regs["cpsr"] # check endianess - if pwndbg.arch.endian == "big": + if pwndbg.gdblib.arch.endian == "big": # check for THUMB mode if cpsr & (1 << 5): return "armbethumb" @@ -89,7 +89,7 @@ def map_arch(): else: return "armle" elif "mips" in arch: - if pwndbg.arch.endian == "little": + if pwndbg.gdblib.arch.endian == "little": return "mipsel" else: return "mips" @@ -109,14 +109,16 @@ def dump_arch_info(): def dump_regs(): reg_state = {} - for reg in pwndbg.regs.all: - reg_val = pwndbg.regs[reg] + for reg in pwndbg.gdblib.regs.all: + reg_val = pwndbg.gdblib.regs[reg] + if reg_val is None: + continue # 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 + reg_state[reg.strip().strip("$")] = int(reg_val) return reg_state @@ -125,7 +127,7 @@ def dump_process_memory(output_dir): final_segment_list = [] # PWNDBG: - vmmap = pwndbg.vmmap.get() + vmmap = pwndbg.gdblib.vmmap.get() # Pointer to end of last dumped memory segment segment_last_addr = 0x0 @@ -165,7 +167,7 @@ def dump_process_memory(output_dir): if entry.read and not "(deleted)" in entry.objfile: try: # Compress and dump the content to a file - seg_content = pwndbg.memory.read(start, end - start) + seg_content = pwndbg.gdblib.memory.read(start, end - start) if seg_content == None: print( "Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format( @@ -181,7 +183,7 @@ def dump_process_memory(output_dir): repr(seg_info["permissions"]), ) ) - compressed_seg_content = zlib.compress(str(seg_content)) + compressed_seg_content = zlib.compress(bytes(seg_content)) md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin" seg_info["content_file"] = md5_sum diff --git a/unicorn_mode/helper_scripts/unicorn_loader.py b/unicorn_mode/helper_scripts/unicorn_loader.py index c48a7572..a83e7000 100644 --- a/unicorn_mode/helper_scripts/unicorn_loader.py +++ b/unicorn_mode/helper_scripts/unicorn_loader.py @@ -21,10 +21,10 @@ import zlib # Unicorn imports from unicornafl import * -from unicornafl.arm_const import * -from unicornafl.arm64_const import * -from unicornafl.x86_const import * -from unicornafl.mips_const import * +from unicorn.arm_const import * +from unicorn.arm64_const import * +from unicorn.x86_const import * +from unicorn.mips_const import * # If Capstone libraries are availible (only check once) try: @@ -87,9 +87,10 @@ class UnicornSimpleHeap(object): _uc = None # Unicorn engine instance to interact with _chunks = [] # List of all known chunks + _chunks_freed = [] # List of all freed chunks _debug_print = False # True to print debug information - def __init__(self, uc, debug_print=False): + def __init__(self, uc, debug_print=Falseļ¼ uaf_check=False): self._uc = uc self._debug_print = debug_print @@ -101,12 +102,23 @@ class UnicornSimpleHeap(object): # - Allocate at least 1 4k page of memory to make Unicorn happy # - Add guard pages at the start and end of the region total_chunk_size = UNICORN_PAGE_SIZE + ALIGN_PAGE_UP(size) + UNICORN_PAGE_SIZE + + if size == 0: + return 0 + # Gross but efficient way to find space for the chunk: chunk = None 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) + + if self.uaf_check: + for chunk_freed in self._chunks_freed: + if chunk_freed.is_buffer_in_chunk(chunk.data_addr, 1): + self._chunks_freed.remove(chunk_freed) + break + if self._debug_print: print( "Allocating 0x{0:x}-byte chunk @ 0x{1:016x}".format( @@ -148,6 +160,9 @@ class UnicornSimpleHeap(object): return new_chunk_addr def free(self, addr): + if addr == 0: + return False + for chunk in self._chunks: if chunk.is_buffer_in_chunk(addr, 1): if self._debug_print: @@ -157,9 +172,14 @@ class UnicornSimpleHeap(object): ) ) self._uc.mem_unmap(chunk.actual_addr, chunk.total_size) + + if self.uaf_check: + self._chunks_freed.append(chunk) + self._chunks.remove(chunk) return True - return False + # Freed an object that doesn't exist. Maybe 'dobule-free' or 'invalid free' vulnerability here. + self._uc.force_crash(UcError(UC_ERR_FETCH_UNMAPPED)) # Implements basic guard-page functionality def __check_mem_access(self, uc, access, address, size, value, user_data): @@ -179,6 +199,15 @@ class UnicornSimpleHeap(object): # Force a memory-based crash uc.force_crash(UcError(UC_ERR_READ_PROT)) + if self.uaf_check: + for chunk in self._chunks_freed: + if address >= chunk.actual_addr and ( + (address + size) <= (chunk.actual_addr + chunk.total_size) + ): + if chunk.is_buffer_in_chunk(address, size): + print("Use-after-free @ 0x{0:016x}".format(address)) + uc.force_crash(UcError(UC_ERR_FETCH_UNMAPPED)) + # --------------------------- # ---- Loading function diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl -Subproject f2cede37a75bbd4a9b9438f0277727b5d462057 +Subproject 764b66b21cd4a8124a5b6c9cc98d1214b203719 |