Skip to content

Commit

Permalink
Merge pull request angr#2388 from Kyle-Kyle/wip/tracing
Browse files Browse the repository at this point in the history
Wip/tracing
  • Loading branch information
rhelmot authored Nov 18, 2020
2 parents 93c1959 + f73aede commit ffb377e
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 18 deletions.
14 changes: 12 additions & 2 deletions angr/engines/vex/claripy/ccall.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import claripy
import logging
from archinfo.arch_arm import is_arm_arch
from angr.state_plugins.sim_action_object import _raw_ast, SimActionObject

l = logging.getLogger(name=__name__)
#l.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -49,6 +50,11 @@ def op_concretize(op):
raise SimError("Hit a symbolic conditional operation. Something has gone wildly wrong.")
return op.args[0]

def strip_simaction(val):
if type(val) == SimActionObject:
return _raw_ast(val)
return val

class CCallMultivaluedException(Exception):
pass

Expand Down Expand Up @@ -1875,6 +1881,10 @@ def arm64g_calculate_condition(state, cond_n_op, cc_dep1, cc_dep2, cc_dep3):
#

def _get_flags(state) -> claripy.ast.bv.BV:
cc_op = strip_simaction(state.regs.cc_op)
cc_dep1 = strip_simaction(state.regs.cc_dep1)
cc_dep2 = strip_simaction(state.regs.cc_dep2)
cc_ndep = strip_simaction(state.regs.cc_ndep)
if state.arch.name == 'X86':
func = x86g_calculate_eflags_all
elif state.arch.name == 'AMD64':
Expand All @@ -1887,10 +1897,10 @@ def _get_flags(state) -> claripy.ast.bv.BV:
l.warning("No such thing as a flags register for arch %s", state.arch.name)
return None
try:
return func(state, state.regs.cc_op, state.regs.cc_dep1, state.regs.cc_dep2, state.regs.cc_ndep)
return func(state, cc_op, cc_dep1, cc_dep2, cc_ndep)
except CCallMultivaluedException as e:
cases = e.args[0]
return claripy.ite_cases([(case, func(state, value, state.regs.cc_dep1, state.regs.cc_dep2, state.regs.cc_ndep)) for case, value in cases], 0)
return claripy.ite_cases([(case, func(state, value, cc_dep1, cc_dep2, cc_ndep)) for case, value in cases], 0)

def _concat_flags(nbits, flags_vec):
"""
Expand Down
42 changes: 28 additions & 14 deletions angr/exploration_techniques/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def _inline_call(self, state, procedure, *arguments, **kwargs):

def run(self, state):

from .. import SIM_PROCEDURES
from .. import SIM_PROCEDURES # pylint: disable=import-outside-toplevel

dst = state.regs.edi if state.arch.name == "X86" else state.regs.rdi

Expand Down Expand Up @@ -107,7 +107,7 @@ def run(self, state):
state.regs.rcx = 0

else:
import ipdb; ipdb.set_trace()
raise NotImplementedError("Unsupported mnemonic %s" % self.mnemonic)


class Tracer(ExplorationTechnique):
Expand All @@ -132,6 +132,8 @@ class Tracer(ExplorationTechnique):
in order to set the predecessors list correctly. If you turn this
on you may want to enable the LAZY_SOLVES option.
:param mode: Tracing mode.
:param aslr: Whether there are aslr slides. if not, tracer uses trace address
as state address.
:ivar predecessors: A list of states in the history before the final state.
"""
Expand All @@ -142,13 +144,15 @@ def __init__(self,
keep_predecessors=1,
crash_addr=None,
copy_states=False,
mode=TracingMode.Strict):
mode=TracingMode.Strict,
aslr=True):
super(Tracer, self).__init__()
self._trace = trace
self._resiliency = resiliency
self._crash_addr = crash_addr
self._copy_states = copy_states
self._mode = mode
self._aslr = aslr

self._aslr_slides = {}
self._current_slide = None
Expand Down Expand Up @@ -177,6 +181,7 @@ def setup(self, simgr):

# calc ASLR slide for main binary and find the entry point in one fell swoop
# ...via heuristics
idx = None
for idx, addr in enumerate(self._trace):
if self.project.loader.main_object.pic:
if ((addr - self.project.entry) & 0xfff) == 0 and (idx == 0 or abs(self._trace[idx-1] - addr) > 0x100000):
Expand All @@ -187,9 +192,15 @@ def setup(self, simgr):
else:
raise AngrTracerError("Could not identify program entry point in trace!")

# pylint: disable=undefined-loop-variable
# pylint doesn't know jack shit
self._current_slide = self._aslr_slides[self.project.loader.main_object] = self._trace[idx] - self.project.entry
# handle aslr slides
if self._aslr:
# pylint: disable=undefined-loop-variable
# pylint doesn't know jack shit
self._current_slide = self._aslr_slides[self.project.loader.main_object] = self._trace[idx] - self.project.entry
else:
for obj in self.project.loader.all_elf_objects:
self._aslr_slides[obj] = 0
self._current_slide = 0

