# pylint:disable=no-self-use,isinstance-second-argument-not-valid-type,unused-argument
from typing import Tuple, Optional, Union, Any
import struct
import re
import logging
import ailment
import pyvex
import claripy
import archinfo
from ...engines.vex.claripy.datalayer import value as claripy_value
from ...engines.vex.claripy.irop import UnsupportedIROpError, SimOperationError, vexop_to_simop
from ...code_location import CodeLocation
from ...utils.constants import DEFAULT_STATEMENT
from ..engine import SimEngine
[docs]class SimEngineLightMixin:
def __init__(self, *args, logger=None, **kwargs):
self.arch: Optional[archinfo.Arch] = None
self.l = logger
super().__init__(*args, **kwargs)
def _is_top(self, expr) -> bool:
"""
Check if a given expression is a TOP value.
:param expr: The given expression.
:return: True if the expression is TOP, False otherwise.
"""
return False
def _top(self, size: int):
"""
Return a TOP value. It will only be called if _is_top() has been implemented.
:param size: The size (in bits) of the TOP value.
:return: A TOP value.
"""
raise NotImplementedError()
[docs] def sp_offset(self, offset: int):
base = claripy.BVS("SpOffset", self.arch.bits, explicit_name=True)
if offset:
base += offset
return base
[docs]class SimEngineLight(
SimEngineLightMixin,
SimEngine,
):
def __init__(self):
logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__)
super().__init__(logger=logger)
# local variables
self.state = None
self.arch: archinfo.Arch = None
self.block = None
self._call_stack = None
self.stmt_idx = None
self.ins_addr = None
self.tmps = None
# for VEX blocks only
self.tyenv = None
[docs] def process(self, state, *args, **kwargs):
# we are using a completely different state. Therefore, we directly call our _process() method before
# SimEngine becomes flexible enough.
self._process(state, None, block=kwargs.pop("block", None), whitelist=kwargs.pop("whitelist", None))
def _process(self, new_state, successors, *args, **kwargs):
raise NotImplementedError()
def _check(self, state, *args, **kwargs):
return True
#
# Helper methods
#
@property
def _context(self) -> Optional[Tuple[int]]:
if self._call_stack is None:
# contextless mode
return None
if not self._call_stack:
# contextful but the callstack is empty
return ()
# Convert to Tuple to make `context` hashable if not None
call_stack_addresses = tuple(self._call_stack)
return call_stack_addresses
def _codeloc(self, block_only=False):
return CodeLocation(
self.block.addr,
None if block_only else self.stmt_idx,
ins_addr=None if block_only else self.ins_addr,
context=self._context,
)
# noinspection PyPep8Naming
[docs]class SimEngineLightVEXMixin(SimEngineLightMixin):
def _process(self, state, successors, *args, block, whitelist=None, **kwargs): # pylint:disable=arguments-differ
# initialize local variables
self.tmps = {}
self.block = block
self.state = state
if state is not None:
self.arch: archinfo.Arch = state.arch
self.tyenv = block.vex.tyenv
self._process_Stmt(whitelist=whitelist)
self.stmt_idx = None
self.ins_addr = None
def _process_Stmt(self, whitelist=None):
if whitelist is not None:
# optimize whitelist lookups
whitelist = set(whitelist)
for stmt_idx, stmt in enumerate(self.block.vex.statements):
if whitelist is not None and stmt_idx not in whitelist:
continue
self.stmt_idx = stmt_idx
if type(stmt) is pyvex.IRStmt.IMark:
# Note that we cannot skip IMarks as they are used later to trigger observation events
# The bug caused by skipping IMarks is reported at https://github.com/angr/angr/pull/1150
self.ins_addr = stmt.addr + stmt.delta
self._handle_Stmt(stmt)
self._process_block_end()
def _process_block_end(self):
# handle calls to another function
# Note that without global information, we cannot handle cases where we *jump* to another function (jumpkind ==
# "Ijk_Boring"). Users are supposed to overwrite this method, detect these cases with the help of global
# information (such as CFG or symbol addresses), and handle them accordingly.
if self.block.vex.jumpkind == "Ijk_Call":
self.stmt_idx = DEFAULT_STATEMENT
handler = "_handle_function"
if hasattr(self, handler):
func_addr = self._expr(self.block.vex.next)
if func_addr is not None:
getattr(self, handler)(func_addr)
else:
self.l.debug("Cannot determine the callee address at %#x.", self.block.addr)
else:
self.l.warning("Function handler not implemented.")
#
# Statement handlers
#
def _handle_Stmt(self, stmt):
handler = "_handle_%s" % type(stmt).__name__
if hasattr(self, handler):
getattr(self, handler)(stmt)
elif type(stmt).__name__ not in ("IMark", "AbiHint"):
self.l.error("Unsupported statement type %s.", type(stmt).__name__)
# synchronize with function _handle_WrTmpData()
def _handle_WrTmp(self, stmt):
data = self._expr(stmt.data)
if data is None:
return
self.tmps[stmt.tmp] = data
# invoked by LoadG
def _handle_WrTmpData(self, tmp, data):
if data is None:
return
self.tmps[tmp] = data
def _handle_Put(self, stmt):
raise NotImplementedError("Please implement the Put handler with your own logic.")
def _handle_Store(self, stmt):
raise NotImplementedError("Please implement the Store handler with your own logic.")
def _handle_StoreG(self, stmt):
raise NotImplementedError("Please implement the StoreG handler with your own logic.")
def _handle_LLSC(self, stmt: pyvex.IRStmt.LLSC):
raise NotImplementedError("Please implement the LLSC handler with your own logic.")
#
# Expression handlers
#
def _expr(self, expr):
handler = "_handle_%s" % type(expr).__name__
if hasattr(self, handler):
return getattr(self, handler)(expr)
else:
self.l.error("Unsupported expression type %s.", type(expr).__name__)
return None
def _handle_Triop(self, expr: pyvex.IRExpr.Triop):
self.l.error("Unsupported Triop %s.", expr.op)
return None
def _handle_RdTmp(self, expr):
tmp = expr.tmp
if tmp in self.tmps:
return self.tmps[tmp]
return None
def _handle_Get(self, expr):
raise NotImplementedError("Please implement the Get handler with your own logic.")
def _handle_Load(self, expr):
raise NotImplementedError("Please implement the Load handler with your own logic.")
def _handle_LoadG(self, stmt):
raise NotImplementedError("Please implement the LoadG handler with your own logic.")
def _handle_Exit(self, stmt):
self._expr(stmt.guard)
self._expr(stmt.dst)
def _handle_ITE(self, expr):
# EDG says: Not sure how generic this is.
cond = self._expr(expr.cond)
if cond is True:
return self._expr(expr.iftrue)
elif cond is False:
return self._expr(expr.iffalse)
else:
return None
def _handle_Unop(self, expr):
handler = None
# All conversions are handled by the Conversion handler
simop = None
try:
simop = vexop_to_simop(expr.op)
except (UnsupportedIROpError, SimOperationError):
pass
if simop is not None and simop.op_attrs.get("conversion", None):
handler = "_handle_Conversion"
# Notice order of "Not" comparisons
elif expr.op == "Iop_Not1":
handler = "_handle_Not1"
elif expr.op.startswith("Iop_Not"):
handler = "_handle_Not"
elif expr.op.startswith("Iop_Clz"):
handler = "_handle_Clz"
elif expr.op.startswith("Iop_Ctz"):
handler = "_handle_Ctz"
if handler is not None and hasattr(self, handler):
return getattr(self, handler)(expr)
else:
self.l.error("Unsupported Unop %s.", expr.op)
return None
def _handle_Binop(self, expr: pyvex.IRExpr.Binop):
handler = None
if expr.op.startswith("Iop_And"):
handler = "_handle_And"
elif expr.op.startswith("Iop_Mod"):
handler = "_handle_Mod"
elif expr.op.startswith("Iop_Or"):
handler = "_handle_Or"
elif expr.op.startswith("Iop_Add"):
handler = "_handle_Add"
elif expr.op.startswith("Iop_HAdd"):
handler = "_handle_HAdd"
elif expr.op.startswith("Iop_Sub"):
handler = "_handle_Sub"
elif expr.op.startswith("Iop_QSub"):
handler = "_handle_QSub"
elif expr.op.startswith("Iop_Mull"):
handler = "_handle_Mull"
elif expr.op.startswith("Iop_Mul"):
handler = "_handle_Mul"
elif expr.op.startswith("Iop_DivMod"):
handler = "_handle_DivMod"
elif expr.op.startswith("Iop_Div"):
handler = "_handle_Div"
elif expr.op.startswith("Iop_Xor"):
handler = "_handle_Xor"
elif expr.op.startswith("Iop_Shl"):
handler = "_handle_Shl"
elif expr.op.startswith("Iop_Shr"):
handler = "_handle_Shr"
elif expr.op.startswith("Iop_Sal"):
# intended use of SHL
handler = "_handle_Shl"
elif expr.op.startswith("Iop_Sar"):
handler = "_handle_Sar"
elif expr.op.startswith("Iop_CmpEQ"):
handler = "_handle_CmpEQ"
elif expr.op.startswith("Iop_CmpNE"):
handler = "_handle_CmpNE"
elif expr.op.startswith("Iop_CmpLT"):
handler = "_handle_CmpLT"
elif expr.op.startswith("Iop_CmpLE"):
handler = "_handle_CmpLE"
elif expr.op.startswith("Iop_CmpGE"):
handler = "_handle_CmpGE"
elif expr.op.startswith("Iop_CmpGT"):
handler = "_handle_CmpGT"
elif expr.op.startswith("Iop_CmpORD"):
handler = "_handle_CmpORD"
elif expr.op.startswith("Iop_CmpF"):
handler = "_handle_CmpF"
elif expr.op == "Iop_32HLto64":
handler = "_handle_32HLto64"
elif expr.op.startswith("Const"):
handler = "_handle_Const"
elif expr.op.startswith("Iop_16HLto32"):
handler = "_handle_16HLto32"
elif expr.op.startswith("Iop_ExpCmpNE64"):
handler = "_handle_ExpCmpNE64"
vector_size, vector_count = None, None
if handler is not None:
# vector information
m = re.match(r"Iop_[^\d]+(\d+)U{0,1}x(\d+)", expr.op)
if m is not None:
vector_size = int(m.group(1))
vector_count = int(m.group(2))
handler += "_v"
if handler is not None and hasattr(self, handler):
if vector_size is not None and vector_count is not None:
return getattr(self, handler)(expr, vector_size, vector_count)
return getattr(self, handler)(expr)
else:
self.l.error("Unsupported Binop %s.", expr.op)
return None
def _handle_CCall(self, expr): # pylint:disable=useless-return
self.l.warning("Unsupported expression type CCall with callee %s.", str(expr.cee))
return None
#
# Unary operation handlers
#
def _handle_U64(self, expr):
return claripy.BVV(expr.value, 64)
def _handle_U32(self, expr):
return claripy.BVV(expr.value, 32)
def _handle_U16(self, expr):
return claripy.BVV(expr.value, 16)
def _handle_U8(self, expr):
return claripy.BVV(expr.value, 8)
def _handle_U1(self, expr):
return claripy.BVV(expr.value, 1)
def _handle_Const(self, expr): # pylint:disable=no-self-use
return claripy_value(expr.con.type, expr.con.value)
def _handle_Conversion(self, expr):
expr_ = self._expr(expr.args[0])
if expr_ is None:
return None
to_size = expr.result_size(self.tyenv)
if self._is_top(expr_):
return self._top(to_size)
if isinstance(expr_, claripy.ast.Base) and expr_.op == "BVV":
if expr_.size() > to_size:
# truncation
return expr_[to_size - 1 : 0]
elif expr_.size() < to_size:
# extension
return claripy.ZeroExt(to_size - expr_.size(), expr_)
else:
return expr_
return self._top(to_size)
#
# Binary operation handlers
#
def _binop_get_args(self, expr) -> Union[Optional[Tuple[Any, Any]], Optional[Any]]:
arg0, arg1 = expr.args
expr_0 = self._expr(arg0)
if expr_0 is None:
return None, None
if self._is_top(expr_0):
return None, self._top(expr_0.size())
expr_1 = self._expr(arg1)
if expr_1 is None:
return None, None
if self._is_top(expr_1):
return None, self._top(expr_0.size()) # always use the size of expr_0
return (expr_0, expr_1), None
def _handle_And(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
return expr_0 & expr_1
def _handle_Or(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
return expr_0 | expr_1
def _handle_Not1(self, expr):
return self._handle_Not(expr)
def _handle_Not(self, expr):
arg0 = expr.args[0]
expr_0 = self._expr(arg0)
if expr_0 is None:
return None
if self._is_top(expr_0):
return self._top(expr_0.size())
try:
return ~expr_0 # pylint:disable=invalid-unary-operand-type
except TypeError as e:
self.l.exception(e)
return None
def _handle_Clz(self, expr):
arg0 = expr.args[0]
expr_0 = self._expr(arg0)
if expr_0 is None:
return None
if self._is_top(expr_0):
return self._top(expr_0.size())
return self._top(expr_0.size())
def _handle_Ctz(self, expr):
arg0 = expr.args[0]
expr_0 = self._expr(arg0)
if expr_0 is None:
return None
if self._is_top(expr_0):
return self._top(expr_0.size())
return self._top(expr_0.size())
def _handle_Add(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
return expr_0 + expr_1
def _handle_Sub(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
return expr_0 - expr_1
def _handle_Mul(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
return expr_0 * expr_1
def _handle_DivMod(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr.result_size(self.tyenv))
signed = "U" in expr.op # Iop_DivModU64to32 vs Iop_DivMod
from_size = expr_0.size()
to_size = expr_1.size()
if signed:
quotient = expr_0.SDiv(claripy.SignExt(from_size - to_size, expr_1))
remainder = expr_1.SMod(claripy.SignExt(from_size - to_size, expr_1))
quotient_size = to_size
remainder_size = to_size
return claripy.Concat(
claripy.Extract(remainder_size - 1, 0, remainder), claripy.Extract(quotient_size - 1, 0, quotient)
)
else:
quotient = expr_0 // claripy.ZeroExt(from_size - to_size, expr_1)
remainder = expr_0 % claripy.ZeroExt(from_size - to_size, expr_1)
quotient_size = to_size
remainder_size = to_size
return claripy.Concat(
claripy.Extract(remainder_size - 1, 0, remainder), claripy.Extract(quotient_size - 1, 0, quotient)
)
def _handle_Div(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
try:
return expr_0 / expr_1
except ZeroDivisionError:
return self._top(expr_0.size())
def _handle_Mod(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
try:
return expr_0 - (expr_1 // expr_1) * expr_1
except ZeroDivisionError:
return self._top(expr_0.size())
def _handle_Xor(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
try:
return expr_0 ^ expr_1
except TypeError as e:
self.l.warning(e)
return None
def _handle_Shl(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
if isinstance(expr_1, claripy.ast.Base) and expr_1.op == "BVV":
# convert it to an int when possible
expr_1 = expr_1.args[0]
else:
# make sure the sizes are the same - VEX does not care about it
if expr_1.size() < expr_0.size():
expr_1 = claripy.ZeroExt(expr_0.size() - expr_1.size(), expr_1)
elif expr_1.size() > expr_0.size():
expr_1 = claripy.Extract(expr_0.size() - 1, 0, expr_1)
return expr_0 << expr_1
def _handle_Shr(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
if isinstance(expr_1, claripy.ast.Base) and expr_1.op == "BVV":
# convert it to an int when possible
expr_1 = expr_1.args[0]
else:
# make sure the sizes are the same - VEX does not care about it
if expr_1.size() < expr_0.size():
expr_1 = claripy.ZeroExt(expr_0.size() - expr_1.size(), expr_1)
elif expr_1.size() > expr_0.size():
expr_1 = claripy.Extract(expr_0.size() - 1, 0, expr_1)
return claripy.LShR(expr_0, expr_1)
def _handle_Sar(self, expr):
# EDG asks: is this right?
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(expr_0.size())
if isinstance(expr_1, claripy.ast.Base) and expr_1.op == "BVV":
# convert it to an int when possible
expr_1 = expr_1.args[0]
else:
# make sure the sizes are the same - VEX does not care about it
if expr_1.size() < expr_0.size():
expr_1 = claripy.ZeroExt(expr_0.size() - expr_1.size(), expr_1)
elif expr_1.size() > expr_0.size():
expr_1 = claripy.Extract(expr_0.size() - 1, 0, expr_1)
return expr_0 >> expr_1
def _handle_CmpEQ(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 == expr_1
def _handle_CmpNE(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 != expr_1
def _handle_CmpLE(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 <= expr_1
def _handle_CmpGE(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 >= expr_1
def _handle_CmpLT(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 < expr_1
def _handle_CmpGT(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
return r
expr_0, expr_1 = args
if self._is_top(expr_0) or self._is_top(expr_1):
return self._top(1)
return expr_0 > expr_1
def _handle_CmpEQ_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_CmpNE_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_CmpLE_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_CmpGE_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_CmpLT_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_CmpGT_v(self, expr, _vector_size, _vector_count):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_MBE(self, _expr: pyvex.IRStmt.MBE):
# Yeah.... no.
return None
def _handle_32HLto64(self, expr):
args, r = self._binop_get_args(expr)
if args is None:
if r is not None:
# the size of r should be 32 but we need to return a 64-bit expression
assert r.size() == 32
r = claripy.ZeroExt(32, r)
return r
return None
def _handle_16HLto32(self, expr):
_, _ = self._binop_get_args(expr)
return self._top(expr.result_size(self.tyenv))
def _handle_ExpCmpNE64(self, expr):
_, _ = self._expr(expr.args[0]), self._expr(expr.args[1])
return self._top(expr.result_size(self.tyenv))
# noinspection PyPep8Naming
[docs]class SimEngineLightAILMixin(SimEngineLightMixin):
def _process(
self, state, successors, *args, block=None, whitelist=None, **kwargs
): # pylint:disable=arguments-differ
self.tmps = {}
self.block: ailment.Block = block
self.state = state
self.arch = state.arch
self._process_Stmt(whitelist=whitelist)
self.stmt_idx = None
self.ins_addr = None
def _process_Stmt(self, whitelist=None):
if whitelist is not None:
whitelist = set(whitelist)
for stmt_idx, stmt in enumerate(self.block.statements):
if whitelist is not None and stmt_idx not in whitelist:
continue
self.stmt_idx = stmt_idx
self.ins_addr = stmt.ins_addr
self._handle_Stmt(stmt)
def _expr(self, expr):
expr_type_name = type(expr).__name__
if isinstance(expr, ailment.Stmt.Call):
# Call can be both an expression and a statement. Add a suffix to make sure we are working on the expression
# variant.
expr_type_name += "Expr"
h = None
handler = "_handle_%s" % expr_type_name
if hasattr(self, handler):
h = getattr(self, handler)
if h is None:
handler = "_ail_handle_%s" % expr_type_name
if hasattr(self, handler):
h = getattr(self, handler)
if h is not None:
return h(expr)
self.l.warning("Unsupported expression type %s.", type(expr).__name__)
return None
#
# Helper methods
#
def _codeloc(self):
# noinspection PyUnresolvedReferences
return CodeLocation(
self.block.addr, self.stmt_idx, ins_addr=self.ins_addr, context=self._context, block_idx=self.block.idx
)
#
# Statement handlers
#
def _handle_Stmt(self, stmt):
handler = "_handle_%s" % type(stmt).__name__
if hasattr(self, handler):
getattr(self, handler)(stmt)
return
# compatibility
old_handler = "_ail_handle_%s" % type(stmt).__name__
if hasattr(self, old_handler):
getattr(self, old_handler)(stmt)
return
self.l.warning("Unsupported statement type %s.", type(stmt).__name__)
def _ail_handle_Label(self, stmt):
pass
def _ail_handle_Jump(self, stmt):
raise NotImplementedError("Please implement the Jump handler with your own logic.")
def _ail_handle_Call(self, stmt):
raise NotImplementedError("Please implement the Call handler with your own logic.")
def _ail_handle_Return(self, stmt):
raise NotImplementedError("Please implement the Return handler with your own logic.")
#
# Expression handlers
#
def _ail_handle_BV(self, expr: claripy.ast.Base):
return expr
def _ail_handle_Const(self, expr): # pylint:disable=no-self-use
return expr.value
def _ail_handle_Tmp(self, expr):
tmp_idx = expr.tmp_idx
try:
return self.tmps[tmp_idx]
except KeyError:
return None
def _ail_handle_Load(self, expr):
raise NotImplementedError("Please implement the Load handler with your own logic.")
def _ail_handle_CallExpr(self, expr):
raise NotImplementedError("Please implement the CallExpr handler with your own logic.")
def _ail_handle_Reinterpret(self, expr: ailment.Expr.Reinterpret):
arg = self._expr(expr.operand)
if isinstance(arg, int) and (
expr.from_bits == 32 and expr.from_type == "I" and expr.to_bits == 32 and expr.to_type == "F"
):
# int -> float
b = struct.pack("<I", arg)
f = struct.unpack("<f", b)[0]
return f
elif (
isinstance(arg, float)
and expr.from_bits == 32
and expr.from_type == "F"
and expr.to_bits == 32
and expr.to_type == "I"
):
# float -> int
b = struct.pack("<f", arg)
v = struct.unpack("<I", b)[0]
return v
return expr
def _ail_handle_UnaryOp(self, expr):
handler_name = "_ail_handle_%s" % expr.op
try:
handler = getattr(self, handler_name)
except AttributeError:
self.l.warning("Unsupported UnaryOp %s.", expr.op)
return None
return handler(expr)
def _ail_handle_BinaryOp(self, expr):
handler_name = "_ail_handle_%s" % expr.op
try:
handler = getattr(self, handler_name)
except AttributeError:
self.l.warning("Unsupported BinaryOp %s.", expr.op)
return None
return handler(expr)
def _ail_handle_TernaryOp(self, expr):
handler_name = "_ail_handle_%s" % expr.op
try:
handler = getattr(self, handler_name)
except AttributeError:
self.l.warning("Unsupported Ternary %s.", expr.op)
return None
return handler(expr)
#
# Binary operation handlers
#
def _ail_handle_CmpLT(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 <= expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "CmpLT", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Add(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 + expr_1
except TypeError:
return ailment.Expr.BinaryOp(
expr.idx,
"Add",
[expr_0, expr_1],
expr.signed,
floating_point=expr.floating_point,
rounding_mode=expr.rounding_mode,
**expr.tags,
)
def _ail_handle_Sub(self, expr):
arg0, arg1 = expr.operands
if not isinstance(arg0, claripy.ast.Base):
expr_0 = self._expr(arg0)
else:
expr_0 = arg0
if not isinstance(arg1, claripy.ast.Base):
expr_1 = self._expr(arg1)
else:
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 - expr_1
except TypeError:
return ailment.Expr.BinaryOp(
expr.idx,
"Sub",
[expr_0, expr_1],
expr.signed,
floating_point=expr.floating_point,
rounding_mode=expr.rounding_mode,
**expr.tags,
)
def _ail_handle_Div(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 // expr_1
except TypeError:
return ailment.Expr.BinaryOp(
expr.idx,
"Div",
[expr_0, expr_1],
expr.signed,
floating_point=expr.floating_point,
rounding_mode=expr.rounding_mode,
**expr.tags,
)
def _ail_handle_DivMod(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
return ailment.Expr.BinaryOp(
expr.idx, "DivMod", [expr_0, expr_1], expr.signed, bits=expr_0.bits * 2, **expr.tags
)
def _ail_handle_Mod(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 % expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Mod", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Mul(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 * expr_1
except TypeError:
return ailment.Expr.BinaryOp(
expr.idx,
"Mul",
[expr_0, expr_1],
expr.signed,
bits=expr.bits,
floating_point=expr.floating_point,
rounding_mode=expr.rounding_mode,
**expr.tags,
)
def _ail_handle_Mull(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 * expr_1
except TypeError:
return ailment.Expr.BinaryOp(
expr.idx,
"Mull",
[expr_0, expr_1],
expr.signed,
bits=expr.bits,
floating_point=expr.floating_point,
rounding_mode=expr.rounding_mode,
**expr.tags,
)
def _ail_handle_And(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 & expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "And", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Or(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 | expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Or", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Xor(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 ^ expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Xor", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Shr(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 >> expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Shr", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Shl(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 << expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Shl", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Sal(self, expr):
return self._ail_handle_Shl(expr)
def _ail_handle_Sar(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
try:
return expr_0 >> expr_1
except TypeError:
return ailment.Expr.BinaryOp(expr.idx, "Sar", [expr_0, expr_1], expr.signed, **expr.tags)
def _ail_handle_Concat(self, expr):
arg0, arg1 = expr.operands
expr_0 = self._expr(arg0)
expr_1 = self._expr(arg1)
if expr_0 is None:
expr_0 = arg0
if expr_1 is None:
expr_1 = arg1
return ailment.Expr.BinaryOp(expr.idx, "Concat", [expr_0, expr_1], expr.signed, **expr.tags)
#
# Unary operation handlers
#
def _ail_handle_Convert(self, expr):
data = self._expr(expr.operand)
if data is not None:
if type(data) is int:
return data
return None
def _ail_handle_Not(self, expr):
data = self._expr(expr.operand)
if data is None:
return None
try:
return ~data # pylint:disable=invalid-unary-operand-type
except TypeError:
return ailment.Expr.UnaryOp(expr.idx, "Not", data, **expr.tags)
# Compatibility
SimEngineLightVEX = SimEngineLightVEXMixin
SimEngineLightAIL = SimEngineLightAILMixin