1import logging
2import os
3import types
4from io import BytesIO, IOBase
5import pickle
6import string
7from collections import defaultdict
8from typing import Dict, Any
9
10import archinfo
11from archinfo.arch_soot import SootAddressDescriptor, ArchSoot
12import cle
13
14from .misc.ux import deprecated
15from .errors import AngrNoPluginError
16
17l = logging.getLogger(name=__name__)
18
19def load_shellcode(shellcode, arch, start_offset=0, load_address=0, thumb=False, **kwargs):
20    """
21    Load a new project based on a snippet of assembly or bytecode.
22
23    :param shellcode:       The data to load, as either a bytestring of instructions or a string of assembly text
24    :param arch:            The name of the arch to use, or an archinfo class
25    :param start_offset:    The offset into the data to start analysis (default 0)
26    :param load_address:    The address to place the data in memory (default 0)
27    :param thumb:           Whether this is ARM Thumb shellcode
28    """
29    if not isinstance(arch, archinfo.Arch):
30        arch = archinfo.arch_from_id(arch)
31    if type(shellcode) is str:
32        shellcode = arch.asm(shellcode, load_address, thumb=thumb)
33    if thumb:
34        start_offset |= 1
35
36    return Project(
37            BytesIO(shellcode),
38            main_opts={
39                'backend': 'blob',
40                'arch': arch,
41                'entry_point': start_offset,
42                'base_addr': load_address,
43            },
44        **kwargs
45        )
46
47
48class Project:
49    """
50    This is the main class of the angr module. It is meant to contain a set of binaries and the relationships between
51    them, and perform analyses on them.
52
53    :param thing:                       The path to the main executable object to analyze, or a CLE Loader object.
54
55    The following parameters are optional.
56
57    :param default_analysis_mode:       The mode of analysis to use by default. Defaults to 'symbolic'.
58    :param ignore_functions:            A list of function names that, when imported from shared libraries, should
59                                        never be stepped into in analysis (calls will return an unconstrained value).
60    :param use_sim_procedures:          Whether to replace resolved dependencies for which simprocedures are
61                                        available with said simprocedures.
62    :param exclude_sim_procedures_func: A function that, when passed a function name, returns whether or not to wrap
63                                        it with a simprocedure.
64    :param exclude_sim_procedures_list: A list of functions to *not* wrap with simprocedures.
65    :param arch:                        The target architecture (auto-detected otherwise).
66    :param simos:                       a SimOS class to use for this project.
67    :param engine:                      The SimEngine class to use for this project.
68    :param bool translation_cache:      If True, cache translated basic blocks rather than re-translating them.
69    :param support_selfmodifying_code:  Whether we aggressively support self-modifying code. When enabled, emulation
70                                        will try to read code from the current state instead of the original memory,
71                                        regardless of the current memory protections.
72    :type support_selfmodifying_code:   bool
73    :param store_function:              A function that defines how the Project should be stored. Default to pickling.
74    :param load_function:               A function that defines how the Project should be loaded. Default to unpickling.
75    :param analyses_preset:             The plugin preset for the analyses provider (i.e. Analyses instance).
76    :type analyses_preset:              angr.misc.PluginPreset
77
78    Any additional keyword arguments passed will be passed onto ``cle.Loader``.
79
80    :ivar analyses:     The available analyses.
81    :type analyses:     angr.analysis.Analyses
82    :ivar entry:        The program entrypoint.
83    :ivar factory:      Provides access to important analysis elements such as path groups and symbolic execution results.
84    :type factory:      AngrObjectFactory
85    :ivar filename:     The filename of the executable.
86    :ivar loader:       The program loader.
87    :type loader:       cle.Loader
88    :ivar storage:      Dictionary of things that should be loaded/stored with the Project.
89    :type storage:      defaultdict(list)
90    """
91
92    def __init__(self, thing,
93                 default_analysis_mode=None,
94                 ignore_functions=None,
95                 use_sim_procedures=True,
96                 exclude_sim_procedures_func=None,
97                 exclude_sim_procedures_list=(),
98                 arch=None, simos=None,
99                 engine=None,
100                 load_options: Dict[str, Any]=None,
101                 translation_cache=True,
102                 support_selfmodifying_code=False,
103                 store_function=None,
104                 load_function=None,
105                 analyses_preset=None,
106                 concrete_target=None,
107                 **kwargs):
108
109        # Step 1: Load the binary
110
111        if load_options is None: load_options = {}
112
113        load_options.update(kwargs)
114        if arch is not None:
115            load_options.update({'arch': arch})
116        if isinstance(thing, cle.Loader):
117            if load_options:
118                l.warning("You provided CLE options to angr but you also provided a completed cle.Loader object!")
119            self.loader = thing
120            self.filename = self.loader.main_object.binary
121        elif hasattr(thing, 'read') and hasattr(thing, 'seek'):
122            l.info("Loading binary from stream")
123            self.filename = None
124            self.loader = cle.Loader(thing, **load_options)
125        elif not isinstance(thing, str) or not os.path.exists(thing) or not os.path.isfile(thing):
126            raise Exception("Not a valid binary file: %s" % repr(thing))
127        else:
128            # use angr's loader, provided by cle
129            l.info("Loading binary %s", thing)
130            self.filename = thing
131            self.loader = cle.Loader(self.filename, concrete_target=concrete_target, **load_options)
132
133        # Step 2: determine its CPU architecture, ideally falling back to CLE's guess
134        if isinstance(arch, str):
135            self.arch = archinfo.arch_from_id(arch)  # may raise ArchError, let the user see this
136        elif isinstance(arch, archinfo.Arch):
137            self.arch = arch # type: archinfo.Arch
138        elif arch is None:
139            self.arch = self.loader.main_object.arch
140        else:
141            raise ValueError("Invalid arch specification.")
142        # Step 3: Set some defaults and set the public and private properties
143        if not default_analysis_mode:
144            default_analysis_mode = 'symbolic'
145        if not ignore_functions:
146            ignore_functions = []
147
148        if isinstance(exclude_sim_procedures_func, types.LambdaType):
149            l.warning("Passing a lambda type as the exclude_sim_procedures_func argument to "
150                      "Project causes the resulting object to be un-serializable.")
151
152        self._sim_procedures = {}
153
154        self.concrete_target = concrete_target
155
156        # It doesn't make any sense to have auto_load_libs
157        # if you have the concrete target, let's warn the user about this.
158        if self.concrete_target and load_options.get('auto_load_libs', None):
159
160            l.critical("Incompatible options selected for this project, please disable auto_load_libs if "
161                       "you want to use a concrete target.")
162            raise Exception("Incompatible options for the project")
163
164        if self.concrete_target and self.arch.name not in ['X86', 'AMD64', 'ARMHF']:
165            l.critical("Concrete execution does not support yet the selected architecture. Aborting.")
166            raise Exception("Incompatible options for the project")
167
168        self._default_analysis_mode = default_analysis_mode
169        self._exclude_sim_procedures_func = exclude_sim_procedures_func
170        self._exclude_sim_procedures_list = exclude_sim_procedures_list
171        self.use_sim_procedures = use_sim_procedures
172        self._ignore_functions = ignore_functions
173        self._support_selfmodifying_code = support_selfmodifying_code
174        self._translation_cache = translation_cache
175        self._executing = False # this is a flag for the convenience API, exec() and terminate_execution() below
176
177        if self._support_selfmodifying_code:
178            if self._translation_cache is True:
179                self._translation_cache = False
180                l.warning("Disabling IRSB translation cache because support for self-modifying code is enabled.")
181
182        self.entry = self.loader.main_object.entry
183        self.storage = defaultdict(list)
184        self.store_function = store_function or self._store
185        self.load_function = load_function or self._load
186
187        # Step 4: Set up the project's hubs
188        # Step 4.1 Factory
189        self.factory = AngrObjectFactory(self, default_engine=engine)
190
191        # Step 4.2: Analyses
192        self._analyses_preset = analyses_preset
193        self.analyses = None
194        self._initialize_analyses_hub()
195
196        # Step 4.3: ...etc
197        self.kb = KnowledgeBase(self, name="global")
198
199        # Step 5: determine the guest OS
200        if isinstance(simos, type) and issubclass(simos, SimOS):
201            self.simos = simos(self) #pylint:disable=invalid-name
202        elif isinstance(simos, str):
203            self.simos = os_mapping[simos](self)
204        elif simos is None:
205            self.simos = os_mapping[self.loader.main_object.os](self)
206        else:
207            raise ValueError("Invalid OS specification or non-matching architecture.")
208
209        self.is_java_project = isinstance(self.arch, ArchSoot)
210        self.is_java_jni_project = isinstance(self.arch, ArchSoot) and self.simos.is_javavm_with_jni_support
211
212        # Step 6: Register simprocedures as appropriate for library functions
213        if isinstance(self.arch, ArchSoot) and self.simos.is_javavm_with_jni_support:
214            # If we execute a Java archive that includes native JNI libraries,
215            # we need to use the arch of the native simos for all (native) sim
216            # procedures.
217            sim_proc_arch = self.simos.native_arch
218        else:
219            sim_proc_arch = self.arch
220        for obj in self.loader.initial_load_objects:
221            self._register_object(obj, sim_proc_arch)
222
223        # Step 7: Run OS-specific configuration
224        self.simos.configure_project()
225
226    def _initialize_analyses_hub(self):
227        """
228        Initializes self.analyses using a given preset.
229        """
230        self.analyses = AnalysesHub(self)
231        self.analyses.use_plugin_preset(self._analyses_preset if self._analyses_preset is not None else 'default')
232
233    def _register_object(self, obj, sim_proc_arch):
234        """
235        This scans through an objects imports and hooks them with simprocedures from our library whenever possible
236        """
237
238        # Step 1: get the set of libraries we are allowed to use to resolve unresolved symbols
239        missing_libs = []
240        for lib_name in self.loader.missing_dependencies:
241            try:
242                missing_libs.append(SIM_LIBRARIES[lib_name])
243            except KeyError:
244                l.info("There are no simprocedures for missing library %s :(", lib_name)
245        # additionally provide libraries we _have_ loaded as a fallback fallback
246        # this helps in the case that e.g. CLE picked up a linux arm libc to satisfy an android arm binary
247        for lib in self.loader.all_objects:
248            if lib.provides in SIM_LIBRARIES:
249                simlib = SIM_LIBRARIES[lib.provides]
250                if simlib not in missing_libs:
251                    missing_libs.append(simlib)
252
253        # Step 2: Categorize every "import" symbol in each object.
254        # If it's IGNORED, mark it for stubbing
255        # If it's blacklisted, don't process it
256        # If it matches a simprocedure we have, replace it
257        for reloc in obj.imports.values():
258            # Step 2.1: Quick filter on symbols we really don't care about
259            func = reloc.symbol
260            if func is None:
261                continue
262            if not func.is_function and func.type != cle.backends.symbol.SymbolType.TYPE_NONE:
263                continue
264            if func.resolvedby is None:
265                # I don't understand the binary which made me add this case. If you are debugging and see this comment,
266                # good luck.
267                # ref: https://github.com/angr/angr/issues/1782
268                # (I also don't know why the TYPE_NONE check in the previous clause is there but I can't find a ref for
269                # that. they are probably related.)
270                continue
271            if not reloc.resolved:
272                # This is a hack, effectively to support Binary Ninja, which doesn't provide access to dependency
273                # library names. The backend creates the Relocation objects, but leaves them unresolved so that
274                # we can try to guess them here. Once the Binary Ninja API starts supplying the dependencies,
275                # The if/else, along with Project._guess_simprocedure() can be removed if it has no other utility,
276                # just leave behind the 'unresolved' debug statement from the else clause.
277                if reloc.owner.guess_simprocs:
278                    l.debug("Looking for matching SimProcedure for unresolved %s from %s with hint %s",
279                            func.name, reloc.owner, reloc.owner.guess_simprocs_hint)
280                    self._guess_simprocedure(func, reloc.owner.guess_simprocs_hint)
281                else:
282                    l.debug("Ignoring unresolved import '%s' from %s ...?", func.name, reloc.owner)
283                continue
284            export = reloc.resolvedby
285            if self.is_hooked(export.rebased_addr):
286                l.debug("Already hooked %s (%s)", export.name, export.owner)
287                continue
288
289            # Step 2.2: If this function has been resolved by a static dependency,
290            # check if we actually can and want to replace it with a SimProcedure.
291            # We opt out of this step if it is blacklisted by ignore_functions, which
292            # will cause it to be replaced by ReturnUnconstrained later.
293            if export.owner is not self.loader._extern_object and \
294                    export.name not in self._ignore_functions:
295                if self._check_user_blacklists(export.name):
296                    continue
297                owner_name = export.owner.provides
298                if isinstance(self.loader.main_object, cle.backends.pe.PE):
299                    owner_name = owner_name.lower()
300                if owner_name not in SIM_LIBRARIES:
301                    continue
302                sim_lib = SIM_LIBRARIES[owner_name]
303                if not sim_lib.has_implementation(export.name):
304                    continue
305                l.info("Using builtin SimProcedure for %s from %s", export.name, sim_lib.name)
306                self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch))
307
308            # Step 2.3: If 2.2 didn't work, check if the symbol wants to be resolved
309            # by a library we already know something about. Resolve it appropriately.
310            # Note that _check_user_blacklists also includes _ignore_functions.
311            # An important consideration is that even if we're stubbing a function out,
312            # we still want to try as hard as we can to figure out where it comes from
313            # so we can get the calling convention as close to right as possible.
314            elif reloc.resolvewith is not None and reloc.resolvewith in SIM_LIBRARIES:
315                sim_lib = SIM_LIBRARIES[reloc.resolvewith]
316                if self._check_user_blacklists(export.name):
317                    if not func.is_weak:
318                        l.info("Using stub SimProcedure for unresolved %s from %s", func.name, sim_lib.name)
319                        self.hook_symbol(export.rebased_addr, sim_lib.get_stub(export.name, sim_proc_arch))
320                else:
321                    l.info("Using builtin SimProcedure for unresolved %s from %s", export.name, sim_lib.name)
322                    self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch))
323
324            # Step 2.4: If 2.3 didn't work (the symbol didn't request a provider we know of), try
325            # looking through each of the SimLibraries we're using to resolve unresolved
326            # functions. If any of them know anything specifically about this function,
327            # resolve it with that. As a final fallback, just ask any old SimLibrary
328            # to resolve it.
329            elif missing_libs:
330                for sim_lib in missing_libs:
331                    if sim_lib.has_metadata(export.name):
332                        if self._check_user_blacklists(export.name):
333                            if not func.is_weak:
334                                l.info("Using stub SimProcedure for unresolved %s from %s", export.name, sim_lib.name)
335                                self.hook_symbol(export.rebased_addr, sim_lib.get_stub(export.name, sim_proc_arch))
336                        else:
337                            l.info("Using builtin SimProcedure for unresolved %s from %s", export.name, sim_lib.name)
338                            self.hook_symbol(export.rebased_addr, sim_lib.get(export.name, sim_proc_arch))
339                        break
340                else:
341                    if not func.is_weak:
342                        l.info("Using stub SimProcedure for unresolved %s", export.name)
343                        the_lib = missing_libs[0]
344                        if export.name and export.name.startswith("_Z"):
345                            # GNU C++ name. Use a C++ library to create the stub
346                            if 'libstdc++.so' in SIM_LIBRARIES:
347                                the_lib = SIM_LIBRARIES['libstdc++.so']
348                            else:
349                                l.critical("Does not find any C++ library in SIM_LIBRARIES. We may not correctly "
350                                           "create the stub or resolve the function prototype for name %s.", export.name)
351
352                        self.hook_symbol(export.rebased_addr, the_lib.get(export.name, sim_proc_arch))
353
354            # Step 2.5: If 2.4 didn't work (we have NO SimLibraries to work with), just
355            # use the vanilla ReturnUnconstrained, assuming that this isn't a weak func
356            elif not func.is_weak:
357                l.info("Using stub SimProcedure for unresolved %s", export.name)
358                self.hook_symbol(export.rebased_addr, SIM_PROCEDURES['stubs']['ReturnUnconstrained'](display_name=export.name, is_stub=True))
359
360    def _guess_simprocedure(self, f, hint):
361        """
362        Does symbol name `f` exist as a SIM_PROCEDURE? If so, return it, else return None.
363        Narrows down the set of libraries to search based on hint.
364        Part of the hack to enable Binary Ninja support. Remove if _register_objects() stops using it.
365        """
366        # First, filter the SIM_LIBRARIES to a reasonable subset based on the hint
367        hinted_libs = []
368        if hint == "win":
369            hinted_libs = filter(lambda lib: lib if lib.endswith(".dll") else None, SIM_LIBRARIES)
370        else:
371            hinted_libs = filter(lambda lib: lib if ".so" in lib else None, SIM_LIBRARIES)
372
373        for lib in hinted_libs:
374            if SIM_LIBRARIES[lib].has_implementation(f.name):
375                l.debug("Found implementation for %s in %s", f, lib)
376                self.hook_symbol(f.relative_addr, (SIM_LIBRARIES[lib].get(f.name, self.arch)))
377                break
378        else:
379            l.debug("Could not find matching SimProcedure for %s, ignoring.", f.name)
380
381    def _check_user_blacklists(self, f):
382        """
383        Has symbol name `f` been marked for exclusion by any of the user
384        parameters?
385        """
386        return not self.use_sim_procedures or \
387            f in self._exclude_sim_procedures_list or \
388            f in self._ignore_functions or \
389            (self._exclude_sim_procedures_func is not None and self._exclude_sim_procedures_func(f))
390
391
392    @staticmethod
393    def _addr_to_str(addr):
394        return "%s" % repr(addr) if isinstance(addr, SootAddressDescriptor) else "%#x" % addr
395
396
397    #
398    # Public methods
399    # They're all related to hooking!
400    #
401
402    # pylint: disable=inconsistent-return-statements
403    def hook(self, addr, hook=None, length=0, kwargs=None, replace=False):
404        """
405        Hook a section of code with a custom function. This is used internally to provide symbolic
406        summaries of library functions, and can be used to instrument execution or to modify
407        control flow.
408
409        When hook is not specified, it returns a function decorator that allows easy hooking.
410        Usage::
411
412            # Assuming proj is an instance of angr.Project, we will add a custom hook at the entry
413            # point of the project.
414            @proj.hook(proj.entry)
415            def my_hook(state):
416                print("Welcome to execution!")
417
418        :param addr:        The address to hook.
419        :param hook:        A :class:`angr.project.Hook` describing a procedure to run at the
420                            given address. You may also pass in a SimProcedure class or a function
421                            directly and it will be wrapped in a Hook object for you.
422        :param length:      If you provide a function for the hook, this is the number of bytes
423                            that will be skipped by executing the hook by default.
424        :param kwargs:      If you provide a SimProcedure for the hook, these are the keyword
425                            arguments that will be passed to the procedure's `run` method
426                            eventually.
427        :param replace:     Control the behavior on finding that the address is already hooked. If
428                            true, silently replace the hook. If false (default), warn and do not
429                            replace the hook. If none, warn and replace the hook.
430        """
431        if hook is None:
432            # if we haven't been passed a thing to hook with, assume we're being used as a decorator
433            return self._hook_decorator(addr, length=length, kwargs=kwargs)
434
435        if kwargs is None: kwargs = {}
436
437        l.debug('hooking %s with %s', self._addr_to_str(addr), str(hook))
438
439        if self.is_hooked(addr):
440            if replace is True:
441                pass
442            elif replace is False:
443                l.warning("Address is already hooked, during hook(%s, %s). Not re-hooking.", self._addr_to_str(addr), hook)
444                return
445            else:
446                l.warning("Address is already hooked, during hook(%s, %s). Re-hooking.", self._addr_to_str(addr), hook)
447
448        if isinstance(hook, type):
449            raise TypeError("Please instanciate your SimProcedure before hooking with it")
450
451        if callable(hook):
452            hook = SIM_PROCEDURES['stubs']['UserHook'](user_func=hook, length=length, **kwargs)
453
454        self._sim_procedures[addr] = hook
455
456    def is_hooked(self, addr):
457        """
458        Returns True if `addr` is hooked.
459
460        :param addr: An address.
461        :returns:    True if addr is hooked, False otherwise.
462        """
463        return addr in self._sim_procedures
464
465    def hooked_by(self, addr):
466        """
467        Returns the current hook for `addr`.
468
469        :param addr: An address.
470
471        :returns:    None if the address is not hooked.
472        """
473
474        if not self.is_hooked(addr):
475            l.warning("Address %s is not hooked", self._addr_to_str(addr))
476            return None
477
478        return self._sim_procedures[addr]
479
480    def unhook(self, addr):
481        """
482        Remove a hook.
483
484        :param addr:    The address of the hook.
485        """
486        if not self.is_hooked(addr):
487            l.warning("Address %s not hooked", self._addr_to_str(addr))
488            return
489
490        del self._sim_procedures[addr]
491
492    def hook_symbol(self, symbol_name, simproc, kwargs=None, replace=None):
493        """
494        Resolve a dependency in a binary. Looks up the address of the given symbol, and then hooks that
495        address. If the symbol was not available in the loaded libraries, this address may be provided
496        by the CLE externs object.
497
498        Additionally, if instead of a symbol name you provide an address, some secret functionality will
499        kick in and you will probably just hook that address, UNLESS you're on powerpc64 ABIv1 or some
500        yet-unknown scary ABI that has its function pointers point to something other than the actual
501        functions, in which case it'll do the right thing.
502
503        :param symbol_name: The name of the dependency to resolve.
504        :param simproc:     The SimProcedure instance (or function) with which to hook the symbol
505        :param kwargs:      If you provide a SimProcedure for the hook, these are the keyword
506                            arguments that will be passed to the procedure's `run` method
507                            eventually.
508        :param replace:     Control the behavior on finding that the address is already hooked. If
509                            true, silently replace the hook. If false, warn and do not replace the
510                            hook. If none (default), warn and replace the hook.
511        :returns:           The address of the new symbol.
512        :rtype:             int
513        """
514        if type(symbol_name) is not int:
515            sym = self.loader.find_symbol(symbol_name)
516            if sym is None:
517                # it could be a previously unresolved weak symbol..?
518                new_sym = None
519                for reloc in self.loader.find_relevant_relocations(symbol_name):
520                    if not reloc.symbol.is_weak:
521                        raise Exception("Symbol is strong but we couldn't find its resolution? Report to @rhelmot.")
522                    if new_sym is None:
523                        new_sym = self.loader.extern_object.make_extern(symbol_name)
524                    reloc.resolve(new_sym)
525                    reloc.relocate([])
526
527                if new_sym is None:
528                    l.error("Could not find symbol %s", symbol_name)
529                    return None
530                sym = new_sym
531
532            basic_addr = sym.rebased_addr
533        else:
534            basic_addr = symbol_name
535            symbol_name = None
536
537        hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=basic_addr)
538
539        self.hook(hook_addr, simproc, kwargs=kwargs, replace=replace)
540        return hook_addr
541
542    def is_symbol_hooked(self, symbol_name):
543        """
544        Check if a symbol is already hooked.
545
546        :param str symbol_name: Name of the symbol.
547        :return: True if the symbol can be resolved and is hooked, False otherwise.
548        :rtype: bool
549        """
550        sym = self.loader.find_symbol(symbol_name)
551        if sym is None:
552            l.warning("Could not find symbol %s", symbol_name)
553            return False
554        hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr)
555        return self.is_hooked(hook_addr)
556
557    def unhook_symbol(self, symbol_name):
558        """
559        Remove the hook on a symbol.
560        This function will fail if the symbol is provided by the extern object, as that would result in a state where
561        analysis would be unable to cope with a call to this symbol.
562        """
563        sym = self.loader.find_symbol(symbol_name)
564        if sym is None:
565            l.warning("Could not find symbol %s", symbol_name)
566            return False
567        if sym.owner is self.loader._extern_object:
568            l.warning("Refusing to unhook external symbol %s, replace it with another hook if you want to change it",
569                      symbol_name)
570            return False
571
572        hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr)
573        self.unhook(hook_addr)
574        return True
575
576    def rehook_symbol(self, new_address, symbol_name, stubs_on_sync):
577        """
578        Move the hook for a symbol to a specific address
579        :param new_address: the new address that will trigger the SimProc execution
580        :param symbol_name: the name of the symbol (f.i. strcmp )
581        :return: None
582        """
583        new_sim_procedures = {}
584        for key_address, simproc_obj in self._sim_procedures.items():
585
586            # if we don't want stubs during the sync let's skip those, we will execute the real function.
587            if not stubs_on_sync and simproc_obj.is_stub:
588                continue
589
590            if simproc_obj.display_name == symbol_name:
591                new_sim_procedures[new_address] = simproc_obj
592            else:
593                new_sim_procedures[key_address] = simproc_obj
594
595        self._sim_procedures = new_sim_procedures
596
597    #
598    # A convenience API (in the style of triton and manticore) for symbolic execution.
599    #
600
601    def execute(self, *args, **kwargs):
602        """
603        This function is a symbolic execution helper in the simple style
604        supported by triton and manticore. It designed to be run after
605        setting up hooks (see Project.hook), in which the symbolic state
606        can be checked.
607
608        This function can be run in three different ways:
609
610            - When run with no parameters, this function begins symbolic execution
611              from the entrypoint.
612            - It can also be run with a "state" parameter specifying a SimState to
613              begin symbolic execution from.
614            - Finally, it can accept any arbitrary keyword arguments, which are all
615              passed to project.factory.full_init_state.
616
617        If symbolic execution finishes, this function returns the resulting
618        simulation manager.
619        """
620
621        if args:
622            state = args[0]
623        else:
624            state = self.factory.full_init_state(**kwargs)
625
626        pg = self.factory.simulation_manager(state)
627        self._executing = True
628        return pg.run(until=lambda lpg: not self._executing)
629
630    def terminate_execution(self):
631        """
632        Terminates a symbolic execution that was started with Project.execute().
633        """
634        self._executing = False
635
636    #
637    # Private methods related to hooking
638    #
639
640    def _hook_decorator(self, addr, length=0, kwargs=None):
641        """
642        Return a function decorator that allows easy hooking. Please refer to hook() for its usage.
643
644        :return: The function decorator.
645        """
646
647        def hook_decorator(func):
648            self.hook(addr, func, length=length, kwargs=kwargs)
649            return func
650
651        return hook_decorator
652
653    #
654    # Pickling
655    #
656
657    def __getstate__(self):
658        try:
659            store_func, load_func = self.store_function, self.load_function
660            self.store_function, self.load_function = None, None
661            # ignore analyses. we re-initialize analyses when restoring from pickling so that we do not lose any newly
662            # added analyses classes
663            d = dict((k, v) for k, v in self.__dict__.items() if k not in {'analyses', })
664            return d
665        finally:
666            self.store_function, self.load_function = store_func, load_func
667
668    def __setstate__(self, s):
669        self.__dict__.update(s)
670        try:
671            self._initialize_analyses_hub()
672        except AngrNoPluginError:
673            l.warning("Plugin preset %s does not exist any more. Fall back to the default preset.")
674            self._analyses_preset = 'default'
675            self._initialize_analyses_hub()
676
677    def _store(self, container):
678        # If container is a filename.
679        if isinstance(container, str):
680            with open(container, 'wb') as f:
681                try:
682                    pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
683                except RuntimeError as e: # maximum recursion depth can be reached here
684                    l.error("Unable to store Project: '%s' during pickling", e)
685
686        # If container is an open file.
687        elif isinstance(container, IOBase):
688            try:
689                pickle.dump(self, container, pickle.HIGHEST_PROTOCOL)
690            except RuntimeError as e: # maximum recursion depth can be reached here
691                l.error("Unable to store Project: '%s' during pickling", e)
692
693        # If container is just a variable.
694        else:
695            try:
696                container = pickle.dumps(self, pickle.HIGHEST_PROTOCOL)
697            except RuntimeError as e: # maximum recursion depth can be reached here
698                l.error("Unable to store Project: '%s' during pickling", e)
699
700    @staticmethod
701    def _load(container):
702        if isinstance(container, str):
703            # If container is a filename.
704            if all(c in string.printable for c in container) and os.path.exists(container):
705                with open(container, 'rb') as f:
706                    return pickle.load(f)
707
708            # If container is a pickle string.
709            else:
710                return pickle.loads(container)
711
712        # If container is an open file
713        elif isinstance(container, IOBase):
714            return pickle.load(container)
715
716        # What else could it be?
717        else:
718            l.error("Cannot unpickle container of type %s", type(container))
719            return None
720
721    def __repr__(self):
722        return '<Project %s>' % (self.filename if self.filename is not None else 'loaded from stream')
723
724    #
725    # Compatibility
726    #
727
728    @property
729    @deprecated(replacement='simos')
730    def _simos(self):
731        return self.simos
732
733
734from .factory import AngrObjectFactory
735from angr.simos import SimOS, os_mapping
736from .analyses.analysis import AnalysesHub
737from .knowledge_base import KnowledgeBase
738from .procedures import SIM_PROCEDURES, SIM_LIBRARIES
739