threatflux_binary_analysis/
lib.rs1#![allow(clippy::uninlined_format_args)]
2pub 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#[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
59pub struct BinaryAnalyzer {
61 config: AnalysisConfig,
62}
63
64#[derive(Debug, Clone)]
66pub struct AnalysisConfig {
67 pub enable_disassembly: bool,
69 #[cfg(any(feature = "disasm-capstone", feature = "disasm-iced"))]
71 pub disassembly_engine: DisassemblyEngine,
72 pub enable_control_flow: bool,
74 pub enable_entropy: bool,
76 pub enable_symbols: bool,
78 pub max_analysis_size: usize,
80 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, architecture_hint: None,
95 }
96 }
97}
98
99impl BinaryAnalyzer {
100 pub fn new() -> Self {
102 Self::with_config(AnalysisConfig::default())
103 }
104
105 pub fn with_config(config: AnalysisConfig) -> Self {
107 Self { config }
108 }
109
110 pub fn config(&self) -> &AnalysisConfig {
112 &self.config
113 }
114
115 pub fn analyze(&self, data: &[u8]) -> Result<AnalysisResult> {
117 let binary_file = BinaryFile::parse(data)?;
118 self.analyze_binary(&binary_file)
119 }
120
121 pub fn analyze_binary(&self, binary: &BinaryFile) -> Result<AnalysisResult> {
123 #[allow(unused_mut)] 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 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
190pub struct BinaryFile {
192 data: Vec<u8>,
193 parsed: Box<dyn BinaryFormatTrait>,
194}
195
196impl BinaryFile {
197 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 pub fn format(&self) -> BinaryFormat {
210 self.parsed.format_type()
211 }
212
213 pub fn architecture(&self) -> Architecture {
215 self.parsed.architecture()
216 }
217
218 pub fn entry_point(&self) -> Option<u64> {
220 self.parsed.entry_point()
221 }
222
223 pub fn sections(&self) -> &[Section] {
225 self.parsed.sections()
226 }
227
228 pub fn symbols(&self) -> &[Symbol] {
230 self.parsed.symbols()
231 }
232
233 pub fn imports(&self) -> &[Import] {
235 self.parsed.imports()
236 }
237
238 pub fn exports(&self) -> &[Export] {
240 self.parsed.exports()
241 }
242
243 pub fn metadata(&self) -> &BinaryMetadata {
245 self.parsed.metadata()
246 }
247
248 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}