/bin/truefor these examples.
archinfo.Archobject for whichever architecture the program is compiled, in this case little-endian amd64. It contains a ton of clerical data about the CPU it runs on, which you can peruse at your leisure. The common ones you care about are
arch.bytes(that one is a
@propertydeclaration on the main
.loaderproperty. We'll get into detail on how to use this soon, but for now just know that you can use it to see the shared libraries that angr loaded alongside your program and perform basic queries about the loaded address space.
project.factory, which has several convenient constructors for common objects you'll want to use frequently.
project.factory.block(), which is used to extract a basic block of code from a given address. This is an important fact - angr analyzes code in units of basic blocks. You will get back a Block object, which can tell you lots of fun things about the block of code:
Projectobject only represents an "initialization image" for the program. When you're performing execution with angr, you are working with a specific object representing a simulated program state - a
SimState. Let's grab one right now!
state.memto access the registers and memory of this state:
.lengthproperty describing how wide it is in bits.
meminterface is a little confusing at first, since it's using some pretty hefty Python magic. The short version of how to use it is:
.<type>to specify that the memory should be interpreted as <type> (common values: char, short, int, long, size_t, uint8_t, uint16_t...)
.resolvedto get the value as a bitvector
.concreteto get the value as a Python int
active, is initialized with the state we passed in. We could look at
simgr.activeto look at our state some more, if we haven't had enough!
/bin/trueisn't a very good example for describing how to do interesting things with symbolic execution, so we'll stop here for now.