Source code for angr.analyses.cfg.indirect_jump_resolvers.const_resolver

from typing import TYPE_CHECKING
import logging

import claripy
import pyvex

from ....utils.constants import DEFAULT_STATEMENT
from ....code_location import CodeLocation
from ....blade import Blade
from ...propagator import vex_vars
from .resolver import IndirectJumpResolver
from .propagator_utils import PropagatorLoadCallback

if TYPE_CHECKING:
    from angr import Block

_l = logging.getLogger(name=__name__)


[docs]def exists_in_replacements(replacements, block_loc, tmp_var): exists = False for rep in replacements: if rep == block_loc: exists = True break if not exists: return False exists = False for var in replacements[block_loc]: if var == tmp_var: exists = True break return exists
[docs]class ConstantResolver(IndirectJumpResolver): """ Resolve an indirect jump by running a constant propagation on the entire function and check if the indirect jump can be resolved to a constant value. This resolver must be run after all other more specific resolvers. """
[docs] def __init__(self, project): super().__init__(project, timeless=False)
[docs] def filter(self, cfg, addr, func_addr, block, jumpkind): # we support both an indirect call and jump since the value can be resolved if jumpkind in {"Ijk_Boring", "Ijk_Call"}: return True return False
[docs] def resolve( # pylint:disable=unused-argument self, cfg, addr: int, func_addr: int, block: "Block", jumpkind: str, func_graph_complete: bool = True, **kwargs ): """ This function does the actual resolve. Our process is easy: Propagate all values inside the function specified, then extract the tmp_var used for the indirect jump from the basic block. Use the tmp var to locate the constant value stored in the replacements. If not present, returns False tuple. :param cfg: CFG with specified function :param addr: Address of indirect jump :param func_addr: Address of function of indirect jump :param block: Block of indirect jump (Block object) :param jumpkind: VEX jumpkind (Ijk_Boring or Ijk_Call) :return: Bool tuple with replacement address """ vex_block = block.vex if isinstance(vex_block.next, pyvex.expr.RdTmp): # what does the jump rely on? slice it back and see b = Blade( cfg.graph, addr, -1, cfg=cfg, project=self.project, ignore_bp=False, ignore_sp=False, max_level=3, stop_at_calls=True, cross_insn_opt=True, ) stmt_loc = addr, DEFAULT_STATEMENT preds = list(b.slice.predecessors(stmt_loc)) while preds: if len(preds) == 1: # skip all IMarks pred_addr, stmt_idx = preds[0] if stmt_idx != DEFAULT_STATEMENT: block = self.project.factory.block(pred_addr, cross_insn_opt=True).vex if isinstance(block.statements[stmt_idx], pyvex.IRStmt.IMark): preds = list(b.slice.predecessors(preds[0])) continue for pred_addr, stmt_idx in preds: block = self.project.factory.block(pred_addr, cross_insn_opt=True).vex if stmt_idx != DEFAULT_STATEMENT: stmt = block.statements[stmt_idx] if isinstance(stmt, pyvex.IRStmt.WrTmp) and isinstance(stmt.data, pyvex.IRExpr.Load): # loading from memory - unsupported return False, [] break func = cfg.functions[func_addr] _l.debug("ConstantResolver: Propagating for %r at %#x.", func, addr) prop = self.project.analyses.Propagator( func=func, only_consts=True, do_binops=True, vex_cross_insn_opt=False, completed_funcs=cfg._completed_functions, load_callback=PropagatorLoadCallback(self.project).propagator_load_callback, cache_results=True, key_prefix="cfg_intermediate", ) replacements = prop.replacements if replacements: block_loc = CodeLocation(block.addr, None) tmp_var = vex_vars.VEXTmp(vex_block.next.tmp) if exists_in_replacements(replacements, block_loc, tmp_var): resolved_tmp = replacements[block_loc][tmp_var] if ( isinstance(resolved_tmp, claripy.ast.Base) and resolved_tmp.op == "BVV" and self._is_target_valid(cfg, resolved_tmp.args[0]) ): return True, [resolved_tmp.args[0]] return False, []