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.

CLE (CLE Loads Everything) is angr’s binary loading component. It takes a binary file and its dependencies and presents them to angr in a unified virtual address space.

The Loader

The loader is available through the project.loader property:
import angr

proj = angr.Project('examples/fauxware/fauxware')
proj.loader
# <Loaded fauxware, maps [0x400000:0x5008000]>

Basic Properties

# Address space bounds
proj.loader.min_addr  # 0x400000
proj.loader.max_addr  # 0x5008000

# Main binary object
proj.loader.main_object
# <ELF Object fauxware, maps [0x400000:0x60105f]>

# All loaded objects
proj.loader.all_objects
# [<ELF Object fauxware, maps [0x400000:0x60105f]>,
#  <ELF Object libc-2.23.so, maps [0x1000000:0x13c999f]>,
#  <ELF Object ld-2.23.so, maps [0x2000000:0x2227167]>,
#  ...]

Loaded Objects

CLE loads multiple binary objects into a single memory space:

Object Collections

# Dictionary of shared objects
proj.loader.shared_objects
# {'fauxware': <ELF Object fauxware, maps [0x400000:0x60105f]>,
#  'libc.so.6': <ELF Object libc-2.23.so, maps [0x1000000:0x13c999f]>,
#  'ld-linux-x86-64.so.2': <ELF Object ld-2.23.so, maps [0x2000000:0x2227167]>}

# All ELF objects (use all_pe_objects for Windows)
proj.loader.all_elf_objects

# Special objects
proj.loader.extern_object  # Provides addresses for unresolved imports
proj.loader.kernel_object  # Provides addresses for emulated syscalls

# Find object containing an address
proj.loader.find_object_containing(0x400000)
# <ELF Object fauxware, maps [0x400000:0x60105f]>

Working with Objects

Object Properties

Each loaded object provides extensive metadata:
obj = proj.loader.main_object

# Entry point
obj.entry  # 0x400580

# Address bounds
obj.min_addr  # 0x400000
obj.max_addr  # 0x60105f

# Segments and sections
obj.segments
# <Regions: [<ELFSegment memsize=0xa74, filesize=0xa74, vaddr=0x400000, flags=0x5, offset=0x0>,
#            <ELFSegment memsize=0x238, filesize=0x228, vaddr=0x600e28, flags=0x6, offset=0xe28>]>

obj.sections
# <Regions: [<.text | offset 0x580, vaddr 0x400580, size 0x338>,
#            <.rodata | offset 0x8b8, vaddr 0x4008b8, size 0x54>,
#            ...]>

Finding Segments and Sections

# Get segment/section containing an address
obj.find_segment_containing(obj.entry)
# <ELFSegment memsize=0xa74, filesize=0xa74, vaddr=0x400000, flags=0x5, offset=0x0>

obj.find_section_containing(obj.entry)
# <.text | offset 0x580, vaddr 0x400580, size 0x338>

PLT and GOT

# Get PLT stub address for a symbol
addr = obj.plt['strcmp']
addr  # 0x400550

# Reverse lookup
obj.reverse_plt[addr]  # 'strcmp'

Binary Properties

# Base addresses
obj.linked_base   # Prelinked base (from binary)
obj.mapped_base   # Actual mapped base (by CLE)

# Binary characteristics
obj.execstack     # Does binary have executable stack?
obj.pic           # Is binary position-independent?

Symbols and Relocations

Finding Symbols

# Find symbol by name
strcmp = proj.loader.find_symbol('strcmp')
strcmp
# <Symbol "strcmp" in libc.so.6 at 0x1089cd0>

strcmp.name          # 'strcmp'
strcmp.owner         # <ELF Object libc-2.23.so, ...>
strcmp.rebased_addr  # 0x1089cd0 (global address space)
strcmp.linked_addr   # 0x89cd0 (relative to prelinked base)
strcmp.relative_addr # 0x89cd0 (RVA - relative to object base)
The three address types serve different purposes:
  • rebased_addr - use for angr analysis (global address space)
  • linked_addr - matches addresses from readelf or other tools
  • relative_addr - relative virtual address (RVA), used in Windows literature

Symbol Properties

# Symbol from main object (import)
main_strcmp = proj.loader.main_object.get_symbol('strcmp')
main_strcmp.is_import  # True
main_strcmp.is_export  # False
main_strcmp.resolvedby # <Symbol "strcmp" in libc.so.6 at 0x1089cd0>

# Symbol from libc (export)
strcmp.is_export  # True
strcmp.is_import  # False

Relocations

Relocations specify how to wire up imports with exports:
# Get all relocations for an object
obj.relocs

