Add JSON output format support

This commit is contained in:
Adir Shitrit
2025-11-08 12:40:55 +02:00
parent 662d239deb
commit d89444a268
7 changed files with 59 additions and 31 deletions

View File

@@ -15,3 +15,5 @@ anyhow = "1.0"
thiserror = "1.0" thiserror = "1.0"
log = "0.4" log = "0.4"
env_logger = "0.11" env_logger = "0.11"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@@ -10,3 +10,5 @@ ghost-core = { path = "../ghost-core" }
anyhow.workspace = true anyhow.workspace = true
env_logger.workspace = true env_logger.workspace = true
log.workspace = true log.workspace = true
serde.workspace = true
serde_json.workspace = true

View File

@@ -2,6 +2,7 @@ use anyhow::Result;
use clap::{Arg, Command}; use clap::{Arg, Command};
use ghost_core::{memory, process, thread, DetectionEngine, ThreatLevel}; use ghost_core::{memory, process, thread, DetectionEngine, ThreatLevel};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use serde_json;
use std::time::Instant; use std::time::Instant;
fn main() -> Result<()> { fn main() -> Result<()> {
@@ -190,32 +191,52 @@ fn main() -> Result<()> {
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 = if detections.is_empty() { let output_content = match format.as_str() {
"No suspicious activity detected.".to_string() "json" => {
} else { if detections.is_empty() {
let mut content = format!("Found {} suspicious processes:\n\n", detections.len()); serde_json::json!({
"status": "clean",
for detection in detections { "message": "No suspicious activity detected",
let level_str = match detection.threat_level { "detections": []
ThreatLevel::Suspicious => "SUSPICIOUS", }).to_string()
ThreatLevel::Malicious => "MALICIOUS", } else {
_ => "CLEAN", serde_json::json!({
}; "status": "suspicious",
"message": format!("Found {} suspicious processes", detections.len()),
content.push_str(&format!( "detections": &detections
"[{}] {} (PID: {}) - Confidence: {:.1}%\n", }).to_string()
level_str, }
detection.process.name, }
detection.process.pid, _ => {
detection.confidence * 100.0 // Default table format
)); if detections.is_empty() {
"No suspicious activity detected.".to_string()
for indicator in &detection.indicators { } else {
content.push_str(&format!(" - {}\n", indicator)); let mut content = format!("Found {} suspicious processes:\n\n", detections.len());
for detection in &detections {
let level_str = match detection.threat_level {
ThreatLevel::Suspicious => "SUSPICIOUS",
ThreatLevel::Malicious => "MALICIOUS",
_ => "CLEAN",
};
content.push_str(&format!(
"[{}] {} (PID: {}) - Confidence: {:.1}%\n",
level_str,
detection.process.name,
detection.process.pid,
detection.confidence * 100.0
));
for indicator in &detection.indicators {
content.push_str(&format!(" - {}\n", indicator));
}
content.push('\n');
}
content
} }
content.push('\n');
} }
content
}; };
if let Some(output_path) = output_file { if let Some(output_path) = output_file {

View File

@@ -5,16 +5,17 @@ use crate::{
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use crate::EbpfDetector; use crate::EbpfDetector;
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThreatLevel { pub enum ThreatLevel {
Clean, Clean,
Suspicious, Suspicious,
Malicious, Malicious,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionResult { pub struct DetectionResult {
pub process: ProcessInfo, pub process: ProcessInfo,
pub threat_level: ThreatLevel, pub threat_level: ThreatLevel,

View File

@@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::time::{SystemTime, Duration}; use std::time::{SystemTime, Duration};
use serde::{Deserialize, Serialize};
use crate::{ProcessInfo, MemoryRegion, ThreadInfo, MemoryProtection}; use crate::{ProcessInfo, MemoryRegion, ThreadInfo, MemoryProtection};
/// Advanced Evasion Detection Module /// Advanced Evasion Detection Module
@@ -11,7 +12,7 @@ pub struct EvasionDetector {
obfuscation_detector: ObfuscationDetector, obfuscation_detector: ObfuscationDetector,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvasionResult { pub struct EvasionResult {
pub evasion_techniques: Vec<EvasionTechnique>, pub evasion_techniques: Vec<EvasionTechnique>,
pub confidence: f32, pub confidence: f32,
@@ -19,7 +20,7 @@ pub struct EvasionResult {
pub anti_analysis_indicators: Vec<String>, pub anti_analysis_indicators: Vec<String>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvasionTechnique { pub struct EvasionTechnique {
pub technique_name: String, pub technique_name: String,
pub mitre_id: String, pub mitre_id: String,
@@ -29,7 +30,7 @@ pub struct EvasionTechnique {
pub severity: EvasionSeverity, pub severity: EvasionSeverity,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EvasionSeverity { pub enum EvasionSeverity {
Low, // Basic evasion attempts Low, // Basic evasion attempts
Medium, // Moderate sophistication Medium, // Moderate sophistication

View File

@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessInfo { pub struct ProcessInfo {
pub pid: u32, pub pid: u32,
pub ppid: u32, pub ppid: u32,

View File

@@ -38,7 +38,7 @@ pub enum IocType {
Mutex, Mutex,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreatContext { pub struct ThreatContext {
pub matched_iocs: Vec<IndicatorOfCompromise>, pub matched_iocs: Vec<IndicatorOfCompromise>,
pub threat_actor: Option<ThreatActor>, pub threat_actor: Option<ThreatActor>,