Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/angr/angr/llms.txt

Use this file to discover all available pages before exploring further.

The VariableManager plugin manages variables across the entire binary, including local variables, stack variables, registers, and global variables. It provides per-function variable tracking and type information.

VariableManager Class

class VariableManager(KnowledgeBasePlugin):
    """Manages variables across functions and globally."""

Constructor

kb
KnowledgeBase
required
The knowledge base instance this manager belongs to
vm = VariableManager(kb)

Properties

global_manager
VariableManagerInternal
Variable manager for global scope (variables not tied to a specific function)
function_managers
dict[int, VariableManagerInternal]
Dictionary mapping function addresses to their VariableManagerInternal instances

Accessing Variable Managers

The VariableManager acts as a container for function-specific variable managers:
# Access global variable manager
global_vars = kb.variables['global']

# Access function-specific variable manager
func_addr = 0x401000
func_vars = kb.variables[func_addr]

# Check if function has variable manager
if func_addr in kb.variables:
    print("Function has variables tracked")

# Access via get_manager (same as dict access)
func_vars = kb.variables.get_manager(func_addr)

VariableManagerInternal

Each function has its own VariableManagerInternal instance that manages variables within that function’s scope.

Properties

func_addr
int | None
Address of the function this manager belongs to (None for global manager)
types
TypesStore
Type information store for this function’s variables
variable_to_types
dict[SimVariable, SimType]
Mapping from variables to their types
variables_with_manual_types
set[SimVariable]
Set of variables with manually-assigned types

Variable Access

get_variables

func_vars.get_variables(
    sort: Literal['stack', 'reg'] | None = None,
    collapse_same_ident: bool = False
) -> list[SimVariable]
Get all variables in the function.
sort
str
default:"None"
Filter by variable type: 'stack' for stack variables, 'reg' for register variables, None for all
collapse_same_ident
bool
default:"False"
Whether to collapse variables with same identifier (not yet implemented)
Returns: List of variables
# Get all variables
all_vars = func_vars.get_variables()

# Get only stack variables
stack_vars = func_vars.get_variables(sort='stack')

# Get only register variables
reg_vars = func_vars.get_variables(sort='reg')

for var in stack_vars:
    print(f"Stack var: {var.name} at offset {var.offset}")

get_unified_variables

func_vars.get_unified_variables(
    sort: Literal['stack', 'reg'] | None = None
) -> list[SimVariable]
Get unified variables (SSA variables merged into single representations).
sort
str
default:"None"
Filter by variable type: 'stack', 'reg', or None for all
Returns: List of unified variables
# Get all unified variables
unified = func_vars.get_unified_variables()

# Get unified stack variables
unified_stack = func_vars.get_unified_variables(sort='stack')

for var in unified:
    print(f"{var.name}: {var}")

get_global_variables

func_vars.get_global_variables(addr: int) -> set[SimVariable]
Get global variables at a specific address.
addr
int
required
Address of the global variable
Returns: Set of variables at that address

Finding Variables

find_variables_by_insn

func_vars.find_variables_by_insn(
    ins_addr: int,
    sort: Literal['memory', 'register']
) -> list[tuple[SimVariable, int | None]] | None
Find variables accessed at a specific instruction address.
ins_addr
int
required
Instruction address
sort
str
required
Type of variables to find: 'memory' or 'register'
Returns: List of (variable, offset) tuples, or None if no variables found
# Find memory variables at instruction
mem_vars = func_vars.find_variables_by_insn(0x401234, 'memory')
if mem_vars:
    for var, offset in mem_vars:
        print(f"{var.name} at offset {offset}")

# Find register variables
reg_vars = func_vars.find_variables_by_insn(0x401234, 'register')

find_variables_by_stmt

func_vars.find_variables_by_stmt(
    block_addr: int,
    stmt_idx: int,
    sort: Literal['memory', 'register', 'constant'],
    block_idx: int | None = None
) -> list[tuple[SimVariable, int | None]]
Find variables accessed at a specific statement.
block_addr
int
required
Address of the block containing the statement
stmt_idx
int
required
Index of the statement within the block
sort
str
required
Type: 'memory', 'register', or 'constant'
block_idx
int
default:"None"
Optional block index for blocks with same address
Returns: List of (variable, offset) tuples
# Find variables at statement
vars = func_vars.find_variables_by_stmt(0x401000, 5, 'memory')
for var, offset in vars:
    print(f"{var.name} ({offset})")

find_variables_by_atom

