Source code for angr.project

import logging
import os
import types
from io import BytesIO, IOBase
import pickle
import string
from collections import defaultdict
from pathlib import Path
from typing import Any, cast

import archinfo
from archinfo.arch_soot import SootAddressDescriptor, ArchSoot
import cle
from .sim_procedure import SimProcedure

from .misc.ux import deprecated
from .errors import AngrNoPluginError

l = logging.getLogger(name=__name__)


[docs] def load_shellcode(shellcode: bytes | str, arch, start_offset=0, load_address=0, thumb=False, **kwargs): """ Load a new project based on a snippet of assembly or bytecode. :param shellcode: The data to load, as either a bytestring of instructions or a string of assembly text :param arch: The name of the arch to use, or an archinfo class :param start_offset: The offset into the data to start analysis (default 0) :param load_address: The address to place the data in memory (default 0) :param thumb: Whether this is ARM Thumb shellcode """ if not isinstance(arch, archinfo.Arch): arch = archinfo.arch_from_id(arch) if isinstance(shellcode, str): shellcode_bytes: bytes = arch.asm(shellcode, load_address, thumb=thumb) else: shellcode_bytes = shellcode if thumb: start_offset |= 1 return Project( BytesIO(shellcode_bytes), main_opts={ "backend": "blob", "arch": arch, "entry_point": start_offset, "base_addr": load_address, }, **kwargs, )
[docs] class Project: """ This is the main class of the angr module. It is meant to contain a set of binaries and the relationships between them, and perform analyses on them. :param thing: The path to the main executable object to analyze, or a CLE Loader object. The following parameters are optional. :param default_analysis_mode: The mode of analysis to use by default. Defaults to 'symbolic'. :param ignore_functions: A list of function names that, when imported from shared libraries, should never be stepped into in analysis (calls will return an unconstrained value). :param use_sim_procedures: Whether to replace resolved dependencies for which simprocedures are available with said simprocedures. :param exclude_sim_procedures_func: A function that, when passed a function name, returns whether or not to wrap it with a simprocedure. :param exclude_sim_procedures_list: A list of functions to *not* wrap with simprocedures. :param arch: The target architecture (auto-detected otherwise). :param simos: a SimOS class to use for this project. :param engine: The SimEngine class to use for this project. :param bool translation_cache: If True, cache translated basic blocks rather than re-translating them. :param selfmodifying_code: Whether we aggressively support self-modifying code. When enabled, emulation will try to read code from the current state instead of the original memory, regardless of the current memory protections. :param store_function: A function that defines how the Project should be stored. Default to pickling. :param load_function: A function that defines how the Project should be loaded. Default to unpickling. :param analyses_preset: The plugin preset for the analyses provider (i.e. Analyses instance). :type analyses_preset: angr.misc.PluginPreset Any additional keyword arguments passed will be passed onto ``cle.Loader``. :ivar analyses: The available analyses. :type analyses: angr.analysis.Analyses :ivar entry: The program entrypoint. :ivar factory: Provides access to important analysis elements such as path groups and symbolic execution results. :type factory: AngrObjectFactory :ivar filename: The filename of the executable. :ivar loader: The program loader. :type loader: cle.Loader :ivar storage: Dictionary of things that should be loaded/stored with the Project. :type storage: defaultdict(list) """ arch: archinfo.Arch
[docs] def __init__( self, thing, default_analysis_mode=None, ignore_functions=None, use_sim_procedures=True, exclude_sim_procedures_func=None, exclude_sim_procedures_list=(), arch=None, simos=None, engine=None, load_options: dict[str, Any] | None = None, translation_cache=True, selfmodifying_code: bool = False, support_selfmodifying_code: bool | None = None, # deprecated. use selfmodifying_code instead store_function=None, load_function=None, analyses_preset=None, concrete_target=None, eager_ifunc_resolution=None, **kwargs, ): # Step 1: Load the binary if load_options is None: load_options = {} load_options.update(kwargs) if arch is not None: load_options.update({"arch": arch}) if isinstance(thing, cle.Loader): if load_options: l.warning("You provided CLE options to angr but you also provided a completed cle.Loader object!") self.loader = thing self.filename = self.loader.main_object.binary elif isinstance(thing, cle.Backend): self.filename = thing.binary self.loader = cle.Loader(thing, **load_options) elif hasattr(thing, "read") and hasattr(thing, "seek"): l.info("Loading binary from stream") self.filename = None self.loader = cle.Loader(thing, **load_options) elif not isinstance(thing, (str, Path)) or not os.path.exists(thing) or not os.path.isfile(thing): raise Exception("Not a valid binary file: %s" % repr(thing)) else: # use angr's loader, provided by cle l.info("Loading binary %s", thing) self.filename = str(thing) self.loader = cle.Loader(self.filename, concrete_target=concrete_target, **load_options) # Step 2: determine its CPU architecture, ideally falling back to CLE's guess if isinstance(arch, str): self.arch = archinfo.arch_from_id(arch) # may raise ArchError, let the user see this elif isinstance(arch, archinfo.Arch): self.arch: archinfo.Arch = arch elif arch is None: self.arch = self.loader.main_object.arch else: raise ValueError("Invalid arch specification.") # Step 3: Set some defaults and set the public and private properties if not default_analysis_mode: default_analysis_mode = "symbolic" if not ignore_functions: ignore_functions = [] if isinstance(exclude_sim_procedures_func, types.LambdaType): l.warning( "Passing a lambda type as the exclude_sim_procedures_func argument to " "Project causes the resulting object to be un-serializable." ) self._sim_procedures = {} self.concrete_target = concrete_target # It doesn't make any sense to have auto_load_libs # if you have the concrete target, let's warn the user about this. if self.concrete_target and load_options.get("auto_load_libs", None): l.critical( "Incompatible options selected for this project, please disable auto_load_libs if " "you want to use a concrete target." ) raise Exception("Incompatible options for the project") if self.concrete_target and self.arch.name not in ["X86", "AMD64", "ARMHF", "ARMEL", "MIPS32"]: l.critical("Concrete execution does not support yet the selected architecture. Aborting.") raise Exception("Incompatible options for the project") self._default_analysis_mode = default_analysis_mode self._exclude_sim_procedures_func = exclude_sim_procedures_func self._exclude_sim_procedures_list = exclude_sim_procedures_list self.use_sim_procedures = use_sim_procedures self._ignore_functions = ignore_functions self.selfmodifying_code = selfmodifying_code self._translation_cache = translation_cache self._eager_ifunc_resolution = eager_ifunc_resolution self._executing = False # this is a flag for the convenience API, exec() and terminate_execution() below # deprecation warning if support_selfmodifying_code is not None: l.warning( 'Parameter "support_selfmodifying_code" is deprecated. Please use "selfmodifying_code"' ' instead. "support_selfmodifying_code" will be removed in the near future.' ) self.selfmodifying_code = bool(support_selfmodifying_code) if self.selfmodifying_code: if self._translation_cache is True: self._translation_cache = False l.warning("Disabling IRSB translation cache because support for self-modifying code is enabled.") self.entry = self.loader.main_object.entry self.storage = defaultdict(list) self.store_function = store_function or self._store self.load_function = load_function or self._load # FIXME: BIG HACK if isinstance(self.filename, str) and self.filename.endswith(".xcal") and not simos: simos = "snimmuc_nxp" # Step 4: determine the guest OS if isinstance(simos, type) and issubclass(simos, SimOS): self.simos = simos(self) # pylint:disable=invalid-name elif isinstance(simos, str): self.simos = os_mapping[simos](self) elif simos is None: self.simos = os_mapping[self.loader.main_object.os](self) else: raise ValueError("Invalid OS specification or non-matching architecture.") # Step 5: Set up the project's hubs # Step 5.1 Factory self.factory = AngrObjectFactory(self, default_engine=engine) # Step 5.2: Analyses self._analyses_preset = analyses_preset self._analyses = None self._initialize_analyses_hub() # Step 5.3: ...etc self.kb = KnowledgeBase(self, name="global") self.is_java_project = isinstance(self.arch, ArchSoot) self.is_java_jni_project = isinstance(self.arch, ArchSoot) and getattr( self.simos, "is_javavm_with_jni_support", False ) # Step 6: Register simprocedures as appropriate for library functions if isinstance(self.arch, ArchSoot) and getattr(self.simos, "is_javavm_with_jni_support", False): # If we execute a Java archive that includes native JNI libraries, # we need to use the arch of the native simos for all (native) sim # procedures. sim_proc_arch = getattr(self.simos, "native_arch") else: sim_proc_arch = self.arch for obj in self.loader.initial_load_objects: self._register_object(obj, sim_proc_arch) # Step 7: Run OS-specific configuration self.simos.configure_project()
@property def analyses(self) -> "AnalysesHubWithDefault": result = self._analyses if result is None: raise ValueError("Cannot access analyses this early in project lifecycle") return result @analyses.setter def analyses(self, v: "AnalysesHubWithDefault"): self._analyses = v def _initialize_analyses_hub(self): """ Initializes self.analyses using a given preset. """ self._analyses = cast("AnalysesHubWithDefault", AnalysesHub(self)) self._analyses.use_plugin_preset(self._analyses_preset if self._analyses_preset is not None else "default") def _register_object(self, obj, sim_proc_arch): """ This scans through an objects imports and hooks them with simprocedures from our library whenever possible """ # Step 1: get the set of libraries we are allowed to use to resolve unresolved symbols missing_libs = [] for lib_name in self.loader.missing_dependencies: try: missing_libs.append(SIM_LIBRARIES[lib_name]) except KeyError: l.info("There are no simprocedures for missing library %s :(", lib_name) # additionally provide libraries we _have_ loaded as a fallback fallback # this helps in the case that e.g. CLE picked up a linux arm libc to satisfy an android arm binary for lib in self.loader.all_objects: if lib.provides is not None and lib.provides in SIM_LIBRARIES: simlib = SIM_LIBRARIES[lib.provides] if simlib not in missing_libs: missing_libs.append(simlib) # Step 2: Categorize every "import" symbol in each object. # If it's IGNORED, mark it for stubbing # If it's blacklisted, don't process it # If it matches a simprocedure we have, replace it for reloc in obj.imports.values(): # Step 2.1: Quick filter on symbols we really don't care about func = reloc.symbol if func is None: continue if not func.is_function and func.type != cle.backends.symbol.SymbolType.TYPE_NONE: continue if func.resolvedby is None: # I don't understand the binary which made me add this case. If you are debugging and see this comment, # good luck. # ref: https://github.com/angr/angr/issues/1782 # (I also don't know why the TYPE_NONE check in the previous clause is there but I can't find a ref for # that. they are probably related.) # (I believe the TYPE_NONE check is to support ELF object files) continue if not reloc.resolved: # This is a hack, effectively to support Binary Ninja, which doesn't provide access to dependency # library names. The backend creates the Relocation objects, but leaves them unresolved so that # we can try to guess them here. if reloc.owner.guess_simprocs: l.debug( "Looking for matching SimProcedure for unresolved %s from %s with hint %s", func.name, reloc.owner, reloc.owner.guess_simprocs_hint, ) self._guess_simprocedure(func, reloc.owner.guess_simprocs_hint) else: l.debug("Ignoring unresolved import '%s' from %s ...?", func.name, reloc.owner) continue export = reloc.resolvedby if self.is_hooked(export.rebased_addr): l.debug("Already hooked %s (%s)", export.name, export.owner) continue # Step 2.2: If this function has been resolved by a static dependency, # check if we actually can and want to replace it with a SimProcedure. # We opt out of this step if it is blacklisted by ignore_functions, which # will cause it to be replaced by ReturnUnconstrained later. if export.owner is not self.loader._extern_object and export.name not in self._ignore_functions: if self._check_user_blacklists(export.name): continue owner_name = export.owner.provides if isinstance(self.loader.main_object, cle.backends.pe.PE): owner_name = owner_name.lower() if owner_name not in SIM_LIBRARIES: continue sim_lib = SIM_LIBRARIES[owner_name] if not sim_lib.has_implementation(export.name): continue l.info("Using builtin SimProcedure for %s from %s", export.name, sim_lib.name) self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch)) # Step 2.3: If 2.2 didn't work, check if the symbol wants to be resolved # by a library we already know something about. Resolve it appropriately. # Note that _check_user_blacklists also includes _ignore_functions. # An important consideration is that even if we're stubbing a function out, # we still want to try as hard as we can to figure out where it comes from # so we can get the calling convention as close to right as possible. elif reloc.resolvewith is not None and reloc.resolvewith in SIM_LIBRARIES: sim_lib = SIM_LIBRARIES[reloc.resolvewith] if self._check_user_blacklists(export.name): if not func.is_weak: l.info("Using stub SimProcedure for unresolved %s from %s", func.name, sim_lib.name) self.hook_symbol(export.rebased_addr, sim_lib.get_stub(export.name, sim_proc_arch)) else: l.info("Using builtin SimProcedure for unresolved %s from %s", export.name, sim_lib.name) self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch)) # Step 2.4: If 2.3 didn't work (the symbol didn't request a provider we know of), try # looking through each of the SimLibraries we're using to resolve unresolved # functions. If any of them know anything specifically about this function, # resolve it with that. As a final fallback, just ask any old SimLibrary # to resolve it. elif missing_libs: for sim_lib in missing_libs: if sim_lib.has_metadata(export.name): if self._check_user_blacklists(export.name): if not func.is_weak: l.info("Using stub SimProcedure for unresolved %s from %s", export.name, sim_lib.name) self.hook_symbol(export.rebased_addr, sim_lib.get_stub(export.name, sim_proc_arch)) else: l.info("Using builtin SimProcedure for unresolved %s from %s", export.name, sim_lib.name) self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch)) break else: if not func.is_weak: l.info("Using stub SimProcedure for unresolved %s", export.name) the_lib = missing_libs[0] if export.name and export.name.startswith("_Z"): # GNU C++ name. Use a C++ library to create the stub if "libstdc++.so" in SIM_LIBRARIES: the_lib = SIM_LIBRARIES["libstdc++.so"] else: l.critical( "Does not find any C++ library in SIM_LIBRARIES. We may not correctly " "create the stub or resolve the function prototype for name %s.", export.name, ) self.hook_symbol(export.rebased_addr, the_lib.get(export.name, sim_proc_arch)) # Step 2.5: If the requesting object wants us to guess simprocedures, do the guessing elif reloc.owner.guess_simprocs and self._guess_simprocedure(func, reloc.owner.guess_simprocs_hint): continue # Step 2.6: If 2.4/2.5 didn't work (we have NO SimLibraries to work with), just # use the vanilla ReturnUnconstrained, assuming that this isn't a weak func elif not func.is_weak: l.info("Using stub SimProcedure for unresolved %s", export.name) self.hook_symbol( export.rebased_addr, SIM_PROCEDURES["stubs"]["ReturnUnconstrained"](display_name=export.name, is_stub=True), ) def _guess_simprocedure(self, f, hint): """ Does symbol name `f` exist as a SIM_PROCEDURE? If so, return it, else return None. Narrows down the set of libraries to search based on hint. """ # First, filter the SIM_LIBRARIES to a reasonable subset based on the hint if hint == "win": hinted_libs = filter(lambda lib: lib if lib.endswith(".dll") else None, SIM_LIBRARIES) else: hinted_libs = filter(lambda lib: lib if ".so" in lib else None, SIM_LIBRARIES) for lib in hinted_libs: if SIM_LIBRARIES[lib].has_implementation(f.name): l.debug("Found implementation for %s in %s", f, lib) if f.resolvedby: hook_at = f.resolvedby.rebased_addr else: # ???? hook_at = f.relative_addr self.hook_symbol(hook_at, (SIM_LIBRARIES[lib].get(f.name, self.arch))) return True l.debug("Could not find matching SimProcedure for %s, ignoring.", f.name) return False def _check_user_blacklists(self, f): """ Has symbol name `f` been marked for exclusion by any of the user parameters? """ return ( not self.use_sim_procedures or f in self._exclude_sim_procedures_list or f in self._ignore_functions or (self._exclude_sim_procedures_func is not None and self._exclude_sim_procedures_func(f)) ) @staticmethod def _addr_to_str(addr): return "%s" % repr(addr) if isinstance(addr, SootAddressDescriptor) else "%#x" % addr # # Public methods # They're all related to hooking! # # pylint: disable=inconsistent-return-statements
[docs] def hook(self, addr, hook=None, length=0, kwargs=None, replace: bool | None = False): """ Hook a section of code with a custom function. This is used internally to provide symbolic summaries of library functions, and can be used to instrument execution or to modify control flow. When hook is not specified, it returns a function decorator that allows easy hooking. Usage:: # Assuming proj is an instance of angr.Project, we will add a custom hook at the entry # point of the project. @proj.hook(proj.entry) def my_hook(state): print("Welcome to execution!") :param addr: The address to hook. :param hook: A :class:`angr.project.Hook` describing a procedure to run at the given address. You may also pass in a SimProcedure class or a function directly and it will be wrapped in a Hook object for you. :param length: If you provide a function for the hook, this is the number of bytes that will be skipped by executing the hook by default. :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :param replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false (default), warn and do not replace the hook. If none, warn and replace the hook. """ if hook is None: # if we haven't been passed a thing to hook with, assume we're being used as a decorator return self._hook_decorator(addr, length=length, kwargs=kwargs) if kwargs is None: kwargs = {} l.debug("hooking %s with %s", self._addr_to_str(addr), str(hook)) if self.is_hooked(addr): if replace is True: pass elif replace is False: l.warning( "Address is already hooked, during hook(%s, %s). Not re-hooking.", self._addr_to_str(addr), hook ) return else: l.warning("Address is already hooked, during hook(%s, %s). Re-hooking.", self._addr_to_str(addr), hook) if isinstance(hook, type): raise TypeError("Please instanciate your SimProcedure before hooking with it") if callable(hook): hook = SIM_PROCEDURES["stubs"]["UserHook"](user_func=hook, length=length, **kwargs) # if not hook.is_stub and hook.guessed_prototype: # l.error( # "You need to instantiate %s with a prototype. " # "It will attempt to guess one but the consequences of guessing wrong are dire.", # hook, # ) # l.error("Consider also using angr.SIM_LIBRARIES instead of angr.SIM_PROCEDURES or angr.procedures.") self._sim_procedures[addr] = hook
[docs] def is_hooked(self, addr) -> bool: """ Returns True if `addr` is hooked. :param addr: An address. :returns: True if addr is hooked, False otherwise. """ return addr in self._sim_procedures
[docs] def hooked_by(self, addr) -> SimProcedure | None: """ Returns the current hook for `addr`. :param addr: An address. :returns: None if the address is not hooked. """ if not self.is_hooked(addr): l.warning("Address %s is not hooked", self._addr_to_str(addr)) return None return self._sim_procedures[addr]
[docs] def unhook(self, addr): """ Remove a hook. :param addr: The address of the hook. """ if not self.is_hooked(addr): l.warning("Address %s not hooked", self._addr_to_str(addr)) return del self._sim_procedures[addr]
[docs] def hook_symbol(self, symbol_name, simproc, kwargs=None, replace: bool | None = None): """ Resolve a dependency in a binary. Looks up the address of the given symbol, and then hooks that address. If the symbol was not available in the loaded libraries, this address may be provided by the CLE externs object. Additionally, if instead of a symbol name you provide an address, some secret functionality will kick in and you will probably just hook that address, UNLESS you're on powerpc64 ABIv1 or some yet-unknown scary ABI that has its function pointers point to something other than the actual functions, in which case it'll do the right thing. :param symbol_name: The name of the dependency to resolve. :param simproc: The SimProcedure instance (or function) with which to hook the symbol :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :param replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook. :returns: The address of the new symbol. :rtype: int """ if type(symbol_name) is not int: sym = self.loader.find_symbol(symbol_name) if sym is None: # it could be a previously unresolved weak symbol..? new_sym = None for reloc in self.loader.find_relevant_relocations(symbol_name): assert reloc.symbol is not None if not reloc.symbol.is_weak: raise Exception("Symbol is strong but we couldn't find its resolution? Report to @rhelmot.") if new_sym is None: new_sym = self.loader.extern_object.make_extern(symbol_name) reloc.resolve(new_sym) reloc.relocate() if new_sym is None: l.error("Could not find symbol %s", symbol_name) return None sym = new_sym basic_addr = sym.rebased_addr else: basic_addr = symbol_name symbol_name = None hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=basic_addr) self.hook(hook_addr, simproc, kwargs=kwargs, replace=replace) return hook_addr
[docs] def symbol_hooked_by(self, symbol_name) -> SimProcedure | None: """ Return the SimProcedure, if it exists, for the given symbol name. :param str symbol_name: Name of the symbol. :returns: None if the address is not hooked. """ sym = self.loader.find_symbol(symbol_name) if sym is None: l.warning("Could not find symbol %s", symbol_name) return None hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr) if not self.is_hooked(hook_addr): return None return self.hooked_by(hook_addr)
[docs] def is_symbol_hooked(self, symbol_name): """ Check if a symbol is already hooked. :param str symbol_name: Name of the symbol. :return: True if the symbol can be resolved and is hooked, False otherwise. :rtype: bool """ sym = self.loader.find_symbol(symbol_name) if sym is None: l.warning("Could not find symbol %s", symbol_name) return False hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr) return self.is_hooked(hook_addr)
[docs] def unhook_symbol(self, symbol_name): """ Remove the hook on a symbol. This function will fail if the symbol is provided by the extern object, as that would result in a state where analysis would be unable to cope with a call to this symbol. """ sym = self.loader.find_symbol(symbol_name) if sym is None: l.warning("Could not find symbol %s", symbol_name) return False if sym.owner is self.loader._extern_object: l.warning( "Refusing to unhook external symbol %s, replace it with another hook if you want to change it", symbol_name, ) return False hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr) self.unhook(hook_addr) return True
[docs] def rehook_symbol(self, new_address, symbol_name, stubs_on_sync): """ Move the hook for a symbol to a specific address :param new_address: the new address that will trigger the SimProc execution :param symbol_name: the name of the symbol (f.i. strcmp ) :return: None """ new_sim_procedures = {} for key_address, simproc_obj in self._sim_procedures.items(): # if we don't want stubs during the sync let's skip those, we will execute the real function. if not stubs_on_sync and simproc_obj.is_stub: continue if simproc_obj.display_name == symbol_name: new_sim_procedures[new_address] = simproc_obj else: new_sim_procedures[key_address] = simproc_obj self._sim_procedures = new_sim_procedures
# # A convenience API (in the style of triton and manticore) for symbolic execution. #
[docs] def execute(self, *args, **kwargs): """ This function is a symbolic execution helper in the simple style supported by triton and manticore. It designed to be run after setting up hooks (see Project.hook), in which the symbolic state can be checked. This function can be run in three different ways: - When run with no parameters, this function begins symbolic execution from the entrypoint. - It can also be run with a "state" parameter specifying a SimState to begin symbolic execution from. - Finally, it can accept any arbitrary keyword arguments, which are all passed to project.factory.full_init_state. If symbolic execution finishes, this function returns the resulting simulation manager. """ if args: state = args[0] else: state = self.factory.full_init_state(**kwargs) pg = self.factory.simulation_manager(state) self._executing = True return pg.run(until=lambda lpg: not self._executing)
[docs] def terminate_execution(self): """ Terminates a symbolic execution that was started with Project.execute(). """ self._executing = False
# # Private methods related to hooking # def _hook_decorator(self, addr, length=0, kwargs=None): """ Return a function decorator that allows easy hooking. Please refer to hook() for its usage. :return: The function decorator. """ def hook_decorator(func): self.hook(addr, func, length=length, kwargs=kwargs) return func return hook_decorator # # Pickling # def __getstate__(self): store_func, load_func = self.store_function, self.load_function try: self.store_function, self.load_function = None, None # ignore analyses. we re-initialize analyses when restoring from pickling so that we do not lose any newly # added analyses classes d = { k: v for k, v in self.__dict__.items() if k not in { "analyses", } } return d finally: self.store_function, self.load_function = store_func, load_func def __setstate__(self, s): self.__dict__.update(s) try: self._initialize_analyses_hub() except AngrNoPluginError: l.warning("Plugin preset %s does not exist any more. Fall back to the default preset.") self._analyses_preset = "default" self._initialize_analyses_hub() def _store(self, container): # If container is a filename. if isinstance(container, str): with open(container, "wb") as f: try: pickle.dump(self, f, pickle.HIGHEST_PROTOCOL) except RuntimeError as e: # maximum recursion depth can be reached here l.error("Unable to store Project: '%s' during pickling", e) # If container is an open file. elif isinstance(container, IOBase): try: pickle.dump(self, container, pickle.HIGHEST_PROTOCOL) except RuntimeError as e: # maximum recursion depth can be reached here l.error("Unable to store Project: '%s' during pickling", e) # If container is just a variable. else: try: container = pickle.dumps(self, pickle.HIGHEST_PROTOCOL) except RuntimeError as e: # maximum recursion depth can be reached here l.error("Unable to store Project: '%s' during pickling", e) @staticmethod def _load(container): if isinstance(container, str): # If container is a filename. if all(c in string.printable for c in container) and os.path.exists(container): with open(container, "rb") as f: return pickle.load(f) else: raise ValueError(container) # If container is an open file elif isinstance(container, IOBase): return pickle.load(container) # What else could it be? else: l.error("Cannot unpickle container of type %s", type(container)) return None def __repr__(self): return "<Project %s>" % (self.filename if self.filename is not None else "loaded from stream") # # Compatibility # @property @deprecated(replacement="simos") def _simos(self): return self.simos
from .factory import AngrObjectFactory from angr.simos import SimOS, os_mapping from .analyses.analysis import AnalysesHub, AnalysesHubWithDefault from .knowledge_base import KnowledgeBase from .procedures import SIM_PROCEDURES, SIM_LIBRARIES