# Get imports (mapping from symbol name to Relocation)
proj.loader.shared_objects['libc.so.6'].imports
# {'__libc_enable_secure': <R_X86_64_GLOB_DAT at 0x...>,
#  '__tls_get_addr': <R_X86_64_JUMP_SLOT at 0x...>,
#  ...}
A relocation has:
  • .symbol - the import symbol
  • .owner - the object requesting the relocation
  • Address identifiers (same as Symbol)
If an import cannot be resolved, CLE automatically adds it to the externs object as an export, allowing analysis to continue.

Loading Options

You can customize how CLE loads binaries by passing options to Project():

Basic Options

import angr

# Don't automatically load shared libraries
proj = angr.Project('./binary', auto_load_libs=False)

# Throw exception if a library dependency is missing
proj = angr.Project('./binary', except_missing_libs=True)

# Force loading specific libraries
proj = angr.Project('./binary', force_load_libs=['libcustom.so'])

# Skip loading specific libraries
proj = angr.Project('./binary', skip_libs=['libunneeded.so'])

# Add custom library search paths
proj = angr.Project('./binary', ld_path=['/custom/lib/path'])
auto_load_libs
bool
default:"True"
Automatically resolve and load shared library dependencies
except_missing_libs
bool
default:"False"
Throw exception when a library dependency cannot be resolved
force_load_libs
list[str]
Treat these as unresolved dependencies even if not in the binary
skip_libs
list[str]
Don’t load these libraries even if they’re dependencies
ld_path
str | list[str]
Additional search paths for shared libraries

Per-Binary Options

You can specify options for individual binaries:
# Options for main binary
main_opts = {
    'backend': 'elf',
    'base_addr': 0x400000,
}

# Options for specific libraries
lib_opts = {
    'libc.so.6': {
        'backend': 'elf'
    }
}

proj = angr.Project('./binary', main_opts=main_opts, lib_opts=lib_opts)
Common per-binary options:
backend
str | class
Which backend to use (elf, pe, blob, etc.)
base_addr
int
Base address to load the binary at
entry_point
int
Override the entry point
arch
str
Architecture name (required for some backends like blob)

Backends

CLE supports multiple file formats through different backends:
BackendDescriptionRequires arch?
elfELF files (Linux, BSD)No
pePE files (Windows)No
mach-oMach-O files (macOS)No
cgcCyber Grand Challenge binariesNo
elfcoreELF core dumpsNo
blobFlat binary imageYes

Loading Flat Binaries

For raw binary files (firmware, shellcode, etc.), use the blob backend:
# Load flat binary
proj = angr.Project(
    'firmware.bin',
    main_opts={
        'backend': 'blob',
        'arch': 'arm',
        'base_addr': 0x8000,
        'entry_point': 0x8000
    }
)

SimProcedures and Hooking

By default, CLE and Project work together to replace library functions with SimProcedures:
1

CLE loads the binary and dependencies

CLE identifies which libraries the binary needs and loads them.
2

Project identifies library functions

Project scans the loaded objects for known library functions.
3

Functions are replaced with SimProcedures

Known library functions are hooked with Python implementations (SimProcedures) instead of their actual code.

Controlling SimProcedure Usage

# Disable all SimProcedures
proj = angr.Project('./binary', use_sim_procedures=False)

# Exclude specific functions from SimProcedures
proj = angr.Project('./binary', 
    exclude_sim_procedures_list=['malloc', 'free'])

# Exclude functions based on a condition
def should_exclude(name):
    return name.startswith('str')

proj = angr.Project('./binary',
    exclude_sim_procedures_func=should_exclude)
SimProcedures dramatically improve analysis tractability but may introduce inaccuracies. If you need to analyze the actual implementation of a library function, exclude it from SimProcedures.

Behavior Without SimProcedures

When a library function has no SimProcedure:
  • If auto_load_libs=True: The real library function executes (may cause state explosion)
  • If auto_load_libs=False: Function is replaced with ReturnUnconstrained stub (returns symbolic value)

Example: Analyzing a Binary

import angr

# Load binary without shared libraries
proj = angr.Project('./vulnerable_program', auto_load_libs=False)

# Examine loaded objects
print(f"Main object: {proj.loader.main_object}")
print(f"Entry point: {proj.loader.main_object.entry:#x}")
print(f"Address space: {proj.loader.min_addr:#x} - {proj.loader.max_addr:#x}")

# Find a specific function
func_symbol = proj.loader.find_symbol('vulnerable_function')
if func_symbol:
    print(f"Found function at: {func_symbol.rebased_addr:#x}")
    
    # Get the section containing it
    section = proj.loader.main_object.find_section_containing(
        func_symbol.rebased_addr
    )
    print(f"Function in section: {section.name}")

# Check what libraries would have been loaded
for lib in proj.loader.missing_dependencies:
    print(f"Missing library: {lib}")

See Also