Files
ghost/ghost-cli/src/main.rs

152 lines
4.9 KiB
Rust
Raw Normal View History

2025-11-07 18:05:07 +02:00
use anyhow::Result;
use clap::{Arg, Command};
use ghost_core::{memory, process, thread, DetectionEngine, ThreatLevel};
use std::time::Instant;
2025-11-07 18:05:07 +02:00
fn main() -> Result<()> {
env_logger::init();
let matches = Command::new("ghost")
.version("0.1.0")
.about("Cross-Platform Process Injection Detection Framework")
.long_about("Ghost scans running processes for signs of code injection, \
process hollowing, and other malicious techniques. \
Supports Windows and Linux platforms with kernel-level monitoring.")
.arg(
Arg::new("format")
.short('f')
.long("format")
.value_name("FORMAT")
.help("Output format: table, json")
.default_value("table")
2025-11-08 12:21:27 +02:00
.value_parser(["table", "json", "csv"])
)
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.help("Enable verbose output")
.action(clap::ArgAction::SetTrue)
)
2025-11-08 12:20:02 +02:00
.arg(
Arg::new("pid")
.short('p')
.long("pid")
.value_name("PID")
.help("Target specific process ID")
)
2025-11-08 12:29:21 +02:00
.arg(
Arg::new("output")
.short('o')
.long("output")
.value_name("FILE")
.help("Write results to file instead of stdout")
)
.get_matches();
let format = matches.get_one::<String>("format").unwrap();
let verbose = matches.get_flag("verbose");
2025-11-08 12:20:02 +02:00
let target_pid = matches.get_one::<String>("pid");
2025-11-08 12:29:21 +02:00
let output_file = matches.get_one::<String>("output");
2025-11-07 18:08:21 +02:00
println!("Ghost v0.1.0 - Process Injection Detection\n");
2025-11-07 18:05:07 +02:00
let scan_start = Instant::now();
2025-11-07 18:08:21 +02:00
let mut engine = DetectionEngine::new();
2025-11-08 12:20:02 +02:00
let processes = if let Some(pid_str) = target_pid {
let pid: u32 = pid_str.parse().map_err(|_| anyhow::anyhow!("Invalid PID format: {}", pid_str))?;
let all_processes = process::enumerate_processes()?;
let filtered: Vec<_> = all_processes
2025-11-08 12:20:02 +02:00
.into_iter()
.filter(|p| p.pid == pid)
.collect();
if filtered.is_empty() {
println!("Warning: No process found with PID {}", pid);
}
filtered
2025-11-08 12:20:02 +02:00
} else {
process::enumerate_processes()?
};
2025-11-07 18:05:07 +02:00
2025-11-07 18:08:21 +02:00
println!("Scanning {} processes...\n", processes.len());
2025-11-07 18:05:07 +02:00
2025-11-07 18:08:21 +02:00
let mut detections = Vec::new();
let mut scanned_count = 0;
let mut error_count = 0;
2025-11-07 18:08:21 +02:00
for proc in &processes {
// Skip known safe system processes for performance
if proc.name == "csrss.exe" || proc.name == "wininit.exe" || proc.name == "winlogon.exe" {
continue;
}
scanned_count += 1;
match memory::enumerate_memory_regions(proc.pid) {
Ok(regions) => {
// Get thread information if available
let threads = thread::enumerate_threads(proc.pid).ok();
let result = engine.analyze_process(proc, &regions, threads.as_deref());
2025-11-07 18:08:21 +02:00
if result.threat_level != ThreatLevel::Clean {
detections.push(result);
}
}
Err(_) => {
error_count += 1;
if verbose {
println!("Warning: Could not scan process {} (PID: {})", proc.name, proc.pid);
}
2025-11-07 18:08:21 +02:00
}
}
}
if verbose && error_count > 0 {
println!("Scan completed with {} access errors", error_count);
}
2025-11-08 12:29:21 +02:00
// Handle output
let output_content = if detections.is_empty() {
"No suspicious activity detected.".to_string()
2025-11-07 18:08:21 +02:00
} else {
2025-11-08 12:29:21 +02:00
let mut content = format!("Found {} suspicious processes:\n\n", detections.len());
2025-11-07 18:08:21 +02:00
for detection in detections {
let level_str = match detection.threat_level {
ThreatLevel::Suspicious => "SUSPICIOUS",
ThreatLevel::Malicious => "MALICIOUS",
_ => "CLEAN",
};
2025-11-08 12:29:21 +02:00
content.push_str(&format!(
"[{}] {} (PID: {}) - Confidence: {:.1}%\n",
2025-11-07 18:08:21 +02:00
level_str,
detection.process.name,
detection.process.pid,
detection.confidence * 100.0
2025-11-08 12:29:21 +02:00
));
2025-11-07 18:05:07 +02:00
2025-11-07 18:08:21 +02:00
for indicator in &detection.indicators {
2025-11-08 12:29:21 +02:00
content.push_str(&format!(" - {}\n", indicator));
2025-11-07 18:05:07 +02:00
}
2025-11-08 12:29:21 +02:00
content.push('\n');
2025-11-07 18:05:07 +02:00
}
2025-11-08 12:29:21 +02:00
content
};
if let Some(output_path) = output_file {
use std::fs::File;
use std::io::Write;
let mut file = File::create(output_path)?;
file.write_all(output_content.as_bytes())?;
println!("Results written to {}", output_path);
} else {
print!("{}", output_content);
2025-11-07 18:05:07 +02:00
}
Ok(())
}