FunctionManager API reference for managing functions in angr
The FunctionManager plugin manages all functions discovered in a binary, providing access to function metadata, call graphs, and function relationships.
FunctionManager implements the Mapping protocol, so you can use it like a dictionary:
# Access by addressfunc = kb.functions[0x401000]# Access by namefunc = kb.functions['main']# Check if function existsif 0x401000 in kb.functions: print("Function exists")# Get number of functionsnum_funcs = len(kb.functions)# Iterate over all functionsfor addr in kb.functions: func = kb.functions[addr] print(f"{hex(addr)}: {func.name}")# Iterate over address, function pairsfor addr, func in kb.functions.items(): print(f"{hex(addr)}: {func.name}")
Filter by PLT status: True for PLT stubs, False for non-PLT, None for any
Returns: Function instance or None if not found
# Get function by addressfunc = kb.functions.function(addr=0x401000)# Get or create functionfunc = kb.functions.function(addr=0x401000, create=True)# Get function by namemain = kb.functions.function(name='main')# Filter PLT functionsplt_func = kb.functions.function(addr=0x401000, plt=True)
# Get all functions named 'helper'for func in kb.functions.get_by_name('helper'): print(f"Found helper at {hex(func.addr)}")# Include previous names in searchfor func in kb.functions.get_by_name('old_name', check_previous_names=True): print(f"Function was previously named 'old_name': {func.name}")
# Query by name in main objectfunc = kb.functions.query('::main')# Query by address and namefunc = kb.functions.query('::0x401000::main')# Query by object and namefunc = kb.functions.query('::libc.so::malloc')
# Iterate all functionsfor func in kb.functions.values(): print(f"{func.name}: {func.size} bytes")# Iterate meta-only (faster for large binaries)for func in kb.functions.values(meta_only=True): print(func.name) # Can access basic properties only
# Create new functionkb.functions[0x401000] = Function(kb.functions, 0x401000, name='my_func')# Or use function() with create=Truefunc = kb.functions.function(addr=0x401000, create=True)
The callgraph property provides access to the function call graph:
import networkx as nx# Get all callers of a functioncallers = list(kb.functions.callgraph.predecessors(0x401000))# Get all calleescallees = list(kb.functions.callgraph.successors(0x401000))# Check if function A calls function Bif kb.functions.callgraph.has_edge(0x401000, 0x402000): print("Function 0x401000 calls 0x402000")# Get edge datafor src, dst, data in kb.functions.callgraph.edges(data=True): edge_type = data.get('type', 'call') print(f"{hex(src)} -> {hex(dst)} ({edge_type})")
# Find all non-returning functionsfor func in kb.functions.values(): if func.returning is False: print(f"Non-returning: {func.name} at {hex(func.addr)}")# Find all PLT functionsfor addr, func in kb.functions.items(): if func.is_plt: print(f"PLT: {func.name}")
import networkx as nx# Find leaf functions (no callees)leaves = [n for n in kb.functions.callgraph.nodes() if kb.functions.callgraph.out_degree(n) == 0]# Find functions with most callerscallers_count = [(n, kb.functions.callgraph.in_degree(n)) for n in kb.functions.callgraph.nodes()]most_called = max(callers_count, key=lambda x: x[1])print(f"Most called: {hex(most_called[0])} ({most_called[1]} callers)")# Get call chain between two functionstry: path = nx.shortest_path(kb.functions.callgraph, 0x401000, 0x402000) print(f"Call path: {' -> '.join(hex(a) for a in path)}")except nx.NetworkXNoPath: print("No call path found")
# Use cache limit for large binarieskb.functions = FunctionManager(kb, cache_limit=1000)# Use meta_only for faster iterationfor func in kb.functions.values(meta_only=True): # Only access basic properties print(f"{hex(func.addr)}: {func.name}") # Don't access func.blocks or func.graph (not loaded)