Architecture
sol-azy is a modular static analysis toolkit designed to work on Solana programs compiled to eBPF.
It is capable of disassembling, analyzing control flow, decoding embedded .rodata strings, and performing pattern-based syntactic analysis through rule-based AST matching.
High-Level Design
sol-azy is structured around three main engines, supported by auxiliary modules:
Core Engines
-
Reverse Engine Handles binary-level disassembly, control flow graph generation, and
.rodataanalysis. → Triggered via thereverseCLI command. -
SAST Engine Performs static source-level analysis using Starlark-based rule evaluation on Rust ASTs. → Triggered via the
sastCLI command. -
Build Engine Detects the project type (
Anchor,SBF) and compiles the bytecode accordingly. → Triggered via thebuildCLI command.
Supporting Modules
-
Dotting Module Allows users to manually reintroduce function clusters into reduced CFGs by editing
.dotfiles post-generation. → Useful for large programs or targeted function exploration. -
Fetcher Module Retrieves deployed program bytecode directly from on-chain Solana accounts via RPC. → Enables reverse analysis even without access to local source code.
Each component is designed to be composable and scriptable, making sol-azy flexible for both auditing and program analysis workflows.
Component Overview
1. reverse/
Handles all operations on compiled .so eBPF files:
disass.rs: Disassembler with inline.rodataresolutioncfg.rs: Generates DOT graphs from the control flowimmediate_tracker.rs: Tracks data regions used byLD_DW_IMMutils.rs: String formatting and decoding
It produces:
disassembly.outimmediate_data_table.outcfg.dot
→ See Reverse Overview
2. parsers/ + state/sast_state.rs
Used in source-level static analysis.
- Parses all
.rsfiles into [syn::File] ASTs - Builds
AstPositionswith span references - Applies Starlark-based rules to nodes and attributes
- Aggregates findings into a
SastState
→ See SAST Overview
3. engines/starlark_engine.rs
Embeds a Starlark interpreter to run user-defined rules against Rust ASTs.
- Prepares the AST (JSON + span tracking)
- Loads
.starfiles fromrules/ - Invokes
syn_ast_rule(...)with context - Collects
matchesand metadata as JSON
→ See Writing Rules
4. commands/
Command routing layer:
build_command.rs: Uses Anchor/Cargo to compile.sofilesreverse_command.rs: Dispatches disass + cfg generationsast_command.rs: Launches Starlark rule scanning
Used by AppState::run_cli() to manage flow.
5. helpers/
Utilities to:
- Detect project type (
Anchor.toml,Cargo.toml) - Check external dependencies (
cargo,anchor) - Run subprocesses with environment overrides
6. dotting/
Post-processing module for .dot control flow graphs:
- Allows restoring function subgraphs in reduced or entrypoint-only graphs
- Takes as input a list of function IDs (clusters) to reinject
- Outputs an
updated_*.dotfile with the requested functions and edges
This module is especially useful when a full CFG is too large or noisy, letting analysts rebuild targeted graphs incrementally.
→ See Dotting
7. fetcher/
Bytecode retrieval module for on-chain programs:
- Connects to Solana RPC endpoints
- Downloads the deployed
.sobytecode of a program ID - Saves the ELF file locally for reverse analysis
This feature is useful for audits where source code is unavailable or unverifiable.
→ See Fetcher
Output Flow
+----------------+
| .so Bytecode | ← built via cargo / anchor
+----------------+
|
[reverse_command]
↓
+-----------------------+
| Analysis (sbpf-solana)
| + immediate tracker
+-----------------------+
↓ ↓ ↓
disass immediate cfg.dot
.out _data.out
--------------------------------------
+----------------+
| Rust Source | ← e.g. Anchor project
+----------------+
|
[sast_command]
↓
+----------------------+
| syn::File + spans |
| + rule evaluation |
+----------------------+
↓
Findings
(printed / JSON)
External Dependencies
sbpf-solana (anza-xyz): Disassembly / Analysis coresyn: Source AST parsingstarlark-rust: Rule evaluation engine
Extensibility
The architecture is modular and designed for extension:
- Add new output formats by extending the
ReverseOutputMode - Plug in new analysis passes (e.g., MIR or LLVM IR) in
engines/ - Write new rules without modifying Rust code (
.starfiles) - Integrate into CI pipelines via CLI interface