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