Source code for revenge.plugins.angr.angr


import logging

LOGGER = logging.getLogger(__name__)

try:
    import angr
    LOGGER.debug("angr found and successfully imported.")

    from .revenge_target import RevengeConcreteTarget
    LOGGER.debug("RevengeConcreteTarget found and successfully imported.")
    ANGR_OK = True
except ModuleNotFoundError:
    ANGR_OK = False
    LOGGER.debug("angr not found or required lib not successfully imported.")

from revenge.exceptions import *
from revenge import common
from .. import Plugin


[docs]class Angr(Plugin): def __init__(self, process, thread=None): """Use angr to enrich your reversing. Examples: .. code-block:: python3 # Grab current location thread = list(process.threads)[0] # Load options and state options can be configured # They use the same name but are exposed as attributes here # If you SET any of these, the project will be re-loaded next # time you ask for an object. It will NOT affect the current # object instance you have. thread.angr.load_options thread.angr.support_selfmodifying_code thread.angr.use_sim_procedures thread.angr.add_options # Ask for a simgr for this location simgr = thread.angr.simgr # Whoops, we wanted self modifying code! thread.angr.support_selfmodifying_code = True simgr = thread.angr.simgr # Use this as you normally would simgr.explore(find=winner) """ self._process = process self._thread = thread self.__project = None self.__sim_procedures_resolved = False self.load_options = {'auto_load_libs': False} self.support_selfmodifying_code = False self.use_sim_procedures = True self.exclude_sim_procedures_list = [] self.add_options = set([]) self.remove_options = set([]) if ANGR_OK: try: self._process.threads._register_plugin(Angr._thread_plugin, "angr") except RevengeModulePluginAlreadyRegistered: # This will error out if we're already registered pass @property def load_options(self): """angr load_options""" return self.__load_options @load_options.setter def load_options(self, load_options): self.__load_options = load_options # Invalidate the project self.__project = None @property def exclude_sim_procedures_list(self): """bool: Which procedures should angr not wrap?""" return self.__exclude_sim_procedures_list @exclude_sim_procedures_list.setter def exclude_sim_procedures_list(self, exclude_sim_procedures_list): self.__exclude_sim_procedures_list = exclude_sim_procedures_list # Invalidate the project self.__project = None @property def use_sim_procedures(self): """bool: Should angr use sim procedures?""" return self.__use_sim_procedures @use_sim_procedures.setter def use_sim_procedures(self, use_sim_procedures): self.__use_sim_procedures = use_sim_procedures # Invalidate the project self.__project = None @property def support_selfmodifying_code(self): """bool: Should angr support self modifying code?""" return self.__support_selfmodifying_code @support_selfmodifying_code.setter def support_selfmodifying_code(self, support_selfmodifying_code): self.__support_selfmodifying_code = support_selfmodifying_code # Invalidate the project self.__project = None @property def _is_valid(self): # Not registering this as a process plugin for now. return False @classmethod def _thread_plugin(klass, thread): return klass(thread._process, thread=thread) @property def project(self): """Returns the angr project for this file.""" if self.__project is not None: return self.__project # Original path might not be our final path # For example: loading angr on a remote android project orig_path = self._process.modules[self._thread.pc].path new_path = common.load_file(self._process, orig_path).name self.__project = angr.Project( new_path, load_options=self.load_options, concrete_target=self._concrete_target, use_sim_procedures=self.use_sim_procedures, exclude_sim_procedures_list=self.exclude_sim_procedures_list, support_selfmodifying_code=self.support_selfmodifying_code) self.__sim_procedures_resolved = False return self.__project @property def state(self): """Returns a state object for the current thread state.""" if self._thread.breakpoint and self._thread.pc in self._process.threads._breakpoint_original_bytes: LOGGER.warning("Overwriting current breakpoint in memory so it doesn't trip up angr.") LOGGER.warning("This has the side-effect of disabling this breakpoint. You can re-enable manually.") orig_bytes = self._process.threads._breakpoint_original_bytes[self._thread.pc] self._process.memory[self._thread.pc:self._thread.pc + len(orig_bytes)].bytes = orig_bytes state = self.project.factory.entry_state( add_options=self.add_options, remove_options=self.remove_options) if self._concrete_target is not None: state.concrete.sync() if not self.__sim_procedures_resolved: LOGGER.debug("Attempting to resolve angr sim procs") # Fixup angr's Sim Procedures since it cannot handle PIC me = self._process.modules[self._thread.pc] imports = [rel.symbol.name for rel in self.project.loader.main_object.relocs if rel.symbol is not None and rel.symbol.is_import] for imp in imports: try: # Try to find the plt first imp_addr = me.symbols["plt." + imp].address except KeyError: # Fallback to trying to find the actual resolved function symbol try: imp_addr = self._process.memory[imp].address except RevengeSymbolLookupFailure: # Give up continue self.project.rehook_symbol(imp_addr, imp, True) self.__sim_procedures_resolved = True return state @property def simgr(self): """Returns an angr simgr object for the current state.""" return self.project.factory.simgr(self.state) @property def _concrete_target(self): """Returns a concrete target for this context or None.""" if self._thread is not None: return RevengeConcreteTarget(self._process, self._thread.context)
# doc fixup Angr.__doc__ = Angr.__init__.__doc__