Use this file to discover all available pages before exploring further.
angr provides two complementary approaches to control-flow graph (CFG) recovery: CFGFast for fast static analysis and CFGEmulated for precise dynamic analysis through symbolic execution.
# Get the NetworkX graphgraph = cfg.graph# Get any node at an addressnode = cfg.model.get_any_node(0x400000)# Get all nodes at an address (multiple contexts)nodes = cfg.model.get_all_nodes(0x400000)# Iterate over nodesfor node in graph.nodes(): print(f"Block at {node.addr:#x}, size {node.size}")# Get successors and predecessorsfor succ in graph.successors(node): print(f"Successor: {succ.addr:#x}")for pred in graph.predecessors(node): print(f"Predecessor: {pred.addr:#x}")# Get edges with jump kindsfor src, dst, data in graph.edges(data=True): jumpkind = data.get('jumpkind', 'Ijk_Boring') print(f"{src.addr:#x} --[{jumpkind}]--> {dst.addr:#x}")
# Generate dynamic CFGcfg = p.analyses.CFGEmulated( # Keep states at each node (memory intensive) keep_state=True, # Context sensitivity level (0-infinity) context_sensitivity_level=1, # Starting addresses starts=[p.entry],)# Access states if keep_state=Truefor node in cfg.graph.nodes(): if node.state is not None: print(f"State at {node.addr:#x}")
Both CFG analyses populate the Function Manager in the knowledge base:
# Access functionsfuncs = cfg.kb.functions# Get function by addressmain = funcs[p.entry]# Function propertiesprint(f"Name: {main.name}")print(f"Returns: {main.returning}")print(f"Blocks: {len(main.block_addrs)}")print(f"Calls: {len(main.get_call_sites())}")# String referencesfor addr, string in main.string_references(): print(f"String at {addr:#x}: {string}")# Iterate blocksfor block in main.blocks: print(f"Block at {block.addr:#x}:") block.pp() # Pretty print# Get call targetsfor callsite in main.get_call_sites(): target = main.get_call_target(callsite) print(f"Call at {callsite:#x} -> {target:#x}")# Function transition graphfunc_graph = main.transition_graphfor node in func_graph.nodes(): print(f"Node in function: {node.addr:#x}")
angr includes resolvers for common indirect jump patterns:
# CFGFast automatically resolves jump tablescfg = p.analyses.CFGFast(resolve_indirect_jumps=True)# Access resolved targetsfor addr, jump in cfg.indirect_jumps.items(): print(f"Indirect jump at {addr:#x}") print(f"Type: {jump.type}") if jump.resolved_targets: print(f"Targets: {[hex(t) for t in jump.resolved_targets]}")