Source code for angr.engines.light.engine

# 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] def extract_offset_to_sp(self, spoffset_expr: claripy.ast.Base) -> Optional[int]: """ Extract the offset to the original stack pointer. :param spoffset_expr: The claripy AST to parse. :return: The offset to the original stack pointer, or None if `spoffset_expr` is not a supported type of SpOffset expression. """ if "SpOffset" in spoffset_expr.variables: # Local variable if spoffset_expr.op == "BVS": return 0 elif spoffset_expr.op == "__add__": if len(spoffset_expr.args) == 1: # Unexpected but fine return 0 elif isinstance(spoffset_expr.args[1], claripy.ast.Base) and spoffset_expr.args[1].op == "BVV": return spoffset_expr.args[1].args[0] return None
[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