Fix CLI formatting and imports

This commit is contained in:
pandaadir05
2025-11-20 14:28:15 +02:00
parent 9a9e94af8d
commit 6b6fbd6878

View File

@@ -13,13 +13,15 @@ fn main() -> Result<()> {
let matches = Command::new("ghost") let matches = Command::new("ghost")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.about("Cross-Platform Process Injection Detection Framework") .about("Cross-Platform Process Injection Detection Framework")
.long_about("Ghost scans running processes for signs of code injection, \ .long_about(
"Ghost scans running processes for signs of code injection, \
process hollowing, and other malicious techniques. \ process hollowing, and other malicious techniques. \
Supports Windows and Linux platforms with kernel-level monitoring.\n\n\ Supports Windows and Linux platforms with kernel-level monitoring.\n\n\
Exit Codes:\n\ Exit Codes:\n\
0 - No suspicious activity detected\n\ 0 - No suspicious activity detected\n\
1 - Suspicious processes found\n\ 1 - Suspicious processes found\n\
2 - Error occurred during scanning") 2 - Error occurred during scanning",
)
.arg( .arg(
Arg::new("format") Arg::new("format")
.short('f') .short('f')
@@ -27,34 +29,34 @@ fn main() -> Result<()> {
.value_name("FORMAT") .value_name("FORMAT")
.help("Output format: table, json") .help("Output format: table, json")
.default_value("table") .default_value("table")
.value_parser(["table", "json", "csv"]) .value_parser(["table", "json", "csv"]),
) )
.arg( .arg(
Arg::new("verbose") Arg::new("verbose")
.short('v') .short('v')
.long("verbose") .long("verbose")
.help("Enable verbose output") .help("Enable verbose output")
.action(clap::ArgAction::SetTrue) .action(clap::ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new("pid") Arg::new("pid")
.short('p') .short('p')
.long("pid") .long("pid")
.value_name("PID") .value_name("PID")
.help("Target specific process ID") .help("Target specific process ID"),
) )
.arg( .arg(
Arg::new("process") Arg::new("process")
.long("process") .long("process")
.value_name("NAME") .value_name("NAME")
.help("Target specific process name") .help("Target specific process name"),
) )
.arg( .arg(
Arg::new("output") Arg::new("output")
.short('o') .short('o')
.long("output") .long("output")
.value_name("FILE") .value_name("FILE")
.help("Write output to file instead of stdout"), .help("Write output to file instead of stdout"),
) )
.arg( .arg(
Arg::new("debug") Arg::new("debug")
@@ -147,22 +149,25 @@ fn main() -> Result<()> {
format, verbose, quiet, target_pid, target_process, config_file); format, verbose, quiet, target_pid, target_process, config_file);
if !quiet { if !quiet {
println!("Ghost v{} - Process Injection Detection\n", env!("CARGO_PKG_VERSION")); println!(
"Ghost v{} - Process Injection Detection\n",
env!("CARGO_PKG_VERSION")
);
} }
let scan_start = Instant::now(); let _scan_start = Instant::now();
let mut engine = DetectionEngine::with_config(config).map_err(|e| { let mut engine = DetectionEngine::with_config(config).map_err(|e| {
error!("Failed to initialize detection engine: {}", e); error!("Failed to initialize detection engine: {}", e);
anyhow::anyhow!("Detection engine initialization failed: {}", e) anyhow::anyhow!("Detection engine initialization failed: {}", e)
})?; })?;
// Display MITRE ATT&CK statistics if requested // Display MITRE ATT&CK statistics if requested
if mitre_stats { if mitre_stats {
if !quiet { if !quiet {
println!("MITRE ATT&CK Framework Statistics:"); println!("MITRE ATT&CK Framework Statistics:");
println!("=================================="); println!("==================================");
} }
let (techniques, tactics, actors) = engine.get_mitre_stats(); let (techniques, tactics, actors) = engine.get_mitre_stats();
if !quiet { if !quiet {
println!("Techniques: {}", techniques); println!("Techniques: {}", techniques);
@@ -177,26 +182,23 @@ fn main() -> Result<()> {
println!(" - APT29 (Cozy Bear)"); println!(" - APT29 (Cozy Bear)");
println!(); println!();
} }
// If only showing stats, exit here // If only showing stats, exit here
if mitre_stats && target_pid.is_none() && target_process.is_none() { if mitre_stats && target_pid.is_none() && target_process.is_none() {
return Ok(()); return Ok(());
} }
} }
let processes = if let Some(pid_str) = target_pid { let processes = if let Some(pid_str) = target_pid {
let pid: u32 = pid_str.parse().map_err(|e| { let pid: u32 = pid_str.parse().map_err(|e| {
error!("Invalid PID format '{}': {}", pid_str, e); error!("Invalid PID format '{}': {}", pid_str, e);
anyhow::anyhow!("Invalid PID format: {}", pid_str) anyhow::anyhow!("Invalid PID format: {}", pid_str)
})?; })?;
info!("Targeting specific process ID: {}", pid); info!("Targeting specific process ID: {}", pid);
let all_processes = process::enumerate_processes()?; let all_processes = process::enumerate_processes()?;
let filtered: Vec<_> = all_processes let filtered: Vec<_> = all_processes.into_iter().filter(|p| p.pid == pid).collect();
.into_iter()
.filter(|p| p.pid == pid)
.collect();
if filtered.is_empty() { if filtered.is_empty() {
warn!("No process found with PID {}", pid); warn!("No process found with PID {}", pid);
if !quiet { if !quiet {
@@ -213,20 +215,36 @@ fn main() -> Result<()> {
.into_iter() .into_iter()
.filter(|p| p.name.to_lowercase().contains(&process_name.to_lowercase())) .filter(|p| p.name.to_lowercase().contains(&process_name.to_lowercase()))
.collect(); .collect();
if filtered.is_empty() { if filtered.is_empty() {
warn!("No processes found matching name: {}", process_name); warn!("No processes found matching name: {}", process_name);
if !quiet { if !quiet {
println!("Warning: No processes found matching name: {}", process_name); println!(
"Warning: No processes found matching name: {}",
process_name
);
} }
} else { } else {
info!("Found {} processes matching name: {}", filtered.len(), process_name); info!(
debug!("Matching processes: {:?}", filtered.iter().map(|p| format!("{} ({})", p.name, p.pid)).collect::<Vec<_>>()); "Found {} processes matching name: {}",
filtered.len(),
process_name
);
debug!(
"Matching processes: {:?}",
filtered
.iter()
.map(|p| format!("{} ({})", p.name, p.pid))
.collect::<Vec<_>>()
);
} }
filtered filtered
} else { } else {
let all_processes = process::enumerate_processes()?; let all_processes = process::enumerate_processes()?;
info!("Enumerating all processes, found {} total", all_processes.len()); info!(
"Enumerating all processes, found {} total",
all_processes.len()
);
all_processes all_processes
}; };
@@ -244,19 +262,26 @@ fn main() -> Result<()> {
debug!("Skipping safe system process: {}", proc.name); debug!("Skipping safe system process: {}", proc.name);
continue; continue;
} }
scanned_count += 1; scanned_count += 1;
debug!("Scanning process: {} (PID: {})", proc.name, proc.pid); debug!("Scanning process: {} (PID: {})", proc.name, proc.pid);
match memory::enumerate_memory_regions(proc.pid) { match memory::enumerate_memory_regions(proc.pid) {
Ok(regions) => { Ok(regions) => {
debug!("Found {} memory regions for process {}", regions.len(), proc.name); debug!(
"Found {} memory regions for process {}",
regions.len(),
proc.name
);
// Get thread information if available // Get thread information if available
let threads = thread::enumerate_threads(proc.pid).ok(); let threads = thread::enumerate_threads(proc.pid).ok();
let result = engine.analyze_process(proc, &regions, threads.as_deref()); let result = engine.analyze_process(proc, &regions, threads.as_deref());
if result.threat_level != ThreatLevel::Clean { if result.threat_level != ThreatLevel::Clean {
warn!("Suspicious activity detected in process {} (PID: {})", proc.name, proc.pid); warn!(
"Suspicious activity detected in process {} (PID: {})",
proc.name, proc.pid
);
detections.push(result); detections.push(result);
} else { } else {
debug!("Process {} (PID: {}) is clean", proc.name, proc.pid); debug!("Process {} (PID: {}) is clean", proc.name, proc.pid);
@@ -264,9 +289,15 @@ fn main() -> Result<()> {
} }
Err(e) => { Err(e) => {
error_count += 1; error_count += 1;
error!("Failed to scan process {} (PID: {}): {}", proc.name, proc.pid, e); error!(
"Failed to scan process {} (PID: {}): {}",
proc.name, proc.pid, e
);
if verbose && !quiet { if verbose && !quiet {
println!("Warning: Could not scan process {} (PID: {})", proc.name, proc.pid); println!(
"Warning: Could not scan process {} (PID: {})",
proc.name, proc.pid
);
} }
} }
} }
@@ -277,7 +308,11 @@ fn main() -> Result<()> {
println!("Scan completed with {} access errors", error_count); println!("Scan completed with {} access errors", error_count);
} }
info!("Scan completed: {} processes scanned, {} suspicious processes found", scanned_count, detections.len()); info!(
"Scan completed: {} processes scanned, {} suspicious processes found",
scanned_count,
detections.len()
);
// Handle output // Handle output
let output_content = match format.as_str() { let output_content = match format.as_str() {
@@ -287,13 +322,15 @@ fn main() -> Result<()> {
"status": "clean", "status": "clean",
"message": "No suspicious activity detected", "message": "No suspicious activity detected",
"detections": [] "detections": []
}).to_string() })
.to_string()
} else { } else {
serde_json::json!({ serde_json::json!({
"status": "suspicious", "status": "suspicious",
"message": format!("Found {} suspicious processes", detections.len()), "message": format!("Found {} suspicious processes", detections.len()),
"detections": &detections "detections": &detections
}).to_string() })
.to_string()
} }
} }
_ => { _ => {
@@ -331,7 +368,7 @@ fn main() -> Result<()> {
if let Some(output_path) = output_file { if let Some(output_path) = output_file {
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
info!("Writing results to file: {}", output_path); info!("Writing results to file: {}", output_path);
let mut file = File::create(output_path)?; let mut file = File::create(output_path)?;
file.write_all(output_content.as_bytes())?; file.write_all(output_content.as_bytes())?;
@@ -347,11 +384,11 @@ fn main() -> Result<()> {
// Exit with appropriate code for automation // Exit with appropriate code for automation
let exit_code = if error_count > 0 { let exit_code = if error_count > 0 {
2 // Error occurred during scanning 2 // Error occurred during scanning
} else if !detections.is_empty() { } else if !detections.is_empty() {
1 // Suspicious processes found 1 // Suspicious processes found
} else { } else {
0 // Clean scan 0 // Clean scan
}; };
debug!("Exiting with code: {}", exit_code); debug!("Exiting with code: {}", exit_code);