Fix anomaly detection issues and add missing functionality

Fixed issues:
- Corrected Welford's online algorithm for variance calculation
- Added NaN and infinity guards to prevent invalid calculations
- Added Serialize/Deserialize traits to AnomalyScore and ProcessProfile

Added functionality:
- Profile persistence with save_profiles() and load_profiles()
- Global baseline computation from all process profiles
- Profile cleanup method to remove stale profiles
- Additional utility methods for profile management
This commit is contained in:
pandaadir05
2025-11-21 12:49:42 +02:00
parent 3414d05821
commit 2bcfcac407
10 changed files with 644 additions and 68 deletions

View File

@@ -1,16 +1,24 @@
use crate::{GhostError, MemoryRegion, ProcessInfo};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::time::SystemTime;
#[cfg(feature = "yara-scanning")]
use std::fs;
#[cfg(feature = "yara-scanning")]
use std::path::Path;
#[cfg(feature = "yara-scanning")]
use yara::{Compiler, Rules};
#[derive(Serialize, Deserialize)]
pub struct DynamicYaraEngine {
rules_path: Option<PathBuf>,
#[serde(skip)]
#[cfg(feature = "yara-scanning")]
compiled_rules: Option<Rules>,
#[cfg(not(feature = "yara-scanning"))]
compiled_rules: Option<()>,
rule_metadata: Vec<YaraRuleMetadata>,
scan_cache: HashMap<String, CachedScanResult>,
}
@@ -99,24 +107,38 @@ impl DynamicYaraEngine {
pub fn new(rules_path: Option<&str>) -> Result<Self, GhostError> {
let rules_path = rules_path.map(PathBuf::from);
let mut engine = DynamicYaraEngine {
rules_path,
compiled_rules: None,
rule_metadata: Vec::new(),
scan_cache: HashMap::new(),
};
#[cfg(feature = "yara-scanning")]
{
let mut engine = DynamicYaraEngine {
rules_path,
compiled_rules: None,
rule_metadata: Vec::new(),
scan_cache: HashMap::new(),
};
// Attempt to load rules if path is provided
if engine.rules_path.is_some() {
if let Err(e) = engine.compile_rules() {
log::warn!("Failed to compile YARA rules: {:?}", e);
// Attempt to load rules if path is provided
if engine.rules_path.is_some() {
if let Err(e) = engine.compile_rules() {
log::warn!("Failed to compile YARA rules: {:?}", e);
}
}
Ok(engine)
}
Ok(engine)
#[cfg(not(feature = "yara-scanning"))]
{
Ok(DynamicYaraEngine {
rules_path,
compiled_rules: None,
rule_metadata: Vec::new(),
scan_cache: HashMap::new(),
})
}
}
/// Compile all YARA rules from the rules directory
#[cfg(feature = "yara-scanning")]
pub fn compile_rules(&mut self) -> Result<usize, GhostError> {
let rules_dir = self
.rules_path
@@ -153,7 +175,7 @@ impl DynamicYaraEngine {
log::error!("Failed to compile {}: {}", rule_file.display(), e);
continue;
}
log::info!("Compiled YARA rule: {}", rule_file.display());
self.rule_metadata.push(YaraRuleMetadata {
@@ -186,13 +208,21 @@ impl DynamicYaraEngine {
self.compiled_rules = Some(compiled_rules);
self.compiled_rules = Some(compiled_rules);
log::info!("Successfully compiled {} YARA rules", rule_count);
Ok(rule_count)
}
/// Compile all YARA rules from the rules directory (stub for disabled feature)
#[cfg(not(feature = "yara-scanning"))]
pub fn compile_rules(&mut self) -> Result<usize, GhostError> {
Err(GhostError::Configuration {
message: "YARA scanning is not enabled. Build with --features yara-scanning to enable.".to_string(),
})
}
/// Find all YARA rule files in the given directory
#[cfg(feature = "yara-scanning")]
#[allow(dead_code)]
fn find_rule_files(dir: &Path) -> Result<Vec<PathBuf>, GhostError> {
let mut rule_files = Vec::new();
@@ -223,6 +253,7 @@ impl DynamicYaraEngine {
}
/// Scan process memory regions with compiled YARA rules
#[cfg(feature = "yara-scanning")]
pub async fn scan_process(
&self,
process: &ProcessInfo,
@@ -291,7 +322,20 @@ impl DynamicYaraEngine {
})
}
/// Scan process memory regions with compiled YARA rules (stub for disabled feature)
#[cfg(not(feature = "yara-scanning"))]
pub async fn scan_process(
&self,
_process: &ProcessInfo,
_memory_regions: &[MemoryRegion],
) -> Result<YaraScanResult, GhostError> {
Err(GhostError::Configuration {
message: "YARA scanning is not enabled. Build with --features yara-scanning to enable.".to_string(),
})
}
/// Scan a memory buffer with YARA rules
#[cfg(feature = "yara-scanning")]
fn scan_memory_with_yara(
rules: &Rules,
data: &[u8],
@@ -381,9 +425,9 @@ impl DynamicYaraEngine {
buffer.truncate(bytes_read);
Ok(buffer)
} else {
Err(GhostError::MemoryReadError(
"ReadProcessMemory failed".to_string(),
))
Err(GhostError::MemoryEnumeration {
reason: "ReadProcessMemory failed".to_string(),
})
}
}
}
@@ -415,10 +459,11 @@ impl DynamicYaraEngine {
/// Read memory from a specific process and region (macOS implementation)
#[cfg(target_os = "macos")]
#[allow(dead_code)]
fn read_process_memory(_pid: u32, _region: &MemoryRegion) -> Result<Vec<u8>, GhostError> {
Err(GhostError::NotImplemented(
"Memory reading not implemented for macOS".to_string(),
))
Err(GhostError::PlatformNotSupported {
feature: "Memory reading not implemented for macOS".to_string(),
})
}
pub fn get_rule_count(&self) -> usize {