use threatflux_binary_analysis::{
analysis::control_flow::{AnalysisConfig, ControlFlowAnalyzer},
BinaryFile,
};
mod util;
use util::{read_binary_from_args, Result};
fn main() -> Result<()> {
let data = read_binary_from_args()?;
let binary = BinaryFile::parse(&data)?;
println!("Binary format: {:?}", binary.format());
println!("Architecture: {:?}", binary.architecture());
let config = AnalysisConfig {
max_instructions: 5000,
max_depth: 50,
detect_loops: true,
calculate_metrics: true,
enable_call_graph: false,
enable_cognitive_complexity: true,
enable_advanced_loops: true,
call_graph_config: None,
};
let analyzer = ControlFlowAnalyzer::with_config(binary.architecture(), config);
println!("\n=== Control Flow Analysis ===");
match analyzer.analyze_binary(&binary) {
Ok(cfgs) => {
println!("Found {} control flow graphs", cfgs.len());
for (i, cfg) in cfgs.iter().enumerate().take(10) {
println!("\nFunction {}: {}", i + 1, cfg.function.name);
println!(
" Address range: 0x{:x} - 0x{:x}",
cfg.function.start_address, cfg.function.end_address
);
println!(" Size: {} bytes", cfg.function.size);
println!(" Type: {:?}", cfg.function.function_type);
if let Some(calling_convention) = &cfg.function.calling_convention {
println!(" Calling convention: {calling_convention}");
}
println!(" Basic blocks: {}", cfg.basic_blocks.len());
for (j, block) in cfg.basic_blocks.iter().enumerate().take(5) {
println!(
" Block {}: 0x{:x} - 0x{:x} ({} instructions)",
j,
block.start_address,
block.end_address,
block.instructions.len()
);
println!(" Successors: {:?}", block.successors);
println!(" Predecessors: {:?}", block.predecessors);
for (k, instr) in block.instructions.iter().enumerate().take(3) {
println!(
" {}: 0x{:x} {} {}",
k, instr.address, instr.mnemonic, instr.operands
);
}
if block.instructions.len() > 3 {
println!(
" ... and {} more instructions",
block.instructions.len() - 3
);
}
}
if cfg.basic_blocks.len() > 5 {
println!(" ... and {} more blocks", cfg.basic_blocks.len() - 5);
}
let metrics = &cfg.complexity;
println!(" Complexity metrics:");
println!(
" Cyclomatic complexity: {}",
metrics.cyclomatic_complexity
);
println!(" Basic block count: {}", metrics.basic_block_count);
println!(" Edge count: {}", metrics.edge_count);
println!(" Nesting depth: {}", metrics.nesting_depth);
println!(" Loop count: {}", metrics.loop_count);
let complexity_level = assess_complexity(metrics.cyclomatic_complexity);
println!(" Complexity level: {complexity_level}");
}
if cfgs.len() > 10 {
println!("\n... and {} more functions", cfgs.len() - 10);
}
println!("\n=== Overall Statistics ===");
let total_blocks: usize = cfgs.iter().map(|cfg| cfg.basic_blocks.len()).sum();
let total_complexity: u32 = cfgs
.iter()
.map(|cfg| cfg.complexity.cyclomatic_complexity)
.sum();
let total_loops: u32 = cfgs.iter().map(|cfg| cfg.complexity.loop_count).sum();
println!("Total functions analyzed: {}", cfgs.len());
println!("Total basic blocks: {total_blocks}");
println!("Total cyclomatic complexity: {total_complexity}");
println!("Total loops detected: {total_loops}");
if !cfgs.is_empty() {
let avg_complexity = total_complexity as f64 / cfgs.len() as f64;
let avg_blocks = total_blocks as f64 / cfgs.len() as f64;
println!("Average complexity per function: {avg_complexity:.2}");
println!("Average blocks per function: {avg_blocks:.2}");
}
let mut sorted_cfgs = cfgs.clone();
sorted_cfgs.sort_by_key(|cfg| std::cmp::Reverse(cfg.complexity.cyclomatic_complexity));
println!("\n=== Most Complex Functions ===");
for (i, cfg) in sorted_cfgs.iter().take(5).enumerate() {
println!(
"{}. {} (complexity: {})",
i + 1,
cfg.function.name,
cfg.complexity.cyclomatic_complexity
);
}
}
Err(e) => {
eprintln!("Control flow analysis failed: {e}");
return Err(e.into());
}
}
Ok(())
}
fn assess_complexity(complexity: u32) -> &'static str {
match complexity {
1..=10 => "Low",
11..=20 => "Moderate",
21..=50 => "High",
_ => "Very High",
}
}