func_vars.find_variables_by_atom(
    block_addr: int,
    stmt_idx: int,
    atom: ailment.expression.Expression,
    block_idx: int | None = None
) -> set[tuple[SimVariable, int | None]]
Find variables represented by a specific AIL atom.
block_addr
int
required
Block address
stmt_idx
int
required
Statement index
atom
ailment.expression.Expression
required
AIL expression atom to search for
block_idx
int
default:"None"
Optional block index
Returns: Set of (variable, offset) tuples

find_variables_by_stack_offset

func_vars.find_variables_by_stack_offset(offset: int) -> set[SimVariable]
Find all variables at a specific stack offset.
# Find variables at stack offset -0x10
vars = func_vars.find_variables_by_stack_offset(-0x10)
for var in vars:
    print(f"{var.name}: {var.size} bytes")

find_variables_by_register

func_vars.find_variables_by_register(reg: str | int) -> set[SimVariable]
Find all variables stored in a specific register.
reg
str | int
required
Register name (e.g., 'rax') or register offset
# Find variables in rax
vars = func_vars.find_variables_by_register('rax')
for var in vars:
    print(f"{var.name} in rax")

Variable Accesses

get_variable_accesses

func_vars.get_variable_accesses(
    variable: SimVariable,
    same_name: bool = False
) -> list[VariableAccess]
Get all accesses (reads/writes) to a variable.
variable
SimVariable
required
The variable to query
same_name
bool
default:"False"
If True, include accesses to all variables with the same name
Returns: List of VariableAccess objects
var = stack_vars[0]
accesses = func_vars.get_variable_accesses(var)

for access in accesses:
    access_type = 'READ' if access.access_type == VariableAccessSort.READ else 'WRITE'
    print(f"{access_type} at {hex(access.location.ins_addr)}")

input_variables

func_vars.input_variables(exclude_specials: bool = True) -> list[SimVariable]
Get all input variables (variables that are read but never written).
exclude_specials
bool
default:"True"
Exclude special variables (like stack pointer)
Returns: List of input variables
# Find function arguments and external inputs
inputs = func_vars.input_variables()
for var in inputs:
    print(f"Input: {var.name}")

get_variables_without_writes

func_vars.get_variables_without_writes() -> list[SimVariable]
Get all variables that have never been written to. Returns: List of variables without write accesses

Variable Recording

write_to / read_from / reference_at

func_vars.write_to(
    variable: SimVariable,
    offset: int | None,
    location: CodeLocation,
    overwrite: bool = False,
    atom: ailment.expression.Atom | None = None
)

func_vars.read_from(
    variable: SimVariable,
    offset: int | None,
    location: CodeLocation,
    overwrite: bool = False,
    atom: ailment.expression.Atom | None = None
)

func_vars.reference_at(
    variable: SimVariable,
    offset: int | None,
    location: CodeLocation,
    overwrite: bool = False,
    atom: ailment.expression.Atom | None = None
)
Record variable accesses. These are typically used internally by analyses.
variable
SimVariable
required
Variable being accessed
offset
int | None
required
Offset within the variable
location
CodeLocation
required
Location of the access
overwrite
bool
default:"False"
Replace existing accesses at this location
atom
ailment.expression.Atom
default:"None"
AIL atom representing this access

Type Management

set_variable_type

func_vars.set_variable_type(
    var: SimVariable,
    ty: SimType,
    name: str | None = None,
    override_bot: bool = True,
    all_unified: bool = False,
    mark_manual: bool = False
) -> None
Set the type of a variable.
var
SimVariable
required
Variable to set type for
ty
SimType
required
Type to assign
name
str
default:"None"
Optional type name for named types
override_bot
bool
default:"True"
Replace bottom types with default integer types
all_unified
bool
default:"False"
Apply type to all unified variables
mark_manual
bool
default:"False"
Mark as manually set (won’t be overridden by analysis)
from angr.sim_type import SimTypeInt, SimTypePointer

# Set variable type to int
var = stack_vars[0]
func_vars.set_variable_type(var, SimTypeInt())

# Set pointer type
ptr_type = SimTypePointer(SimTypeInt())
func_vars.set_variable_type(var, ptr_type, mark_manual=True)

# Apply to all unified variables
func_vars.set_variable_type(var, SimTypeInt(), all_unified=True)

get_variable_type

func_vars.get_variable_type(var: SimVariable) -> SimType | None
Get the type of a variable. Returns: SimType instance or None if no type is set
var_type = func_vars.get_variable_type(var)
if var_type:
    print(f"{var.name} has type: {var_type}")

remove_types

func_vars.remove_types() -> None
Clear all type information.

Phi Variables

Phi variables represent merged SSA variables at control flow join points.

make_phi_node