# step to entry point
while self._trace and self._trace[idx] != simgr.one_active.addr + self._current_slide:
Expand Down Expand Up @@ -252,13 +263,15 @@ def step_state(self, simgr, state, **kwargs):
# optimization:
# look forward, is it a rep stos/movs instruction?
# if so, we add a temporary hook to speed up constraint solving
block = self.project.factory.block(state.addr)
if len(block.capstone.insns) == 1 and (
block.capstone.insns[0].mnemonic.startswith("rep m") or
block.capstone.insns[0].mnemonic.startswith("rep s")
) and not self.project.is_hooked(state.addr):
insn = block.capstone.insns[0]
self.project.hook(state.addr, RepHook(insn.mnemonic.split(" ")[1]).run, length=insn.size)
if not self.project.is_hooked(state.addr):
block = self.project.factory.block(state.addr)

if len(block.capstone.insns) == 1 and (
block.capstone.insns[0].mnemonic.startswith("rep m") or
block.capstone.insns[0].mnemonic.startswith("rep s")
):
insn = block.capstone.insns[0]
self.project.hook(state.addr, RepHook(insn.mnemonic.split(" ")[1]).run, length=insn.size)

# perform the step. ask qemu to stop at the termination point.
stops = set(kwargs.pop('extra_stop_points', ())) | {self._trace[-1]}
Expand Down Expand Up @@ -429,7 +442,8 @@ def _update_state_tracking(self, state: 'angr.SimState'):
raise Exception("Extremely bad news: we're executing an unhooked address in the externs space")
if proc.is_continuation:
orig_addr = self.project.loader.find_symbol(proc.display_name).rebased_addr
orig_trace_addr = orig_addr + self._aslr_slides[self.project.loader.find_object_containing(orig_addr)]
obj = self.project.loader.find_object_containing(orig_addr)
orig_trace_addr = self._translate_state_addr(orig_addr, obj)
if 0 <= self._trace[idx + 1] - orig_trace_addr <= 0x10000:
# this is fine. we do nothing and then next round it'll get handled by the is_hooked(state.history.addr) case
pass
Expand Down
5 changes: 5 additions & 0 deletions angr/procedures/linux_kernel/arm_user_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def run(self, oldval, newval, ptr):
# handle cmpxchg
if self.state.solver.is_true(retval == 0):
self.state.memory.store(ptr, newval)
# set CARRY flag
self.state.regs.flags |= 0x20000000
else:
# zero CARRY flag
self.state.regs.flags &= 0xdfffffff
return retval

class _kuser_memory_barrier(angr.SimProcedure):
Expand Down
29 changes: 28 additions & 1 deletion angr/procedures/linux_kernel/fstat64.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
import angr

class fstat64(angr.SimProcedure):
"""
FIXME: currently the implementation is very hacky, basically build dependent.
To continue this weird implementation you can include `<asm/stat.h>` and then
use offsetof macro to grab offsets
"""

def run(self, fd, stat_buf): # pylint:disable=arguments-differ
stat = self.state.posix.fstat(fd)
# TODO: make arch-neutral
if self.arch.bits == 32:
if self.arch.name.startswith("ARM"):
self._store_arm(stat_buf, stat)
elif self.arch.bits == 32:
self._store_i386(stat_buf, stat)
else:
self._store_amd64(stat_buf, stat)
return 0

def _store_arm(self, stat_buf, stat):
store = lambda offset, val: self.state.memory.store(stat_buf + offset, val, endness='Iend_LE')
store(0x00, stat.st_dev)
store(0x0c, stat.st_ino)
store(0x10, stat.st_mode)
store(0x14, stat.st_nlink)
store(0x18, stat.st_uid)
store(0x1c, stat.st_gid)
store(0x20, stat.st_rdev)
store(0x30, stat.st_size)
store(0x38, stat.st_blksize)
store(0x40, stat.st_blocks)
store(0x48, stat.st_atime)
store(0x4c, stat.st_atimensec)
store(0x50, stat.st_mtime)
store(0x54, stat.st_mtimensec)
store(0x58, stat.st_ctime)
store(0x5c, stat.st_ctimensec)
store(0x60, stat.st_ino) # weird verification st_ino

def _store_i386(self, stat_buf, stat):
store = lambda offset, val: self.state.memory.store(stat_buf + offset, val, endness='Iend_LE')
store(0x00, stat.st_dev)
Expand Down
2 changes: 1 addition & 1 deletion angr/state_plugins/posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def fstat(self, sim_fd): #pylint:disable=unused-argument
self.state.solver.BVV(0, 32), # st_gid
self.state.solver.BVV(0, 64), # st_rdev
size, # st_size
self.state.solver.BVV(0, 64), # st_blksize
self.state.solver.BVV(0x400, 64), # st_blksize
self.state.solver.BVV(0, 64), # st_blocks
self.state.solver.BVV(0, 64), # st_atime
self.state.solver.BVV(0, 64), # st_atimensec
Expand Down

0 comments on commit ffb377e

Please sign in to comment.