angr.analyses.decompiler.optimization_passes.engine_base 源代码

# pylint:disable=too-many-boolean-expressions
from __future__ import annotations
import logging

import ailment

from angr.engines.light import SimEngineLightAIL

_l = logging.getLogger(name=__name__)


[文档] class SimplifierAILState: """ The abstract state used in SimplifierAILEngine. """
[文档] def __init__(self, arch, variables=None): self.arch = arch self._variables = {} if variables is None else variables
def __repr__(self): return "<SimplifierAILState>"
[文档] def copy(self): return SimplifierAILState( self.arch, variables=self._variables.copy(), )
[文档] def merge(self, *others): raise NotImplementedError
[文档] def store_variable(self, old: ailment.expression.VirtualVariable, new): if new is not None: self._variables[old.varid] = new
[文档] def get_variable(self, old: ailment.expression.VirtualVariable): return self._variables.get(old.varid, None)
[文档] def remove_variable(self, old): self._variables.pop(old, None)
[文档] class SimplifierAILEngine( SimEngineLightAIL[SimplifierAILState, ailment.expression.Expression, ailment.statement.Statement, ailment.Block] ): """ Essentially implements a peephole optimization engine for AIL statements (because we do not perform memory or register loads). """ def _process_block_end(self, block, stmt_data, whitelist): if whitelist is None: block.statements = stmt_data else: for stmt_idx, stmt in zip(sorted(whitelist), stmt_data): block.statements[stmt_idx] = stmt return block def _top(self, bits): assert False, "This code should be unreachable" def _is_top(self, expr): assert False, "This code should be unreachable" # handle stmt def _handle_stmt_Assignment(self, stmt): src = self._expr(stmt.src) dst = self._expr(stmt.dst) if isinstance(dst, ailment.expression.VirtualVariable) and not isinstance(src, ailment.expression.Phi): self.state.store_variable(dst, src) if (src, dst) != (stmt.src, stmt.dst): return ailment.statement.Assignment(stmt.idx, dst, src, **stmt.tags) return stmt def _handle_stmt_Store(self, stmt): addr = self._expr(stmt.addr) data = self._expr(stmt.data) # replace if (addr, data) != (stmt.addr, stmt.data): return ailment.statement.Store( stmt.idx, addr, data, stmt.size, stmt.endness, variable=stmt.variable, **stmt.tags ) return stmt def _handle_stmt_Jump(self, stmt): target = self._expr(stmt.target) return ailment.statement.Jump(stmt.idx, target, **stmt.tags) def _handle_stmt_ConditionalJump(self, stmt): # pylint: disable=no-self-use condition = self._expr(stmt.condition) iftrue = self._expr(stmt.true_target) if stmt.true_target is not None else None iffalse = self._expr(stmt.false_target) if stmt.false_target is not None else None if (condition, iftrue, iffalse) != (stmt.condition, stmt.true_target, stmt.false_target): return ailment.statement.ConditionalJump( stmt.idx, condition, iftrue, iffalse, true_target_idx=stmt.true_target_idx, false_target_idx=stmt.false_target_idx, **stmt.tags, ) return stmt def _handle_stmt_Call(self, stmt): target = self._expr(stmt.target) if isinstance(stmt.target, ailment.Expr.Expression) else stmt.target new_args = None if stmt.args: new_args = [] for arg in stmt.args: new_arg = self._expr(arg) new_args.append(new_arg) return ailment.statement.Call( stmt.idx, target, calling_convention=stmt.calling_convention, prototype=stmt.prototype, args=new_args, ret_expr=stmt.ret_expr, fp_ret_expr=stmt.fp_ret_expr, bits=stmt.bits, **stmt.tags, ) def _handle_stmt_Return(self, stmt): if stmt.ret_exprs: new_retexprs = [] for ret_expr in stmt.ret_exprs: new_retexpr = self._expr(ret_expr) new_retexprs.append(new_retexpr) if new_retexprs != stmt.ret_exprs: new_stmt = stmt.copy() new_stmt.ret_exprs = new_retexprs return new_stmt return stmt def _handle_stmt_DirtyStatement(self, stmt): expr = self._expr(stmt.dirty) if expr != stmt.dirty: return ailment.statement.DirtyStatement(stmt.idx, expr, **stmt.tags) return stmt def _handle_stmt_Label(self, stmt): return stmt # handle expr def _handle_expr_StackBaseOffset(self, expr): # pylint:disable=no-self-use return expr def _handle_expr_VirtualVariable(self, expr): # pylint:disable=no-self-use # We don't want to return new values and construct new AIL expressions in caller methods without def-use # information. Otherwise, we may end up creating incorrect expressions. # Therefore, we do not perform vvar load, which essentially turns SimplifierAILEngine into a peephole # optimization engine. return expr def _handle_expr_Phi(self, expr): # pylint:disable=no-self-use return expr def _handle_expr_Load(self, expr): # We don't want to load new values and construct new AIL expressions in caller methods without def-use # information. Otherwise, we may end up creating incorrect expressions. # Therefore, we do not perform memory load, which essentially turns SimplifierAILEngine into a peephole # optimization engine. addr = self._expr(expr.addr) if addr != expr.addr: return ailment.expression.Load(expr.idx, addr, expr.size, expr.endness, **expr.tags) return expr def _handle_expr_Register(self, expr): # pylint:disable=no-self-use # We don't want to return new values and construct new AIL expressions in caller methods without def-use # information. Otherwise, we may end up creating incorrect expressions. # Therefore, we do not perform register load, which essentially turns SimplifierAILEngine into a peephole # optimization engine. return expr def _handle_binop_Mul(self, expr): operand_0 = self._expr(expr.operands[0]) operand_1 = self._expr(expr.operands[1]) if (operand_0, operand_1) != (expr.operands[0], expr.operands[1]): return ailment.expression.BinaryOp(expr.idx, "Mul", [operand_0, operand_1], expr.signed, **expr.tags) return expr def _handle_binop_Mull(self, expr): operand_0 = self._expr(expr.operands[0]) operand_1 = self._expr(expr.operands[1]) if (operand_0, operand_1) != (expr.operands[0], expr.operands[1]): return ailment.expression.BinaryOp(expr.idx, "Mull", [operand_0, operand_1], expr.signed, **expr.tags) return expr def _handle_expr_Convert(self, expr): operand_expr = self._expr(expr.operand) if isinstance(operand_expr, ailment.expression.Convert): if expr.from_bits == operand_expr.to_bits and expr.to_bits == operand_expr.from_bits: # eliminate the redundant Convert return operand_expr.operand return ailment.expression.Convert( expr.idx, operand_expr.from_bits, expr.to_bits, expr.is_signed, operand_expr.operand, from_type=operand_expr.from_type, to_type=expr.to_type, rounding_mode=expr.rounding_mode, **expr.tags, ) if ( type(operand_expr) is ailment.expression.Const and expr.from_type == ailment.expression.Convert.TYPE_INT and expr.to_type == ailment.expression.Convert.TYPE_INT ): # do the conversion right away value = operand_expr.value mask = (2**expr.to_bits) - 1 value &= mask return ailment.expression.Const(expr.idx, operand_expr.variable, value, expr.to_bits, **expr.tags) if type(operand_expr) is ailment.expression.BinaryOp and operand_expr.op in { "Mul", "Shl", "Div", "Mod", "Add", "Sub", }: if isinstance(operand_expr.operands[1], ailment.expression.Const): if ( isinstance(operand_expr.operands[0], ailment.expression.Register) and expr.from_bits == operand_expr.operands[0].bits ): converted = ailment.expression.Convert( expr.idx, expr.from_bits, expr.to_bits, expr.is_signed, operand_expr.operands[0] ) converted_const = ailment.expression.Const( operand_expr.operands[1].idx, operand_expr.operands[1].variable, operand_expr.operands[1].value, expr.to_bits, **operand_expr.operands[1].tags, ) return ailment.expression.BinaryOp( operand_expr.idx, operand_expr.op, [converted, converted_const], operand_expr.signed, **expr.tags, ) # TODO: the below optimization was unsound # Conv(32->64, (Conv(64->32, r14<8>) + 0x1<32>)) became Add(r14<8>, 0x1<32>) # ideally it should become Conv(32->64, Conv(64->32, r14<8> + 0x1<64>)) # and then the double convert can be pretty-printed away # elif isinstance(operand_expr.operands[0], ailment.expression.Convert) and \ # expr.from_bits == operand_expr.operands[0].to_bits and \ # expr.to_bits == operand_expr.operands[0].from_bits: # return ailment.expression.BinaryOp(operand_expr.idx, operand_expr.op, # [operand_expr.operands[0].operand, operand_expr.operands[1]], # operand_expr.signed, # **operand_expr.tags) elif ( isinstance(operand_expr.operands[0], ailment.expression.Convert) and isinstance(operand_expr.operands[1], ailment.expression.Convert) and operand_expr.operands[0].from_bits == operand_expr.operands[1].from_bits ) and ( operand_expr.operands[0].to_bits == operand_expr.operands[1].to_bits and expr.from_bits == operand_expr.operands[0].to_bits and expr.to_bits == operand_expr.operands[1].from_bits ): return ailment.expression.BinaryOp( operand_expr.idx, operand_expr.op, [operand_expr.operands[0].operand, operand_expr.operands[1].operand], expr.is_signed, **operand_expr.tags, ) return ailment.expression.Convert( expr.idx, expr.from_bits, expr.to_bits, expr.is_signed, operand_expr, from_type=expr.from_type, to_type=expr.to_type, rounding_mode=expr.rounding_mode, **expr.tags, ) def _handle_expr_Const(self, expr): return expr def _handle_expr_Tmp(self, expr): return expr def _handle_expr_Reinterpret(self, expr): return expr def _handle_expr_ITE(self, expr): return expr def _handle_expr_Call(self, expr): return expr def _handle_expr_DirtyExpression(self, expr): return expr def _handle_expr_VEXCCallExpression(self, expr): return expr def _handle_expr_MultiStatementExpression(self, expr): return expr def _handle_expr_BasePointerOffset(self, expr): return expr def _handle_unop_Default(self, expr): operand = self._expr(expr.operand) if operand != expr.operand: return ailment.expression.UnaryOp( expr.idx, expr.op, operand, variable=expr.variable, variable_offset=expr.variable_offset, **expr.tags ) return expr _handle_unop_Not = _handle_unop_Default _handle_unop_Neg = _handle_unop_Default _handle_unop_BitwiseNeg = _handle_unop_Default _handle_unop_Reference = _handle_unop_Default _handle_unop_Dereference = _handle_unop_Default _handle_unop_Clz = _handle_unop_Default _handle_unop_Ctz = _handle_unop_Default _handle_unop_GetMSBs = _handle_unop_Default _handle_unop_unpack = _handle_unop_Default _handle_unop_Sqrt = _handle_unop_Default _handle_unop_RSqrtEst = _handle_unop_Default def _handle_binop_Default(self, expr): lhs = self._expr(expr.operands[0]) rhs = self._expr(expr.operands[1]) if (lhs, rhs) != tuple(expr.operands): return ailment.expression.BinaryOp( expr.idx, expr.op, (lhs, rhs), expr.signed, variable=expr.variable, variable_offset=expr.variable_offset, bits=expr.bits, floating_point=expr.floating_point, rounding_mode=expr.rounding_mode, vector_count=expr.vector_count, vector_size=expr.vector_size, **expr.tags, ) return expr _handle_binop_Add = _handle_binop_Default _handle_binop_AddF = _handle_binop_Default _handle_binop_AddV = _handle_binop_Default _handle_binop_Sub = _handle_binop_Default _handle_binop_SubF = _handle_binop_Default _handle_binop_SubV = _handle_binop_Default _handle_binop_MulF = _handle_binop_Default _handle_binop_MulV = _handle_binop_Default _handle_binop_MulHiV = _handle_binop_Default _handle_binop_Div = _handle_binop_Default _handle_binop_DivV = _handle_binop_Default _handle_binop_DivF = _handle_binop_Default _handle_binop_Mod = _handle_binop_Default _handle_binop_Xor = _handle_binop_Default _handle_binop_And = _handle_binop_Default _handle_binop_Or = _handle_binop_Default _handle_binop_LogicalAnd = _handle_binop_Default _handle_binop_LogicalOr = _handle_binop_Default _handle_binop_Shl = _handle_binop_Default _handle_binop_Shr = _handle_binop_Default _handle_binop_Sar = _handle_binop_Default _handle_binop_CmpF = _handle_binop_Default _handle_binop_CmpEQ = _handle_binop_Default _handle_binop_CmpNE = _handle_binop_Default _handle_binop_CmpLT = _handle_binop_Default _handle_binop_CmpLE = _handle_binop_Default _handle_binop_CmpGT = _handle_binop_Default _handle_binop_CmpGE = _handle_binop_Default _handle_binop_Concat = _handle_binop_Default _handle_binop_Ror = _handle_binop_Default _handle_binop_Rol = _handle_binop_Default _handle_binop_Carry = _handle_binop_Default _handle_binop_SCarry = _handle_binop_Default _handle_binop_SBorrow = _handle_binop_Default _handle_binop_InterleaveLOV = _handle_binop_Default _handle_binop_InterleaveHIV = _handle_binop_Default _handle_binop_CasCmpEQ = _handle_binop_Default _handle_binop_CasCmpNE = _handle_binop_Default _handle_binop_ExpCmpNE = _handle_binop_Default _handle_binop_SarNV = _handle_binop_Default _handle_binop_ShrNV = _handle_binop_Default _handle_binop_ShlNV = _handle_binop_Default _handle_binop_CmpEQV = _handle_binop_Default _handle_binop_CmpNEV = _handle_binop_Default _handle_binop_CmpGEV = _handle_binop_Default _handle_binop_CmpGTV = _handle_binop_Default _handle_binop_CmpLEV = _handle_binop_Default _handle_binop_CmpLTV = _handle_binop_Default _handle_binop_MinV = _handle_binop_Default _handle_binop_MaxV = _handle_binop_Default _handle_binop_QAddV = _handle_binop_Default _handle_binop_QNarrowBinV = _handle_binop_Default _handle_binop_PermV = _handle_binop_Default _handle_binop_Set = _handle_binop_Default