func_vars.make_phi_node(
    block_addr: int,
    *variables: SimVariable
) -> SimVariable
Create or get a phi variable for the given variables at a block.
block_addr
int
required
Address of the block where phi occurs
variables
SimVariable
required
Variables to merge
Returns: The phi variable

is_phi_variable

func_vars.is_phi_variable(var: SimVariable) -> bool
Check if a variable is a phi variable.

get_phi_subvariables

func_vars.get_phi_subvariables(var: SimVariable) -> set[SimVariable]
Get the sub-variables that a phi variable represents.
if func_vars.is_phi_variable(var):
    subvars = func_vars.get_phi_subvariables(var)
    print(f"Phi variable {var.name} merges:")
    for subvar in subvars:
        print(f"  - {subvar.name}")

get_phi_variables

func_vars.get_phi_variables(block_addr: int) -> dict[SimVariable, set[SimVariable]]
Get all phi variables at a block.
block_addr
int
required
Block address
Returns: Dictionary mapping phi variables to their sub-variables

Variable Unification

unify_variables

func_vars.unify_variables(
    interference: networkx.Graph[int] | None = None
) -> None
Unify SSA variables into single representations based on phi nodes and interference.
interference
networkx.Graph[int]
default:"None"
Variable interference graph

unified_variable

func_vars.unified_variable(variable: SimVariable) -> SimVariable | None
Get the unified variable for an SSA variable.
ssa_var = stack_vars[0]
unified = func_vars.unified_variable(ssa_var)
if unified:
    print(f"{ssa_var.name} unifies to {unified.name}")

set_unified_variable

func_vars.set_unified_variable(
    variable: SimVariable,
    unified: SimVariable
) -> None
Manually set the unified variable for an SSA variable.

Variable Naming

assign_variable_names

func_vars.assign_variable_names(
    labels: dict | None = None,
    types: list[type] | None = None
) -> None
Assign default names to all variables.
labels
dict
default:"None"
Known labels in the binary
types
list[type]
default:"None"
Variable types to name (None for all)
# Assign default names to all variables
func_vars.assign_variable_names()

# Only assign names to stack variables
from angr.sim_variable import SimStackVariable
func_vars.assign_variable_names(types=[SimStackVariable])

assign_unified_variable_names

func_vars.assign_unified_variable_names(
    labels: dict | None = None,
    arg_names: list[str] | None = None,
    reset: bool = False,
    func_blocks: list[ailment.Block] | None = None
) -> None
Assign names to unified variables.
labels
dict
default:"None"
Known labels
arg_names
list[str]
default:"None"
Argument names to use
reset
bool
default:"False"
Reset existing names
func_blocks
list[ailment.Block]
default:"None"
Function blocks for optimization
# Assign names with custom argument names
func_vars.assign_unified_variable_names(
    arg_names=['argc', 'argv', 'envp']
)

Usage Examples

Basic Variable Analysis

# Get function variable manager
func = kb.functions[0x401000]
var_mgr = kb.variables[func.addr]

# Analyze stack variables
stack_vars = var_mgr.get_variables(sort='stack')
print(f"Found {len(stack_vars)} stack variables")

for var in stack_vars:
    accesses = var_mgr.get_variable_accesses(var)
    reads = sum(1 for a in accesses if a.access_type == VariableAccessSort.READ)
    writes = sum(1 for a in accesses if a.access_type == VariableAccessSort.WRITE)
    print(f"{var.name}: {reads} reads, {writes} writes")

Type Assignment

from angr.sim_type import SimTypeInt, SimTypePointer, SimTypeChar

var_mgr = kb.variables[0x401000]

# Assign types to arguments
args = [v for v in var_mgr.get_variables() if v.ident.startswith('arg_')]
if len(args) >= 2:
    # argc: int
    var_mgr.set_variable_type(args[0], SimTypeInt(), mark_manual=True)
    # argv: char**
    var_mgr.set_variable_type(
        args[1],
        SimTypePointer(SimTypePointer(SimTypeChar())),
        mark_manual=True
    )

Finding Function Inputs

func_addr = 0x401000
var_mgr = kb.variables[func_addr]

# Get input variables (likely function arguments)
input_vars = var_mgr.input_variables()

print(f"Function {hex(func_addr)} has {len(input_vars)} inputs:")
for var in input_vars:
    var_type = var_mgr.get_variable_type(var)
    type_str = str(var_type) if var_type else 'unknown'
    print(f"  {var.name} ({type_str})")

VariableType Constants

class VariableType:
    REGISTER = 0
    MEMORY = 1
These constants are used in some methods for filtering variable types.