threatflux_binary_analysis/
lib.rs

1#![allow(clippy::uninlined_format_args)]
2//! # ThreatFlux Binary Analysis Library
3//!
4//! A comprehensive binary analysis framework for security research, reverse engineering,
5//! and threat detection. Supports multiple binary formats with advanced analysis capabilities.
6//!
7//! ## Features
8//!
9//! - **Multi-format Support**: ELF, PE, Mach-O, Java, WASM
10//! - **Disassembly**: Multi-architecture support via Capstone and iced-x86
11//! - **Control Flow Analysis**: CFG construction, complexity metrics, anomaly detection
12//! - **Symbol Resolution**: Debug info parsing, demangling, cross-references
13//! - **Entropy Analysis**: Statistical analysis, packing detection
14//! - **Security Analysis**: Vulnerability patterns, malware indicators
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use threatflux_binary_analysis::BinaryAnalyzer;
20//!
21//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
22//! // Example with minimal data - analysis may fail for incomplete binaries
23//! let data = vec![0x7f, 0x45, 0x4c, 0x46]; // ELF magic
24//!
25//! let analyzer = BinaryAnalyzer::new();
26//! match analyzer.analyze(&data) {
27//!     Ok(analysis) => {
28//!         println!("Format: {:?}", analysis.format);
29//!         println!("Architecture: {:?}", analysis.architecture);
30//!     }
31//!     Err(e) => {
32//!         println!("Analysis failed: {}", e);
33//!     }
34//! }
35//! # Ok(())
36//! # }
37//! ```
38
39pub mod analysis;
40pub mod error;
41pub mod formats;
42pub mod types;
43
44#[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
45pub mod disasm;
46
47pub mod utils;
48
49// Re-export main types
50#[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
51pub use disasm::DisassemblyEngine;
52pub use error::{BinaryError, Result};
53pub use types::{
54    AnalysisResult, Architecture, BasicBlock, BinaryFormat, BinaryFormatParser, BinaryFormatTrait,
55    BinaryMetadata, ComplexityMetrics, ControlFlowGraph, EntropyAnalysis, Export, Function, Import,
56    Instruction, Section, SecurityIndicators, Symbol,
57};
58
59/// Main entry point for binary analysis
60pub struct BinaryAnalyzer {
61    config: AnalysisConfig,
62}
63
64/// Configuration for binary analysis
65#[derive(Debug, Clone)]
66pub struct AnalysisConfig {
67    /// Enable disassembly analysis
68    pub enable_disassembly: bool,
69    /// Preferred disassembly engine
70    #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
71    pub disassembly_engine: DisassemblyEngine,
72    /// Enable control flow analysis
73    pub enable_control_flow: bool,
74    /// Enable entropy analysis
75    pub enable_entropy: bool,
76    /// Enable symbol resolution
77    pub enable_symbols: bool,
78    /// Maximum bytes to analyze for large files
79    pub max_analysis_size: usize,
80    /// Architecture hint (None for auto-detection)
81    pub architecture_hint: Option<Architecture>,
82}
83
84impl Default for AnalysisConfig {
85    fn default() -> Self {
86        Self {
87            enable_disassembly: true,
88            #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
89            disassembly_engine: DisassemblyEngine::Auto,
90            enable_control_flow: true,
91            enable_entropy: true,
92            enable_symbols: true,
93            max_analysis_size: 100 * 1024 * 1024, // 100MB
94            architecture_hint: None,
95        }
96    }
97}
98
99impl BinaryAnalyzer {
100    /// Create a new analyzer with default configuration
101    pub fn new() -> Self {
102        Self::with_config(AnalysisConfig::default())
103    }
104
105    /// Create a new analyzer with custom configuration
106    pub fn with_config(config: AnalysisConfig) -> Self {
107        Self { config }
108    }
109
110    /// Get a reference to the analysis configuration
111    pub fn config(&self) -> &AnalysisConfig {
112        &self.config
113    }
114
115    /// Analyze a binary file from raw data
116    pub fn analyze(&self, data: &[u8]) -> Result<AnalysisResult> {
117        let binary_file = BinaryFile::parse(data)?;
118        self.analyze_binary(&binary_file)
119    }
120
121    /// Analyze a parsed binary file
122    pub fn analyze_binary(&self, binary: &BinaryFile) -> Result<AnalysisResult> {
123        #[allow(unused_mut)] // mut needed when optional analysis features are enabled
124        let mut result = AnalysisResult {
125            format: binary.format(),
126            architecture: binary.architecture(),
127            entry_point: binary.entry_point(),
128            sections: binary.sections().to_vec(),
129            symbols: binary.symbols().to_vec(),
130            imports: binary.imports().to_vec(),
131            exports: binary.exports().to_vec(),
132            metadata: binary.metadata().clone(),
133            ..Default::default()
134        };
135
136        // Perform optional analyses based on configuration
137        if self.config.enable_disassembly {
138            #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
139            {
140                result.disassembly = Some(self.perform_disassembly(binary)?);
141            }
142        }
143
144        if self.config.enable_control_flow {
145            #[cfg(feature = "control-flow")]
146            {
147                result.control_flow = Some(self.perform_control_flow_analysis(binary)?);
148            }
149        }
150
151        if self.config.enable_entropy {
152            #[cfg(feature = "entropy-analysis")]
153            {
154                result.entropy = Some(self.perform_entropy_analysis(binary)?);
155            }
156        }
157
158        #[cfg(feature = "symbol-resolution")]
159        {
160            if self.config.enable_symbols {
161                analysis::symbols::demangle_symbols(&mut result.symbols);
162            }
163        }
164
165        Ok(result)
166    }
167
168    #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
169    fn perform_disassembly(&self, binary: &BinaryFile) -> Result<Vec<Instruction>> {
170        disasm::disassemble_binary(binary, &self.config)
171    }
172
173    #[cfg(feature = "control-flow")]
174    fn perform_control_flow_analysis(&self, binary: &BinaryFile) -> Result<Vec<ControlFlowGraph>> {
175        analysis::control_flow::analyze_binary(binary)
176    }
177
178    #[cfg(feature = "entropy-analysis")]
179    fn perform_entropy_analysis(&self, binary: &BinaryFile) -> Result<EntropyAnalysis> {
180        analysis::entropy::analyze_binary(binary)
181    }
182}
183
184impl Default for BinaryAnalyzer {
185    fn default() -> Self {
186        Self::new()
187    }
188}
189
190/// Parsed binary file representation
191pub struct BinaryFile {
192    data: Vec<u8>,
193    parsed: Box<dyn BinaryFormatTrait>,
194}
195
196impl BinaryFile {
197    /// Parse binary data and detect format
198    pub fn parse(data: &[u8]) -> Result<Self> {
199        let format = formats::detect_format(data)?;
200        let parsed = formats::parse_binary(data, format)?;
201
202        Ok(Self {
203            data: data.to_vec(),
204            parsed,
205        })
206    }
207
208    /// Get the binary format type
209    pub fn format(&self) -> BinaryFormat {
210        self.parsed.format_type()
211    }
212
213    /// Get the target architecture
214    pub fn architecture(&self) -> Architecture {
215        self.parsed.architecture()
216    }
217
218    /// Get the entry point address
219    pub fn entry_point(&self) -> Option<u64> {
220        self.parsed.entry_point()
221    }
222
223    /// Get binary sections
224    pub fn sections(&self) -> &[Section] {
225        self.parsed.sections()
226    }
227
228    /// Get symbol table
229    pub fn symbols(&self) -> &[Symbol] {
230        self.parsed.symbols()
231    }
232
233    /// Get imports
234    pub fn imports(&self) -> &[Import] {
235        self.parsed.imports()
236    }
237
238    /// Get exports
239    pub fn exports(&self) -> &[Export] {
240        self.parsed.exports()
241    }
242
243    /// Get binary metadata
244    pub fn metadata(&self) -> &BinaryMetadata {
245        self.parsed.metadata()
246    }
247
248    /// Get raw binary data
249    pub fn data(&self) -> &[u8] {
250        &self.data
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn test_analyzer_creation() {
260        let analyzer = BinaryAnalyzer::new();
261        assert!(analyzer.config.enable_disassembly);
262        assert!(analyzer.config.enable_control_flow);
263        assert!(analyzer.config.enable_entropy);
264        assert!(analyzer.config.enable_symbols);
265    }
266
267    #[test]
268    fn test_custom_config() {
269        let config = AnalysisConfig {
270            enable_disassembly: false,
271            #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
272            disassembly_engine: DisassemblyEngine::Auto,
273            enable_control_flow: true,
274            enable_entropy: false,
275            enable_symbols: true,
276            max_analysis_size: 1024,
277            architecture_hint: Some(Architecture::X86_64),
278        };
279
280        let analyzer = BinaryAnalyzer::with_config(config);
281        assert!(!analyzer.config.enable_disassembly);
282        assert!(analyzer.config.enable_control_flow);
283        assert!(!analyzer.config.enable_entropy);
284        assert!(analyzer.config.enable_symbols);
285        assert_eq!(analyzer.config.max_analysis_size, 1024);
286    }
287}