# pylint:disable=no-self-use
import pyvex
from .errors import SimSlicerError
[docs]class SimLightState:
"""
Represents a program state. Only used in SimSlicer.
"""
__slots__ = (
"temps",
"regs",
"stack_offsets",
"options",
)
[docs] def __init__(self, temps=None, regs=None, stack_offsets=None, options=None):
self.temps = temps if temps is not None else set()
self.regs = regs if regs is not None else set()
self.stack_offsets = stack_offsets if stack_offsets is not None else set()
self.options = {} if options is None else options
[docs]class SimSlicer:
"""
A super lightweight intra-IRSB slicing class.
"""
[docs] def __init__(
self,
arch,
statements,
target_tmps=None,
target_regs=None,
target_stack_offsets=None,
inslice_callback=None,
inslice_callback_infodict=None,
include_imarks: bool = True,
):
self._arch = arch
self._statements = statements
self._target_tmps = target_tmps if target_tmps is not None else set()
self._target_regs = target_regs if target_regs is not None else set()
self._target_stack_offsets = target_stack_offsets if target_stack_offsets is not None else set()
self._inslice_callback = inslice_callback
self._include_imarks = include_imarks
# It could be accessed publicly
self.inslice_callback_infodict = inslice_callback_infodict
self.stmts = []
self.stmt_indices = []
self.final_regs = set()
self.final_stack_offsets = set()
if not self._target_tmps and not self._target_regs and not self._target_stack_offsets:
raise SimSlicerError(
'You must specify at least one of the following: "'
"target temps, target registers, and/or target stack offsets."
)
# convert target registers to base registers
target_base_regs = set()
for target_reg in self._target_regs:
base_reg = self._arch.get_base_register(target_reg)
if base_reg is None:
target_base_regs.add(target_reg)
else:
target_base_regs.add(base_reg[0])
self._target_regs = target_base_regs
self._aliases = {}
self._alias_analysis()
self._slice()
def _alias_analysis(self, mock_sp=True, mock_bp=True):
"""
Perform a forward execution and perform alias analysis. Note that this analysis is fast, light-weight, and by no
means complete. For instance, most arithmetic operations are not supported.
- Depending on user settings, stack pointer and stack base pointer will be mocked and propagated to individual
tmps.
:param bool mock_sp: propagate stack pointer or not
:param bool mock_bp: propagate stack base pointer or not
:return: None
"""
state = SimLightState(
regs={
self._arch.sp_offset: self._arch.initial_sp,
# TODO: take care of the relation between sp and bp
self._arch.bp_offset: self._arch.initial_sp + 0x2000,
},
temps={},
options={
"mock_sp": mock_sp,
"mock_bp": mock_bp,
},
)
for stmt in self._statements:
self._forward_handler_stmt(stmt, state)
#
# Forward execution IRStmt handlers
#
def _forward_handler_stmt(self, stmt, state):
"""
:param stmt:
:param SimLightState state:
:return:
"""
funcname = "_forward_handler_stmt_%s" % type(stmt).__name__
if hasattr(self, funcname):
getattr(self, funcname)(stmt, state)
def _forward_handler_stmt_WrTmp(self, stmt, state):
tmp = stmt.tmp
val = self._forward_handler_expr(stmt.data, state)
if val is not None:
state.temps[tmp] = val
self._aliases[tmp] = val
#
# Forward execution IRExpr handlers
#
def _forward_handler_expr(self, expr, state):
"""
:param stmt:
:param SimLightState state:
:return:
"""
funcname = "_forward_handler_expr_%s" % type(expr).__name__
if hasattr(self, funcname):
return getattr(self, funcname)(expr, state)
return None
def _forward_handler_expr_Get(self, expr, state):
reg = expr.offset
if state.options["mock_sp"] and reg == self._arch.sp_offset:
return state.regs[reg]
elif state.options["mock_bp"] and reg == self._arch.bp_offset:
return state.regs[reg]
return None
def _forward_handler_expr_RdTmp(self, expr, state):
tmp = expr.tmp
if tmp in state.temps:
return state.temps[tmp]
return None
def _forward_handler_expr_Const(self, expr, state): # pylint:disable=unused-argument
return expr.con.value
def _forward_handler_expr_Binop(self, expr, state):
funcname = "_forward_handler_expr_binop_%s" % expr.op.strip("Iop_")
if hasattr(self, funcname):
op0_val = self._forward_handler_expr(expr.args[0], state)
op1_val = self._forward_handler_expr(expr.args[1], state)
if op0_val is not None and op1_val is not None:
return getattr(self, funcname)(op0_val, op1_val, state)
return None
def _forward_handler_expr_binop_Add64(self, op0, op1, state): # pylint:disable=unused-argument
return (op0 + op1) & (2**64 - 1)
def _forward_handler_expr_binop_Add32(self, op0, op1, state): # pylint:disable=unused-argument
return (op0 + op1) & (2**32 - 1)
#
# Backward slicing
#
def _slice(self):
"""
Slice it!
"""
regs = set(self._target_regs)
tmps = set(self._target_tmps)
stack_offsets = set(self._target_stack_offsets)
state = SimLightState(regs=regs, temps=tmps, stack_offsets=stack_offsets)
for stmt_idx, stmt in reversed(list(enumerate(self._statements))):
if self._backward_handler_stmt(stmt, state):
self.stmts.insert(0, stmt)
self.stmt_indices.insert(0, stmt_idx)
if self._inslice_callback:
self._inslice_callback(stmt_idx, stmt, self.inslice_callback_infodict)
if not regs and not tmps and not stack_offsets:
break
self.final_regs = state.regs
self.final_stack_offsets = state.stack_offsets
#
# Backward slice IRStmt handlers
#
def _backward_handler_stmt(self, stmt, state):
funcname = "_backward_handler_stmt_%s" % type(stmt).__name__
in_slice = False
if hasattr(self, funcname):
in_slice = getattr(self, funcname)(stmt, state)
return in_slice
def _backward_handler_stmt_IMark(self, stmt, state) -> bool: # pylint:disable=unused-argument
# include all IMark statements
return self._include_imarks
def _backward_handler_stmt_WrTmp(self, stmt, state):
tmp = stmt.tmp
if tmp not in state.temps:
return False
state.temps.remove(tmp)
self._backward_handler_expr(stmt.data, state)
return True
def _backward_handler_stmt_Put(self, stmt: pyvex.IRStmt.Put, state):
reg = stmt.offset
# convert it to its base register
base_reg = self._arch.get_base_register(reg)
if base_reg is not None:
reg = base_reg[0]
if reg in state.regs:
state.regs.remove(reg)
self._backward_handler_expr(stmt.data, state)
return True
else:
return False
def _backward_handler_stmt_Store(self, stmt, state):
addr = stmt.addr
if type(addr) is pyvex.IRExpr.RdTmp:
tmp = addr.tmp
if tmp in self._aliases:
# We know its value
concrete_addr = self._aliases[tmp]
if concrete_addr in state.stack_offsets:
# It's written at this statement
state.stack_offsets.remove(concrete_addr)
self._backward_handler_expr(addr, state)
self._backward_handler_expr(stmt.data, state)
return True
return False
def _backward_handler_stmt_LoadG(self, expr, state):
if expr.dst not in state.temps:
return False
state.temps.remove(expr.dst)
self._backward_handler_expr(expr.guard, state)
self._backward_handler_expr(expr.addr, state)
self._backward_handler_expr(expr.alt, state)
return True
#
# Backward slice IRExpr handlers
#
def _backward_handler_expr(self, expr, state):
funcname = "_backward_handler_expr_%s" % type(expr).__name__
if hasattr(self, funcname):
getattr(self, funcname)(expr, state)
def _backward_handler_expr_RdTmp(self, expr, state):
tmp = expr.tmp
state.temps.add(tmp)
def _backward_handler_expr_Get(self, expr, state):
reg = expr.offset
# convert it to its base register
base_reg = self._arch.get_base_register(reg)
if base_reg is not None:
reg = base_reg[0]
state.regs.add(reg)
def _backward_handler_expr_Load(self, expr, state):
addr = expr.addr
if type(addr) is pyvex.IRExpr.RdTmp:
self._backward_handler_expr(addr, state)
# Do we know the concrete value of this tmp?
tmp = addr.tmp
if tmp in self._aliases:
# awesome!
state.stack_offsets.add(self._aliases[tmp])
def _backward_handler_expr_Unop(self, expr, state):
arg = expr.args[0]
if type(arg) is pyvex.IRExpr.RdTmp:
self._backward_handler_expr(arg, state)
def _backward_handler_expr_CCall(self, expr, state):
for arg in expr.args:
if type(arg) is pyvex.IRExpr.RdTmp:
self._backward_handler_expr(arg, state)
def _backward_handler_expr_Binop(self, expr, state):
for arg in expr.args:
if type(arg) is pyvex.IRExpr.RdTmp:
self._backward_handler_expr(arg, state)
def _backward_handler_expr_ITE(self, expr, state):
self._backward_handler_expr(expr.cond, state)
self._backward_handler_expr(expr.iftrue, state)
self._backward_handler_expr(expr.iffalse, state)