use anyhow::{Context, Result};
use clap::Parser;
use colored::Colorize;
use std::fs;
use std::path::PathBuf;
use binsleuth::analyzer::AnalysisReport;
use binsleuth::analyzer::hardening::CheckResult;
use binsleuth::report;
use binsleuth::report::terminal::TerminalReporter;
#[derive(Parser, Debug)]
#[command(
name = "binsleuth",
version,
about = "Analyze ELF/PE binaries for security hardening flags and entropy anomalies",
long_about = "BinSleuth inspects compiled binaries for:\n\
• Security hardening flags (NX, PIE, RELRO, Stack Canary)\n\
• Shannon entropy per section (detects packing/encryption)\n\
• Dangerous symbol usage (system(), execve(), mprotect(), …)\n\
• Debug symbol / DWARF info presence"
)]
struct Cli {
#[arg(value_name = "FILE")]
path: PathBuf,
#[arg(short, long)]
verbose: bool,
#[arg(long)]
json: bool,
#[arg(long)]
strict: bool,
}
fn main() {
match run() {
Err(e) => {
eprintln!("{}: {}", "error".red().bold(), e);
std::process::exit(1);
}
Ok(strict_fail) => {
if strict_fail {
std::process::exit(2);
}
}
}
}
fn run() -> Result<bool> {
let cli = Cli::parse();
let path = &cli.path;
if !path.exists() {
anyhow::bail!(
"File not found: '{}'\nPlease provide a valid path to an ELF or PE binary.",
path.display()
);
}
let data =
fs::read(path).with_context(|| format!("Failed to read file: '{}'", path.display()))?;
if data.is_empty() {
anyhow::bail!("File '{}' is empty.", path.display());
}
let report = AnalysisReport::analyze(&data).with_context(|| {
format!(
"Failed to parse '{}'. Is it a valid ELF or PE binary?",
path.display()
)
})?;
if cli.json {
report::json::print_json(path, &report);
} else {
let reporter = TerminalReporter::new(cli.verbose);
reporter.print_report(path, &report.hardening, &report.sections);
}
let strict_fail = cli.strict && has_security_issues(&report);
Ok(strict_fail)
}
fn has_security_issues(report: &AnalysisReport) -> bool {
let h = &report.hardening;
h.nx == CheckResult::Disabled
|| h.pie == CheckResult::Disabled
|| h.stack_canary == CheckResult::Disabled
|| h.relro == CheckResult::Disabled
|| !h.dangerous_symbols.is_empty()
}