aboutsummaryrefslogtreecommitdiff
path: root/unicorn_mode
diff options
context:
space:
mode:
authorAlexander Shvedov <60114847+a-shvedov@users.noreply.github.com>2024-05-30 10:43:01 +0300
committerGitHub <noreply@github.com>2024-05-30 10:43:01 +0300
commitf8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13 (patch)
tree7aec2a095a30ed609ce96f85ec3c4e0a8b8eb74c /unicorn_mode
parent629edb1e78d791894ce9ee6d53259f95fe1a29af (diff)
parente7d871c8bf64962a658e447b90a1a3b43aaddc28 (diff)
downloadafl++-f8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13.tar.gz
Merge branch 'AFLplusplus:stable' into stable
Diffstat (limited to 'unicorn_mode')
-rw-r--r--unicorn_mode/UNICORNAFL_VERSION2
-rwxr-xr-xunicorn_mode/build_unicorn_support.sh8
-rw-r--r--unicorn_mode/helper_scripts/unicorn_dumper_gdb.py10
-rw-r--r--unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py30
-rw-r--r--unicorn_mode/helper_scripts/unicorn_loader.py41
m---------unicorn_mode/unicornafl0
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