ThreatFlux Binary Analysis
A synchronous Rust library for inspecting the structure of executable and bytecode formats. The core API detects and parses bytes into a common representation of metadata, sections, symbols, imports, and exports. Optional Cargo features add disassembly, control-flow reconstruction, entropy measurements, symbol demangling, JSON support, and graph output.
Upgrading from 0.2.0? See the migration guide.
This is a static-analysis building block, not a malware verdict engine or an execution sandbox. Its security findings, packing indicators, and control-flow results are heuristics that can produce false positives, false negatives, or incomplete output.
What is implemented
| Input | Cargo feature | Current parser output and limits |
|---|---|---|
| ELF | elf (default) | Header metadata, sections, symbols, imports, exports, and selected hardening metadata |
| PE | pe (default) | Header metadata, sections, COFF symbols when present, imports, exports, and selected hardening metadata; Authenticode is not verified |
| Thin Mach-O | macho (default) | Header metadata, sections, nlist symbols when present, imports, exports, entry point when reported by the image, and selected hardening metadata; fat/universal binaries are unsupported |
| Java class | java (default) | Fixed-header checks and a synthetic whole-file section; the version is validated for detection but not exposed, and the constant pool, fields, methods, and attributes are not decoded |
| JAR | java (default) | Identifies ZIP archives containing class entries and exposes class-entry names as symbols; class bodies are not decoded |
| WebAssembly | wasm | Validated module plus selected section ranges, imports, and exports; the WebAssembly start-function index is not represented as a virtual-address entry point |
| Other non-empty bytes | always | A raw binary with one synthetic data section |
Valid Java class headers, bounded universal Mach-O headers, and WebAssembly magic are recognized even when the corresponding parser feature is disabled. Parsing such input then returns an unsupported-format error. Empty input is rejected; an unrecognized non-empty input is classified as raw data.
Parser output and optional analyses are separate layers:
- BinaryFile::parse detects and parses structural data.
- BinaryAnalyzer copies that structural data and invokes only the analysis modules compiled into the crate.
- Disassembler, control-flow analysis, entropy analysis, and SecurityAnalyzer are opt-in tools with independent limitations.
See Analysis boundaries before using the crate on untrusted input.
Requirements
- Rust 1.95.0 or newer
- A C toolchain when building the Capstone backend
Installation
Default parsers (ELF, PE, thin Mach-O, Java class/JAR):
[]
= "0.3"
Select only the formats and analysis backends you need:
[]
= {
version = "0.3",
= false,
= ["elf", "disasm-iced", "serde-support"]
}
Feature flags
| Feature | Default | Adds |
|---|---|---|
| elf | yes | ELF parser |
| pe | yes | PE parser |
| macho | yes | Thin Mach-O parser |
| java | yes | Java class and class-containing JAR parser |
| wasm | no | WebAssembly parser |
| disasm-capstone | no | Capstone backend for x86, x86-64, ARM, AArch64, MIPS, PowerPC |
| disasm-iced | no | iced-x86 backend for x86 and x86-64 |
| control-flow | no | Control-flow and call-graph analysis; also enables Capstone |
| entropy-analysis | no | Shannon entropy and packing heuristics |
| symbol-resolution | no | Demangling of parsed symbol names |
| compression | no | gzip and zlib decompression helper |
| visualization | no | DOT export for control-flow graphs |
| serde-support | no | Serde derives and JSON helpers |
Cargo features are compile-time capabilities. Setting an AnalysisConfig boolean does not enable a missing feature. Version 0.3 defaults to parser-only analysis: every optional runtime switch is false. To run an optional analysis, compile its Cargo feature and set its runtime switch. Requesting a capability that was not compiled returns BinaryError::FeatureNotAvailable.
Quick start
Parse a file and inspect its structural metadata:
use fs;
use ;
Use BinaryAnalyzer when you want the unified AnalysisResult:
use ;
The API is synchronous and byte-slice based. BinaryAnalyzer enforces its configured input limit, but bounding a file before reading it, scheduling work, and applying wall-time limits remain caller responsibilities.
Examples
Each example expects a path after --:
cargo run --example basic_analysis -- path/to/file
cargo run --example security_analysis -- path/to/file
cargo run --example disassembly --features disasm-capstone -- path/to/file
cargo run --example control_flow --features control-flow -- path/to/file
Section::size is the virtual or in-memory span, while Section::file_size is the byte count backed by the file at Section::offset. Section::data is only an inline preview for eligible file-backed sections no larger than 1 KiB. Built-in disassembly and graph analysis resolve checked file-backed ranges against BinaryFile's full owned bytes. The current disassembly example prints the preview directly and can therefore be partial; production callers can use Disassembler::disassemble_section or pass a validated byte range to disassemble.
Security and resource boundaries
Treat every parsed file as adversarial:
- Reject oversized input before reading it. BinaryAnalyzer also enforces AnalysisConfig::max_analysis_size and returns BinaryError::InputTooLarge; direct BinaryFile::parse has no configured size policy.
- Set AnalysisConfig::max_disassembly_instructions for the maximum high-level disassembly result size. The default is 10,000 instructions.
- Expect copies. BinaryFile::parse owns a copy of the full input, and format implementations may retain additional owned data.
- ELF, PE, Mach-O, Java/JAR, and WebAssembly parsing rejects more than 100,000 structural/output records, names longer than 4 KiB, or more than 32 MiB of aggregate owned name bytes per parse. JAR central-directory inspection has the tighter 50,000-entry limit described above.
- Run hostile inputs in a separate process with OS-level memory, CPU, and wall time limits. The API has no cancellation or timeout mechanism.
- Do not treat a successful parse, a low heuristic score, or absence of a finding as proof that a file is safe.
- The default compression helper caps expanded output at 64 MiB. decompress_with_limit accepts an application-specific ceiling.
- Pattern searches cap aggregate owned result data at 16 MiB by default; configure MatchConfig::max_output_bytes for a different budget.
- Keep files immutable while they are memory mapped.
Memory-map construction is an unsafe API because callers must keep the backing file stable for the mapping lifetime. Rust's type system reduces many classes of memory errors, but it is not a sandbox or a guarantee that malformed input cannot panic, exhaust resources, or trigger a dependency bug.
Report vulnerabilities privately as described in SECURITY.md.
Documentation
- Curated API guide
- Analysis boundaries and threat model
- Migrating from 0.2 to 0.3
- Release process
- Development guide
- Testing guide
- Contributing
- Generated Rust API documentation
Generate API documentation locally with:
RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps
Development
The supported local contracts are:
make check
make test
make security
make feature-check
make ci
See DEVELOPMENT.md for prerequisites and feature-specific checks. There are currently no Criterion benchmark targets in Cargo.toml; performance-named integration tests are ordinary test targets, not a published benchmark suite.
Contributing
Bug reports, focused fixes, tests, and documentation corrections are welcome. Please read CONTRIBUTING.md and the Code of Conduct first.
License
Licensed under the MIT License.