/bin/true, and then loaded it again without its shared libraries. You also saw
proj.loaderand a few things it could do. Now, we'll dive into the nuances of these interfaces and the things they can tell you.
examples/fauxware/fauxwareand take a deeper look at how to interact with the loader.
cle.Loader) represents an entire conglomerate of loaded binary objects, loaded and mapped into a single memory space. Each binary object is loaded by a loader backend that can handle its filetype (a subclass of
cle.Backend). For example,
cle.ELFis used to load ELF binaries.
loader.all_objects, as well as several more targeted classifications:
loader.find_symbol, which takes either a name or an address and returns a Symbol object.
.rebased_addris its address in the global address space. This is what is shown in the print output.
.linked_addris its address relative to the prelinked base of the binary. This is the address reported in, for example,
.relative_addris its address relative to the object base. This is known in the literature (particularly the Windows literature) as an RVA (relative virtual address).
obj.relocs, or just a mapping from symbol name to Relocation as
obj.imports. There is no corresponding list of export symbols.
.symbol. The address the relocation will write to is accessable through any of the address identifiers you can use for Symbol, and you can get a reference to the object requesting the relocation with
loader.extern_obj) to claim it provides the symbol as an export.
angr.Projectand you want to pass an option to the
cle.Loaderinstance that Project implicitly creates, you can just pass the keyword argument directly to the Project constructor, and it will be passed on to CLE. You should look at the CLE API docs. if you want to know everything that could possibly be passed in as an option, but we will go over some important and frequently used options here.
auto_load_libsalready - it enables or disables CLE's attempt to automatically resolve shared library dependencies, and is on by default. Additionally, there is the opposite,
except_missing_libs, which, if set to true, will cause an exception to be thrown whenever a binary has a shared library dependency that cannot be resolved.
force_load_libsand anything listed will be treated as an unresolved shared library dependency right out of the gate, or you can pass a list of strings to
skip_libsto prevent any library of that name from being resolved as a dependency. Additionally, you can pass a list of strings (or a single string) to
ld_path, which will be used as an additional search path for shared libraries, before any of the defaults: the same directory as the loaded program, the current working directory, and your system libraries.
lib_optsdo this by taking dictionaries of options.
main_optsis a mapping from option names to option values, while
lib_optsis a mapping from library name to dictionaries mapping option names to option values.
backend- which backend to use, as either a class or a name
base_addr- a base address to use
entry_point- an entry point to use
arch- the name of an architecture to use
archspecified. The key doesn't need to match any list of architectures; angr will identify which architecture you mean given almost any common identifier for any supported arch.
angr.SIM_PROCEDURESdictionary, which is two-leveled, keyed first on the package name (libc, posix, win32, stubs) and then on the name of the library function. Executing a SimProcedure instead of the actual library function that gets loaded from your system makes analysis a LOT more tractable, at the cost of some potential inaccuracies.
True(this is the default), then the real library function is executed instead. This may or may not be what you want, depending on the actual function. For example, some of libc's functions are extremely complex to analyze and will most likely cause an explosion of the number of states for the path trying to execute them.
False, then external functions are unresolved, and Project will resolve them to a generic "stub" SimProcedure called
ReturnUnconstrained. It does what its name says: it returns a unique unconstrained symbolic value each time it is called.
use_sim_procedures(this is a parameter to
Trueby default), then only symbols provided by the extern object will be replaced with SimProcedures, and they will be replaced by a stub
ReturnUnconstrained, which does nothing but return a symbolic value.
angr.Project._register_objectfor the exact algorithm.
proj.hook(addr, hook), where
hookis a SimProcedure instance. You can manage your project's hooks with
.hooked_by, which should hopefully not require explanation.
proj.hook(addr)as a function decorator. If you do this, you can also optionally specify a
lengthkeyword argument to make execution jump some number of bytes forward after your hook finishes.
proj.hook_symbol(name, hook), providing the name of a symbol as the first argument, to hook the address where the symbol lives. One very important usage of this is to extend the behavior of angr's built-in library SimProcedures. Since these library functions are just classes, you can subclass them, overriding pieces of their behavior, and then use your subclass in a hook.