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

import logging

import archinfo
import pyvex

from ....blade import Blade
from ....utils.constants import DEFAULT_STATEMENT
from .resolver import IndirectJumpResolver

_l = logging.getLogger(name=__name__)


[docs]class ArmElfFastResolver(IndirectJumpResolver): """ Resolves the indirect jump in ARM ELF binaries where all internal function calls are performed in the following manner:: ldr r3, [pc+#0x124] ; load a constant from the constant_pool blx r3 """
[docs] def __init__(self, project): super().__init__(project, timeless=True)
[docs] def filter(self, cfg, addr, func_addr, block, jumpkind): if not isinstance(self.project.arch, archinfo.ArchARM): return False if jumpkind not in ("Ijk_Boring", "Ijk_Call"): return False return True
[docs] def resolve( # pylint:disable=unused-argument self, cfg, addr, func_addr, block, jumpkind, func_graph_complete: bool = True, **kwargs ): """ The main resolving function. :param cfg: A CFG instance. :param int addr: Address of the IRSB. :param int func_addr: Address of the function. :param block: The IRSB. :param str jumpkind: The jumpkind. :return: :rtype: tuple """ # Note that this function assumes the IRSB is optimized (opt_level > 0) # the logic will be vastly different if the IRSB is not optimized (opt_level == 0) b = Blade(cfg.graph, addr, -1, cfg=cfg, project=self.project, ignore_sp=True, ignore_bp=True, max_level=2) sources = [n for n in b.slice.nodes() if b.slice.in_degree(n) == 0] if not sources: return False, [] if len(sources) != 1: return False, [] source = sources[0] block_addr, stmt_idx = source if block_addr != block.addr: # TODO: We should be able to support this case very easily # TODO: Fix it when we see such a case return False, [] stmt = block.statements[stmt_idx] if not isinstance(stmt, pyvex.IRStmt.WrTmp): return False, [] if not isinstance(stmt.data, pyvex.IRExpr.Load): return False, [] if not isinstance(stmt.data.addr, pyvex.IRExpr.Const): return False, [] load_addr = stmt.data.addr.con.value load_size = stmt.data.result_size(block.tyenv) // 8 endness = archinfo.Endness.BE if stmt.data.endness == "Iend_BE" else archinfo.Endness.LE # the next statement should be the default exit next_target = next(iter(b.slice.successors(source))) if not (next_target[0] == block.addr and next_target[1] == DEFAULT_STATEMENT): return False, [] next_tmp = block.next if next_tmp.tmp != stmt.tmp: return False, [] # load the address to jump to try: target_addr = self.project.loader.memory.unpack_word(load_addr, size=load_size, endness=endness) if cfg.tag == "CFGFast": cfg._seg_list.occupy(load_addr, load_size, "pointer-array") except KeyError: return False, [] return True, [target_addr]