Skip to content

Commit

Permalink
do not allow gadgets to change segment register states
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Kyle committed Apr 16, 2024
1 parent 87d2b37 commit f8c5ce9
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
1 change: 1 addition & 0 deletions angrop/arch.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def __init__(self, project, kernel_mode=False):
self.max_block_size = 20 # X86 and AMD64 have alignment of 1, 8 bytes is certainly not good enough
self.syscall_insts = {b"\xcd\x80"} # int 0x80
self.ret_insts = {b"\xc2", b"\xc3", b"\xca", b"\xcb"}
self.segment_regs = {"cs", "ds", "es", "fs", "gs", "ss"}

def block_make_sense(self, block):
capstr = str(block.capstone).lower()
Expand Down
22 changes: 20 additions & 2 deletions angrop/gadget_finder/gadget_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import claripy

from .. import rop_utils
from ..arch import get_arch
from ..arch import get_arch, X86
from ..rop_gadget import RopGadget, RopMemAccess, RopRegMove, PivotGadget, SyscallGadget
from ..errors import RopException, RegNotFoundException

Expand Down Expand Up @@ -35,7 +35,12 @@ def __init__(self, project, fast_mode, kernel_mode=False, arch=None, stack_gsize
# the copied state
self._stack_bsize = stack_gsize * self.project.arch.bytes # number of controllable bytes on stack
sym_reg_set = self.arch.reg_set.union({self.arch.base_pointer})
self._state = rop_utils.make_symbolic_state(self.project, sym_reg_set, stack_gsize=stack_gsize)
if isinstance(self.arch, X86):
extra_reg_set = self.arch.segment_regs
else:
extra_reg_set = None
self._state = rop_utils.make_symbolic_state(self.project, sym_reg_set,
extra_reg_set=extra_reg_set, stack_gsize=stack_gsize)
self._concrete_sp = self._state.solver.eval(self._state.regs.sp)

@rop_utils.timeout(3)
Expand All @@ -59,6 +64,9 @@ def analyze_gadget(self, addr):

init_state, final_state = self._reach_unconstrained_or_syscall(addr)

if self._change_arch_state(init_state, final_state):
return None

ctrl_type = self._check_for_control_type(init_state, final_state)
if not ctrl_type:
# for example, jump outside of the controllable region
Expand Down Expand Up @@ -93,6 +101,16 @@ def analyze_gadget(self, addr):
l.debug("... Appending gadget!")
return gadget

def _change_arch_state(self, init_state, final_state):
if isinstance(self.arch, X86):
for reg in self.arch.segment_regs:
init_reg= init_state.registers.load(reg)
final_reg = final_state.registers.load(reg)
# check whether there is any possibility that they can be different
if final_state.solver.satisfiable([init_reg != final_reg]):
return True
return False

def _block_make_sense(self, addr):
"""
Checks if a block at addr makes sense to analyze for rop gadgets
Expand Down
5 changes: 3 additions & 2 deletions angrop/rop_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,16 @@ def make_initial_state(project, stack_gsize):
return initial_state


def make_symbolic_state(project, reg_set, stack_gsize=80):
def make_symbolic_state(project, reg_set, extra_reg_set=None, stack_gsize=80):
"""
converts an input state into a state with symbolic registers
:return: the symbolic state
"""
if extra_reg_set is None: extra_reg_set = set()
input_state = make_initial_state(project, stack_gsize)
symbolic_state = input_state.copy()
# overwrite all registers
for reg in reg_set:
for reg in reg_set.union(extra_reg_set) :
symbolic_state.registers.store(reg, symbolic_state.solver.BVS("sreg_" + reg + "-", project.arch.bits))
# restore sp
symbolic_state.regs.sp = input_state.regs.sp
Expand Down
39 changes: 39 additions & 0 deletions tests/test_find_gadgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,45 @@ def test_syscall_gadget():
rop = proj.analyses.ROP()
assert all(gadget_exists(rop, x) for x in [0x0806f860, 0x0806f85e, 0x080939e3, 0x0806f2f1])

BIN_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "binaries")
CACHE_DIR = os.path.join(BIN_DIR, 'tests_data', 'angrop_gadgets_cache')
def test_shift_gadget():
"""
438a91 pop es
438a92 add esp, 0x9c
438a98 ret
454e75 push cs
454e76 add esp, 0x5c
454e79 pop ebx
454e7a pop esi
454e7b pop edi
454e7c pop ebp
454e7d ret
5622d5 push ss
5622d6 add esp, 0x74
5622d9 pop ebx
5622da pop edi
5622db ret
516fb2 clc
516fb3 pop ds
516fb4 add esp, 0x8
516fb7 pop ebx
516fb8 ret
490058 push ds
490059 add esp, 0x2c
49005c ret
"""
cache_path = os.path.join(CACHE_DIR, "i386_glibc_2.35")
proj = angr.Project(os.path.join(tests_dir, "i386", "i386_glibc_2.35"), auto_load_libs=False)
rop = proj.analyses.ROP()

assert all(not gadget_exists(rop, x) for x in [0x438a91, 0x516fb2])
assert all(gadget_exists(rop, x) for x in [0x454e75, 0x5622d5, 0x490058])

def run_all():
functions = globals()
all_functions = {x:y for x, y in functions.items() if x.startswith('test_')}
Expand Down

0 comments on commit f8c5ce9

Please sign in to comment.