SimEngine
class) to emulate the effects that of a given section of code has on an input state. The execution core of angr simply tries all the available engines in sequence, taking the first one that is able to handle the step. The following is the default list of engines, in order:UNICORN
state option is enabled and there is no symbolic data in the stateproject.factory.successors(state, **kwargs)
, which passes its arguments onto each of the engines. This function is at the heart of state.step()
and simulation_manager.step()
. It returns a SimSuccessors object, which we discussed briefly before. The purpose of SimSuccessors is to perform a simple categorization of the successor states, stored in various list attributes. They are:successors
unconstrained_successors
).unsat_successors
flat_successors
successors
list can have symbolic instruction pointers. This is rather confusing, as elsewhere in the code (i.e., in SimEngineVEX.process
, when it's time to step that state forward), we make assumptions that a single program state only represents the execution of a single spot in the code. To alleviate this, when we encounter states in successors
with symbolic instruction pointers, we compute all possible concrete solutions (up to an arbitrary threshold of 256) for them, and make a copy of the state for each such solution. We call this process "flattening". These flat_successors
are states, each of which has a different, concrete instruction pointer. For example, if the instruction pointer of a state in successors
was X+5
, where X
had constraints of X > 0x800000
and X <= 0x800010
, we would flatten it into 16 different flat_successors
states, one with an instruction pointer of 0x800006
, one with 0x800007
, and so on until 0x800015
.unconstrained_successors
unconstrained_successors
and not in successors
.all_successors
successors + unsat_successors + unconstrained_successors
.state.inspect
during the appropriate breakpoint callback to access the appropriate values. You can even modify these value to modify further uses of the values!inspect.b
to make the breakpoint conditional:mem_read
breakpointmem_read
breakpoint gets triggered anytime there are memory reads by either the executing program or the binary analysis. If you are using breakpoint on mem_read
and also using state.mem
to load data from memory addresses, then know that the breakpoint will be fired as you are technically reading memory.mem_read
breakpoint you have had set up, then use state.memory.load
with the keyword arguments disable_actions=True
and inspect=False
.state.find
and you can use the same keyword arguments to prevent mem_read
breakpoints from firing.