Standardize import ordering and code formatting
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::{GhostError, MemoryRegion, ProcessInfo, Result};
|
||||
use crate::{MemoryRegion, ProcessInfo, Result};
|
||||
|
||||
#[cfg(windows)]
|
||||
use crate::memory::{validate_pe_header, read_pe_header_info, PEHeaderValidation};
|
||||
use crate::memory::{read_pe_header_info, validate_pe_header, PEHeaderValidation};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HollowingDetection {
|
||||
@@ -15,12 +15,25 @@ pub struct HollowingDetection {
|
||||
pub enum HollowingIndicator {
|
||||
UnmappedMainImage,
|
||||
SuspiciousImageBase,
|
||||
MemoryLayoutAnomaly { expected_size: usize, actual_size: usize },
|
||||
MemoryLayoutAnomaly {
|
||||
expected_size: usize,
|
||||
actual_size: usize,
|
||||
},
|
||||
MismatchedPEHeader,
|
||||
InvalidPEHeader { validation: String },
|
||||
CorruptedPEStructure { address: usize, reason: String },
|
||||
UnusualEntryPoint { address: usize },
|
||||
SuspiciousMemoryGaps { gap_count: usize, largest_gap: usize },
|
||||
InvalidPEHeader {
|
||||
validation: String,
|
||||
},
|
||||
CorruptedPEStructure {
|
||||
address: usize,
|
||||
reason: String,
|
||||
},
|
||||
UnusualEntryPoint {
|
||||
address: usize,
|
||||
},
|
||||
SuspiciousMemoryGaps {
|
||||
gap_count: usize,
|
||||
largest_gap: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HollowingIndicator {
|
||||
@@ -28,8 +41,15 @@ impl std::fmt::Display for HollowingIndicator {
|
||||
match self {
|
||||
Self::UnmappedMainImage => write!(f, "Main executable image appears unmapped"),
|
||||
Self::SuspiciousImageBase => write!(f, "Image base address is suspicious"),
|
||||
Self::MemoryLayoutAnomaly { expected_size, actual_size } => {
|
||||
write!(f, "Memory layout anomaly: expected {:#x}, found {:#x}", expected_size, actual_size)
|
||||
Self::MemoryLayoutAnomaly {
|
||||
expected_size,
|
||||
actual_size,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Memory layout anomaly: expected {:#x}, found {:#x}",
|
||||
expected_size, actual_size
|
||||
)
|
||||
}
|
||||
Self::MismatchedPEHeader => write!(f, "PE header mismatch detected"),
|
||||
Self::InvalidPEHeader { validation } => {
|
||||
@@ -41,14 +61,22 @@ impl std::fmt::Display for HollowingIndicator {
|
||||
Self::UnusualEntryPoint { address } => {
|
||||
write!(f, "Entry point at unusual location: {:#x}", address)
|
||||
}
|
||||
Self::SuspiciousMemoryGaps { gap_count, largest_gap } => {
|
||||
write!(f, "{} memory gaps detected, largest: {:#x} bytes", gap_count, largest_gap)
|
||||
Self::SuspiciousMemoryGaps {
|
||||
gap_count,
|
||||
largest_gap,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{} memory gaps detected, largest: {:#x} bytes",
|
||||
gap_count, largest_gap
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process hollowing detection engine
|
||||
#[derive(Debug)]
|
||||
pub struct HollowingDetector;
|
||||
|
||||
impl HollowingDetector {
|
||||
@@ -147,7 +175,13 @@ impl HollowingDetector {
|
||||
// Calculate total executable memory size
|
||||
let total_executable: usize = regions
|
||||
.iter()
|
||||
.filter(|r| matches!(r.protection, crate::MemoryProtection::ReadExecute | crate::MemoryProtection::ReadWriteExecute))
|
||||
.filter(|r| {
|
||||
matches!(
|
||||
r.protection,
|
||||
crate::MemoryProtection::ReadExecute
|
||||
| crate::MemoryProtection::ReadWriteExecute
|
||||
)
|
||||
})
|
||||
.map(|r| r.size)
|
||||
.sum();
|
||||
|
||||
@@ -241,7 +275,11 @@ impl HollowingDetector {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn validate_pe_headers(&self, pid: u32, regions: &[MemoryRegion]) -> Option<HollowingIndicator> {
|
||||
fn validate_pe_headers(
|
||||
&self,
|
||||
pid: u32,
|
||||
regions: &[MemoryRegion],
|
||||
) -> Option<HollowingIndicator> {
|
||||
// Focus on main executable IMAGE regions
|
||||
let image_regions: Vec<_> = regions
|
||||
.iter()
|
||||
@@ -251,44 +289,42 @@ impl HollowingDetector {
|
||||
|
||||
for region in image_regions {
|
||||
match validate_pe_header(pid, region.base_address) {
|
||||
Ok(validation) => {
|
||||
match validation {
|
||||
PEHeaderValidation::Valid => continue,
|
||||
PEHeaderValidation::InvalidDosSignature => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid DOS signature (not MZ)".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::InvalidNtSignature => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid NT signature (not PE)".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::InvalidHeaderOffset => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid PE header offset".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::MismatchedImageBase => {
|
||||
return Some(HollowingIndicator::CorruptedPEStructure {
|
||||
address: region.base_address,
|
||||
reason: "Image base mismatch - possible hollowing".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::SuspiciousEntryPoint => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Suspicious entry point location".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::CorruptedHeader => {
|
||||
return Some(HollowingIndicator::CorruptedPEStructure {
|
||||
address: region.base_address,
|
||||
reason: "Corrupted PE header structure".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::NotPE => continue,
|
||||
Ok(validation) => match validation {
|
||||
PEHeaderValidation::Valid => continue,
|
||||
PEHeaderValidation::InvalidDosSignature => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid DOS signature (not MZ)".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
PEHeaderValidation::InvalidNtSignature => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid NT signature (not PE)".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::InvalidHeaderOffset => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Invalid PE header offset".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::MismatchedImageBase => {
|
||||
return Some(HollowingIndicator::CorruptedPEStructure {
|
||||
address: region.base_address,
|
||||
reason: "Image base mismatch - possible hollowing".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::SuspiciousEntryPoint => {
|
||||
return Some(HollowingIndicator::InvalidPEHeader {
|
||||
validation: "Suspicious entry point location".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::CorruptedHeader => {
|
||||
return Some(HollowingIndicator::CorruptedPEStructure {
|
||||
address: region.base_address,
|
||||
reason: "Corrupted PE header structure".to_string(),
|
||||
});
|
||||
}
|
||||
PEHeaderValidation::NotPE => continue,
|
||||
},
|
||||
Err(_) => {
|
||||
// Could not read memory - might be suspicious but don't report
|
||||
continue;
|
||||
@@ -300,7 +336,11 @@ impl HollowingDetector {
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn validate_pe_headers(&self, _pid: u32, _regions: &[MemoryRegion]) -> Option<HollowingIndicator> {
|
||||
fn validate_pe_headers(
|
||||
&self,
|
||||
_pid: u32,
|
||||
_regions: &[MemoryRegion],
|
||||
) -> Option<HollowingIndicator> {
|
||||
// PE validation is Windows-specific
|
||||
None
|
||||
}
|
||||
@@ -317,8 +357,11 @@ impl HollowingDetector {
|
||||
let executable_regions: Vec<_> = regions
|
||||
.iter()
|
||||
.filter(|r| {
|
||||
matches!(r.protection, crate::MemoryProtection::ReadExecute | crate::MemoryProtection::ReadWriteExecute)
|
||||
&& r.region_type == "PRIVATE"
|
||||
matches!(
|
||||
r.protection,
|
||||
crate::MemoryProtection::ReadExecute
|
||||
| crate::MemoryProtection::ReadWriteExecute
|
||||
) && r.region_type == "PRIVATE"
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -340,4 +383,4 @@ impl Default for HollowingDetector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
//! used for process injection (T1055.003, T1055.012).
|
||||
//! On Linux, it detects LD_PRELOAD and LD_LIBRARY_PATH based injection.
|
||||
|
||||
use crate::{GhostError, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Type of hook detected.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -78,18 +76,18 @@ mod platform {
|
||||
use std::collections::HashMap;
|
||||
use windows::Win32::Foundation::CloseHandle;
|
||||
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
|
||||
use windows::Win32::System::LibraryLoader::{
|
||||
GetModuleHandleW, GetProcAddress, LoadLibraryW,
|
||||
};
|
||||
use windows::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress, LoadLibraryW};
|
||||
use windows::Win32::System::ProcessStatus::{
|
||||
EnumProcessModulesEx, GetModuleBaseNameW, GetModuleInformation, LIST_MODULES_ALL,
|
||||
MODULEINFO,
|
||||
};
|
||||
use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
|
||||
use windows::Win32::System::Threading::{
|
||||
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
|
||||
};
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
WH_CALLWNDPROC, WH_CALLWNDPROCRET, WH_CBT, WH_DEBUG, WH_FOREGROUNDIDLE, WH_GETMESSAGE,
|
||||
WH_JOURNALPLAYBACK, WH_JOURNALRECORD, WH_KEYBOARD, WH_KEYBOARD_LL, WH_MOUSE,
|
||||
WH_MOUSE_LL, WH_MSGFILTER, WH_SHELL, WH_SYSMSGFILTER,
|
||||
WH_JOURNALPLAYBACK, WH_JOURNALRECORD, WH_KEYBOARD, WH_KEYBOARD_LL, WH_MOUSE, WH_MOUSE_LL,
|
||||
WH_MSGFILTER, WH_SHELL, WH_SYSMSGFILTER,
|
||||
};
|
||||
|
||||
/// Critical APIs commonly hooked for injection.
|
||||
@@ -151,10 +149,14 @@ mod platform {
|
||||
let mut hooks = Vec::new();
|
||||
|
||||
unsafe {
|
||||
let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, target_pid)
|
||||
.map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to open process: {}", e),
|
||||
})?;
|
||||
let handle = OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||
false,
|
||||
target_pid,
|
||||
)
|
||||
.map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to open process: {}", e),
|
||||
})?;
|
||||
|
||||
// Get loaded modules in target process
|
||||
let mut modules = [windows::Win32::Foundation::HMODULE::default(); 1024];
|
||||
@@ -407,11 +409,10 @@ mod platform {
|
||||
/// Detect LD_PRELOAD environment variable in process.
|
||||
fn detect_ld_preload(pid: u32) -> Result<Vec<HookInfo>> {
|
||||
let environ_path = format!("/proc/{}/environ", pid);
|
||||
let environ_content = fs::read_to_string(&environ_path).map_err(|e| {
|
||||
GhostError::Process {
|
||||
let environ_content =
|
||||
fs::read_to_string(&environ_path).map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to read process environment: {}", e),
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
|
||||
let mut hooks = Vec::new();
|
||||
|
||||
@@ -419,7 +420,7 @@ mod platform {
|
||||
for env_var in environ_content.split('\0') {
|
||||
if env_var.starts_with("LD_PRELOAD=") {
|
||||
let libraries = env_var.strip_prefix("LD_PRELOAD=").unwrap_or("");
|
||||
|
||||
|
||||
// Multiple libraries can be separated by spaces or colons
|
||||
for lib in libraries.split(&[' ', ':'][..]) {
|
||||
if !lib.is_empty() {
|
||||
@@ -442,18 +443,17 @@ mod platform {
|
||||
/// Detect LD_LIBRARY_PATH environment variable manipulation.
|
||||
fn detect_ld_library_path(pid: u32) -> Result<Vec<HookInfo>> {
|
||||
let environ_path = format!("/proc/{}/environ", pid);
|
||||
let environ_content = fs::read_to_string(&environ_path).map_err(|e| {
|
||||
GhostError::Process {
|
||||
let environ_content =
|
||||
fs::read_to_string(&environ_path).map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to read process environment: {}", e),
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
|
||||
let mut hooks = Vec::new();
|
||||
|
||||
for env_var in environ_content.split('\0') {
|
||||
if env_var.starts_with("LD_LIBRARY_PATH=") {
|
||||
let paths = env_var.strip_prefix("LD_LIBRARY_PATH=").unwrap_or("");
|
||||
|
||||
|
||||
// Check for suspicious paths
|
||||
for path in paths.split(':') {
|
||||
if is_suspicious_library_path(path) {
|
||||
@@ -476,25 +476,18 @@ mod platform {
|
||||
/// Check if a library path is suspicious.
|
||||
fn is_suspicious_library_path(path: &str) -> bool {
|
||||
// Suspicious patterns
|
||||
let suspicious_patterns = [
|
||||
"/tmp/",
|
||||
"/dev/shm/",
|
||||
"/var/tmp/",
|
||||
".",
|
||||
"..",
|
||||
"/home/",
|
||||
];
|
||||
let suspicious_patterns = ["/tmp/", "/dev/shm/", "/var/tmp/", ".", "..", "/home/"];
|
||||
|
||||
suspicious_patterns.iter().any(|&pattern| path.contains(pattern))
|
||||
suspicious_patterns
|
||||
.iter()
|
||||
.any(|&pattern| path.contains(pattern))
|
||||
}
|
||||
|
||||
/// Detect ptrace attachment (debugging/injection).
|
||||
fn detect_ptrace_attachment(pid: u32) -> Result<bool> {
|
||||
let status_path = format!("/proc/{}/status", pid);
|
||||
let status_content = fs::read_to_string(&status_path).map_err(|e| {
|
||||
GhostError::Process {
|
||||
message: format!("Failed to read process status: {}", e),
|
||||
}
|
||||
let status_content = fs::read_to_string(&status_path).map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to read process status: {}", e),
|
||||
})?;
|
||||
|
||||
// Look for TracerPid field
|
||||
@@ -505,7 +498,7 @@ mod platform {
|
||||
.nth(1)
|
||||
.and_then(|s| s.parse::<u32>().ok())
|
||||
.unwrap_or(0);
|
||||
|
||||
|
||||
// Non-zero TracerPid means the process is being traced
|
||||
if tracer_pid != 0 {
|
||||
return Ok(true);
|
||||
@@ -519,10 +512,8 @@ mod platform {
|
||||
/// Detect suspicious loaded libraries.
|
||||
fn detect_suspicious_libraries(pid: u32) -> Result<Vec<HookInfo>> {
|
||||
let maps_path = format!("/proc/{}/maps", pid);
|
||||
let maps_content = fs::read_to_string(&maps_path).map_err(|e| {
|
||||
GhostError::Process {
|
||||
message: format!("Failed to read process maps: {}", e),
|
||||
}
|
||||
let maps_content = fs::read_to_string(&maps_path).map_err(|e| GhostError::Process {
|
||||
message: format!("Failed to read process maps: {}", e),
|
||||
})?;
|
||||
|
||||
let mut hooks = Vec::new();
|
||||
@@ -535,7 +526,7 @@ mod platform {
|
||||
}
|
||||
|
||||
let pathname = parts[5..].join(" ");
|
||||
|
||||
|
||||
// Check if it's a shared library
|
||||
if pathname.ends_with(".so") || pathname.contains(".so.") {
|
||||
// Skip if already seen
|
||||
@@ -563,29 +554,23 @@ mod platform {
|
||||
/// Check if a library path is suspicious.
|
||||
fn is_suspicious_library(path: &str) -> bool {
|
||||
// Libraries in these locations are often used for injection
|
||||
let suspicious_locations = [
|
||||
"/tmp/",
|
||||
"/dev/shm/",
|
||||
"/var/tmp/",
|
||||
"/home/",
|
||||
];
|
||||
let suspicious_locations = ["/tmp/", "/dev/shm/", "/var/tmp/", "/home/"];
|
||||
|
||||
// Check if library is in a suspicious location
|
||||
if suspicious_locations.iter().any(|&loc| path.starts_with(loc)) {
|
||||
if suspicious_locations
|
||||
.iter()
|
||||
.any(|&loc| path.starts_with(loc))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for libraries with suspicious names
|
||||
let suspicious_names = [
|
||||
"inject",
|
||||
"hook",
|
||||
"cheat",
|
||||
"hack",
|
||||
"rootkit",
|
||||
];
|
||||
let suspicious_names = ["inject", "hook", "cheat", "hack", "rootkit"];
|
||||
|
||||
let path_lower = path.to_lowercase();
|
||||
suspicious_names.iter().any(|&name| path_lower.contains(name))
|
||||
suspicious_names
|
||||
.iter()
|
||||
.any(|&name| path_lower.contains(name))
|
||||
}
|
||||
|
||||
pub fn get_hook_type_name(_hook_type: u32) -> &'static str {
|
||||
@@ -596,7 +581,7 @@ mod platform {
|
||||
#[cfg(not(any(windows, target_os = "linux")))]
|
||||
mod platform {
|
||||
use super::HookDetectionResult;
|
||||
use crate::{GhostError, Result};
|
||||
use crate::Result;
|
||||
|
||||
pub fn detect_hook_injection(_target_pid: u32) -> Result<HookDetectionResult> {
|
||||
// Hook detection is not implemented for this platform
|
||||
@@ -613,4 +598,4 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
pub use platform::{detect_hook_injection, get_hook_type_name};
|
||||
pub use platform::{detect_hook_injection, get_hook_type_name};
|
||||
|
||||
@@ -73,42 +73,42 @@ pub mod yara_engine;
|
||||
|
||||
pub use anomaly::{AnomalyDetector, AnomalyScore, ProcessFeatures};
|
||||
pub use behavioral_ml::{
|
||||
AdvancedBehavioralML, BehavioralAnalysisResult, PredictedTechnique, BehavioralAnomaly,
|
||||
ModelConsensus, TemporalAnalysis, RiskLevel
|
||||
AdvancedBehavioralML, BehavioralAnalysisResult, BehavioralAnomaly, ModelConsensus,
|
||||
PredictedTechnique, RiskLevel, TemporalAnalysis,
|
||||
};
|
||||
pub use config::{DetectionConfig, ProcessFilter};
|
||||
pub use detection::{DetectionEngine, DetectionResult, ThreatLevel};
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use ebpf::{EbpfDetector, EbpfEvent, EbpfError, EbpfStatistics};
|
||||
pub use ebpf::{EbpfDetector, EbpfError, EbpfEvent, EbpfStatistics};
|
||||
pub use error::{GhostError, Result};
|
||||
pub use evasion::{
|
||||
EvasionDetector, EvasionResult, EvasionTechnique, EvasionSeverity,
|
||||
TimingAnalyzer, EnvironmentChecker, BehaviorAnalyzer, ObfuscationDetector
|
||||
BehaviorAnalyzer, EnvironmentChecker, EvasionDetector, EvasionResult, EvasionSeverity,
|
||||
EvasionTechnique, ObfuscationDetector, TimingAnalyzer,
|
||||
};
|
||||
pub use hollowing::{HollowingDetection, HollowingDetector, HollowingIndicator};
|
||||
pub use hooks::{detect_hook_injection, HookDetectionResult, HookInfo};
|
||||
pub use live_feeds::{LiveThreatFeeds, ThreatFeed, FeedType};
|
||||
pub use live_feeds::{FeedType, LiveThreatFeeds, ThreatFeed};
|
||||
pub use memory::{MemoryProtection, MemoryRegion};
|
||||
pub use mitre_attack::{
|
||||
MitreAttackEngine, MitreAnalysisResult, AttackTechnique, AttackTactic, ThreatActor,
|
||||
DetectedTechnique, TacticCoverage, ThreatActorMatch, KillChainAnalysis, RiskAssessment
|
||||
AttackTactic, AttackTechnique, DetectedTechnique, KillChainAnalysis, MitreAnalysisResult,
|
||||
MitreAttackEngine, RiskAssessment, TacticCoverage, ThreatActor, ThreatActorMatch,
|
||||
};
|
||||
pub use ml_cloud::{CloudMLEngine, InferenceResult, MLModel, ThreatPrediction, ThreatSeverity};
|
||||
pub use neural_memory::{
|
||||
NeuralMemoryAnalyzer, NeuralAnalysisResult, DetectedPattern, DetectedEvasion,
|
||||
PolymorphicIndicator, MemoryAnomaly, NeuralInsights, PatternType, EvasionCategory
|
||||
DetectedEvasion, DetectedPattern, EvasionCategory, MemoryAnomaly, NeuralAnalysisResult,
|
||||
NeuralInsights, NeuralMemoryAnalyzer, PatternType, PolymorphicIndicator,
|
||||
};
|
||||
pub use process::ProcessInfo;
|
||||
pub use shellcode::{ShellcodeDetection, ShellcodeDetector};
|
||||
pub use streaming::{
|
||||
EventStreamingSystem, EventChannel, StreamingEvent, EventType, EventSeverity,
|
||||
AlertManager, Alert, AlertRule, CorrelationEngine, NotificationSystem
|
||||
Alert, AlertManager, AlertRule, CorrelationEngine, EventChannel, EventSeverity,
|
||||
EventStreamingSystem, EventType, NotificationSystem, StreamingEvent,
|
||||
};
|
||||
pub use thread::ThreadInfo;
|
||||
pub use threat_intel::{
|
||||
ThreatIntelligence, ThreatContext, IndicatorOfCompromise,
|
||||
ThreatActor as ThreatIntelActor, Campaign, IocType, SophisticationLevel
|
||||
Campaign, IndicatorOfCompromise, IocType, SophisticationLevel, ThreatActor as ThreatIntelActor,
|
||||
ThreatContext, ThreatIntelligence,
|
||||
};
|
||||
pub use yara_engine::{
|
||||
DynamicYaraEngine, YaraRuleSource, YaraScanResult, RuleMatch, ThreatLevel as YaraThreatLevel
|
||||
DynamicYaraEngine, RuleMatch, ThreatLevel as YaraThreatLevel, YaraRuleSource, YaraScanResult,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::GhostError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LiveThreatFeeds {
|
||||
@@ -72,7 +72,7 @@ impl LiveThreatFeeds {
|
||||
|
||||
pub async fn update_feeds(&mut self) -> Result<usize, GhostError> {
|
||||
let mut updated_count = 0;
|
||||
|
||||
|
||||
for feed in &mut self.feeds {
|
||||
if !feed.enabled {
|
||||
continue;
|
||||
@@ -99,4 +99,4 @@ impl LiveThreatFeeds {
|
||||
pub fn get_feed_status(&self) -> Vec<&ThreatFeed> {
|
||||
self.feeds.iter().collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{ProcessInfo, MemoryRegion, ThreadInfo, GhostError};
|
||||
use crate::{GhostError, MemoryRegion, ProcessInfo, ThreadInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// MITRE ATT&CK Framework Integration Engine
|
||||
/// Provides comprehensive technique mapping, threat actor profiling, and tactical analysis
|
||||
@@ -74,7 +74,7 @@ pub enum Platform {
|
||||
Linux,
|
||||
MacOS,
|
||||
Android,
|
||||
iOS,
|
||||
IOS,
|
||||
Cloud,
|
||||
Network,
|
||||
Container,
|
||||
@@ -344,7 +344,7 @@ pub enum EvidenceType {
|
||||
NetworkActivity,
|
||||
FileSystem,
|
||||
Registry,
|
||||
API_Calls,
|
||||
ApiCalls,
|
||||
Timing,
|
||||
}
|
||||
|
||||
@@ -370,114 +370,125 @@ impl MitreAttackEngine {
|
||||
|
||||
fn initialize_techniques(&mut self) -> Result<(), GhostError> {
|
||||
// Process Injection (T1055)
|
||||
self.techniques.insert("T1055".to_string(), AttackTechnique {
|
||||
id: "T1055".to_string(),
|
||||
name: "Process Injection".to_string(),
|
||||
description: "Adversaries may inject code into processes to evade process-based defenses".to_string(),
|
||||
tactics: vec!["TA0004".to_string(), "TA0005".to_string()], // Defense Evasion, Privilege Escalation
|
||||
platforms: vec![Platform::Windows, Platform::Linux, Platform::MacOS],
|
||||
data_sources: vec![
|
||||
DataSource {
|
||||
self.techniques.insert(
|
||||
"T1055".to_string(),
|
||||
AttackTechnique {
|
||||
id: "T1055".to_string(),
|
||||
name: "Process Injection".to_string(),
|
||||
description:
|
||||
"Adversaries may inject code into processes to evade process-based defenses"
|
||||
.to_string(),
|
||||
tactics: vec!["TA0004".to_string(), "TA0005".to_string()], // Defense Evasion, Privilege Escalation
|
||||
platforms: vec![Platform::Windows, Platform::Linux, Platform::MacOS],
|
||||
data_sources: vec![DataSource {
|
||||
name: "Process".to_string(),
|
||||
data_component: "Process Access".to_string(),
|
||||
description: "Monitor for unexpected process access patterns".to_string(),
|
||||
},
|
||||
],
|
||||
detection_methods: vec![
|
||||
DetectionMethod {
|
||||
}],
|
||||
detection_methods: vec![DetectionMethod {
|
||||
method_type: DetectionType::BehavioralAnalysis,
|
||||
description: "Monitor for unusual cross-process activity".to_string(),
|
||||
effectiveness: 0.85,
|
||||
false_positive_rate: 0.1,
|
||||
},
|
||||
],
|
||||
mitigations: vec![
|
||||
Mitigation {
|
||||
}],
|
||||
mitigations: vec![Mitigation {
|
||||
id: "M1040".to_string(),
|
||||
name: "Behavior Prevention on Endpoint".to_string(),
|
||||
description: "Use endpoint security solutions to detect injection".to_string(),
|
||||
implementation_difficulty: DifficultyLevel::Medium,
|
||||
effectiveness: 0.8,
|
||||
},
|
||||
],
|
||||
sub_techniques: vec!["T1055.001".to_string(), "T1055.002".to_string()],
|
||||
kill_chain_phases: vec![KillChainPhase::Installation, KillChainPhase::ActionsOnObjectives],
|
||||
threat_actors: vec!["APT1".to_string(), "APT29".to_string()],
|
||||
references: vec![
|
||||
Reference {
|
||||
}],
|
||||
sub_techniques: vec!["T1055.001".to_string(), "T1055.002".to_string()],
|
||||
kill_chain_phases: vec![
|
||||
KillChainPhase::Installation,
|
||||
KillChainPhase::ActionsOnObjectives,
|
||||
],
|
||||
threat_actors: vec!["APT1".to_string(), "APT29".to_string()],
|
||||
references: vec![Reference {
|
||||
source: "MITRE ATT&CK".to_string(),
|
||||
url: "https://attack.mitre.org/techniques/T1055/".to_string(),
|
||||
description: "Process Injection".to_string(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}],
|
||||
},
|
||||
);
|
||||
|
||||
// Process Hollowing (T1055.012)
|
||||
self.techniques.insert("T1055.012".to_string(), AttackTechnique {
|
||||
id: "T1055.012".to_string(),
|
||||
name: "Process Hollowing".to_string(),
|
||||
description: "Adversaries may inject malicious code into suspended and hollowed processes".to_string(),
|
||||
tactics: vec!["TA0004".to_string(), "TA0005".to_string()],
|
||||
platforms: vec![Platform::Windows],
|
||||
data_sources: vec![
|
||||
DataSource {
|
||||
self.techniques.insert(
|
||||
"T1055.012".to_string(),
|
||||
AttackTechnique {
|
||||
id: "T1055.012".to_string(),
|
||||
name: "Process Hollowing".to_string(),
|
||||
description:
|
||||
"Adversaries may inject malicious code into suspended and hollowed processes"
|
||||
.to_string(),
|
||||
tactics: vec!["TA0004".to_string(), "TA0005".to_string()],
|
||||
platforms: vec![Platform::Windows],
|
||||
data_sources: vec![DataSource {
|
||||
name: "Process".to_string(),
|
||||
data_component: "Process Creation".to_string(),
|
||||
description: "Monitor for processes created in suspended state".to_string(),
|
||||
},
|
||||
],
|
||||
detection_methods: vec![
|
||||
DetectionMethod {
|
||||
}],
|
||||
detection_methods: vec![DetectionMethod {
|
||||
method_type: DetectionType::EndpointDetection,
|
||||
description: "Detect hollowing through memory analysis".to_string(),
|
||||
effectiveness: 0.9,
|
||||
false_positive_rate: 0.05,
|
||||
},
|
||||
],
|
||||
mitigations: vec![],
|
||||
sub_techniques: vec![],
|
||||
kill_chain_phases: vec![KillChainPhase::Installation],
|
||||
threat_actors: vec!["APT29".to_string(), "Lazarus Group".to_string()],
|
||||
references: vec![],
|
||||
});
|
||||
}],
|
||||
mitigations: vec![],
|
||||
sub_techniques: vec![],
|
||||
kill_chain_phases: vec![KillChainPhase::Installation],
|
||||
threat_actors: vec!["APT29".to_string(), "Lazarus Group".to_string()],
|
||||
references: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize_tactics(&mut self) -> Result<(), GhostError> {
|
||||
self.tactics.insert("TA0004".to_string(), AttackTactic {
|
||||
id: "TA0004".to_string(),
|
||||
name: "Defense Evasion".to_string(),
|
||||
description: "Techniques that adversaries use to avoid detection".to_string(),
|
||||
techniques: vec!["T1055".to_string()],
|
||||
matrix_position: 4,
|
||||
});
|
||||
self.tactics.insert(
|
||||
"TA0004".to_string(),
|
||||
AttackTactic {
|
||||
id: "TA0004".to_string(),
|
||||
name: "Defense Evasion".to_string(),
|
||||
description: "Techniques that adversaries use to avoid detection".to_string(),
|
||||
techniques: vec!["T1055".to_string()],
|
||||
matrix_position: 4,
|
||||
},
|
||||
);
|
||||
|
||||
self.tactics.insert("TA0005".to_string(), AttackTactic {
|
||||
id: "TA0005".to_string(),
|
||||
name: "Privilege Escalation".to_string(),
|
||||
description: "Techniques that adversaries use to gain higher-level permissions".to_string(),
|
||||
techniques: vec!["T1055".to_string()],
|
||||
matrix_position: 5,
|
||||
});
|
||||
self.tactics.insert(
|
||||
"TA0005".to_string(),
|
||||
AttackTactic {
|
||||
id: "TA0005".to_string(),
|
||||
name: "Privilege Escalation".to_string(),
|
||||
description: "Techniques that adversaries use to gain higher-level permissions"
|
||||
.to_string(),
|
||||
techniques: vec!["T1055".to_string()],
|
||||
matrix_position: 5,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize_threat_actors(&mut self) -> Result<(), GhostError> {
|
||||
self.threat_actors.insert("APT29".to_string(), ThreatActor {
|
||||
id: "G0016".to_string(),
|
||||
name: "APT29".to_string(),
|
||||
aliases: vec!["Cozy Bear".to_string(), "The Dukes".to_string()],
|
||||
description: "Russian state-sponsored threat group".to_string(),
|
||||
country: Some("Russia".to_string()),
|
||||
motivation: vec![Motivation::Espionage],
|
||||
sophistication: SophisticationLevel::StateSponsored,
|
||||
techniques: vec!["T1055".to_string(), "T1055.012".to_string()],
|
||||
campaigns: vec!["Operation Ghost".to_string()],
|
||||
first_seen: SystemTime::now() - Duration::from_secs(365 * 24 * 3600 * 10), // 10 years ago
|
||||
last_activity: SystemTime::now() - Duration::from_secs(30 * 24 * 3600), // 30 days ago
|
||||
});
|
||||
self.threat_actors.insert(
|
||||
"APT29".to_string(),
|
||||
ThreatActor {
|
||||
id: "G0016".to_string(),
|
||||
name: "APT29".to_string(),
|
||||
aliases: vec!["Cozy Bear".to_string(), "The Dukes".to_string()],
|
||||
description: "Russian state-sponsored threat group".to_string(),
|
||||
country: Some("Russia".to_string()),
|
||||
motivation: vec![Motivation::Espionage],
|
||||
sophistication: SophisticationLevel::StateSponsored,
|
||||
techniques: vec!["T1055".to_string(), "T1055.012".to_string()],
|
||||
campaigns: vec!["Operation Ghost".to_string()],
|
||||
first_seen: SystemTime::now() - Duration::from_secs(365 * 24 * 3600 * 10), // 10 years ago
|
||||
last_activity: SystemTime::now() - Duration::from_secs(30 * 24 * 3600), // 30 days ago
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -501,7 +512,9 @@ impl MitreAttackEngine {
|
||||
memory_regions: &[MemoryRegion],
|
||||
threads: &[ThreadInfo],
|
||||
) -> Result<MitreAnalysisResult, GhostError> {
|
||||
let detected_techniques = self.detect_techniques(process, memory_regions, threads).await?;
|
||||
let detected_techniques = self
|
||||
.detect_techniques(process, memory_regions, threads)
|
||||
.await?;
|
||||
let tactics_coverage = self.analyze_tactics_coverage(&detected_techniques)?;
|
||||
let threat_actor_matches = self.match_threat_actors(&detected_techniques)?;
|
||||
let campaign_indicators = self.analyze_campaign_indicators(&detected_techniques)?;
|
||||
@@ -529,8 +542,13 @@ impl MitreAttackEngine {
|
||||
let mut detected = Vec::new();
|
||||
|
||||
// Check for Process Injection indicators
|
||||
let rwx_regions = memory_regions.iter()
|
||||
.filter(|r| r.protection.is_readable() && r.protection.is_writable() && r.protection.is_executable())
|
||||
let rwx_regions = memory_regions
|
||||
.iter()
|
||||
.filter(|r| {
|
||||
r.protection.is_readable()
|
||||
&& r.protection.is_writable()
|
||||
&& r.protection.is_executable()
|
||||
})
|
||||
.count();
|
||||
|
||||
if rwx_regions > 0 {
|
||||
@@ -538,14 +556,12 @@ impl MitreAttackEngine {
|
||||
detected.push(DetectedTechnique {
|
||||
technique: technique.clone(),
|
||||
confidence: 0.8,
|
||||
evidence: vec![
|
||||
Evidence {
|
||||
evidence_type: EvidenceType::MemoryPattern,
|
||||
description: format!("Found {} RWX memory regions", rwx_regions),
|
||||
confidence: 0.9,
|
||||
source: "Memory Analysis".to_string(),
|
||||
},
|
||||
],
|
||||
evidence: vec![Evidence {
|
||||
evidence_type: EvidenceType::MemoryPattern,
|
||||
description: format!("Found {} RWX memory regions", rwx_regions),
|
||||
confidence: 0.9,
|
||||
source: "Memory Analysis".to_string(),
|
||||
}],
|
||||
sub_technique_id: None,
|
||||
detection_timestamp: SystemTime::now(),
|
||||
});
|
||||
@@ -558,14 +574,13 @@ impl MitreAttackEngine {
|
||||
detected.push(DetectedTechnique {
|
||||
technique: technique.clone(),
|
||||
confidence: 0.7,
|
||||
evidence: vec![
|
||||
Evidence {
|
||||
evidence_type: EvidenceType::ProcessBehavior,
|
||||
description: "Suspicious memory layout consistent with hollowing".to_string(),
|
||||
confidence: 0.7,
|
||||
source: "Process Analysis".to_string(),
|
||||
},
|
||||
],
|
||||
evidence: vec![Evidence {
|
||||
evidence_type: EvidenceType::ProcessBehavior,
|
||||
description: "Suspicious memory layout consistent with hollowing"
|
||||
.to_string(),
|
||||
confidence: 0.7,
|
||||
source: "Process Analysis".to_string(),
|
||||
}],
|
||||
sub_technique_id: Some("T1055.012".to_string()),
|
||||
detection_timestamp: SystemTime::now(),
|
||||
});
|
||||
@@ -575,14 +590,18 @@ impl MitreAttackEngine {
|
||||
Ok(detected)
|
||||
}
|
||||
|
||||
fn analyze_tactics_coverage(&self, detected_techniques: &[DetectedTechnique]) -> Result<Vec<TacticCoverage>, GhostError> {
|
||||
fn analyze_tactics_coverage(
|
||||
&self,
|
||||
detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<Vec<TacticCoverage>, GhostError> {
|
||||
let mut coverage = Vec::new();
|
||||
|
||||
|
||||
for tactic in self.tactics.values() {
|
||||
let techniques_detected = detected_techniques.iter()
|
||||
let techniques_detected = detected_techniques
|
||||
.iter()
|
||||
.filter(|dt| dt.technique.tactics.contains(&tactic.id))
|
||||
.count();
|
||||
|
||||
|
||||
let total_techniques = tactic.techniques.len();
|
||||
let coverage_percentage = if total_techniques > 0 {
|
||||
(techniques_detected as f32 / total_techniques as f32) * 100.0
|
||||
@@ -601,18 +620,23 @@ impl MitreAttackEngine {
|
||||
Ok(coverage)
|
||||
}
|
||||
|
||||
fn match_threat_actors(&self, detected_techniques: &[DetectedTechnique]) -> Result<Vec<ThreatActorMatch>, GhostError> {
|
||||
fn match_threat_actors(
|
||||
&self,
|
||||
detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<Vec<ThreatActorMatch>, GhostError> {
|
||||
let mut matches = Vec::new();
|
||||
|
||||
for actor in self.threat_actors.values() {
|
||||
let matching_techniques: Vec<String> = detected_techniques.iter()
|
||||
let matching_techniques: Vec<String> = detected_techniques
|
||||
.iter()
|
||||
.filter(|dt| actor.techniques.contains(&dt.technique.id))
|
||||
.map(|dt| dt.technique.id.clone())
|
||||
.collect();
|
||||
|
||||
if !matching_techniques.is_empty() {
|
||||
let match_confidence = matching_techniques.len() as f32 / actor.techniques.len() as f32;
|
||||
|
||||
let match_confidence =
|
||||
matching_techniques.len() as f32 / actor.techniques.len() as f32;
|
||||
|
||||
matches.push(ThreatActorMatch {
|
||||
threat_actor: actor.clone(),
|
||||
match_confidence,
|
||||
@@ -625,13 +649,19 @@ impl MitreAttackEngine {
|
||||
Ok(matches)
|
||||
}
|
||||
|
||||
fn analyze_campaign_indicators(&self, _detected_techniques: &[DetectedTechnique]) -> Result<Vec<CampaignIndicator>, GhostError> {
|
||||
fn analyze_campaign_indicators(
|
||||
&self,
|
||||
_detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<Vec<CampaignIndicator>, GhostError> {
|
||||
Ok(Vec::new()) // Simplified implementation
|
||||
}
|
||||
|
||||
fn analyze_kill_chain(&self, detected_techniques: &[DetectedTechnique]) -> Result<KillChainAnalysis, GhostError> {
|
||||
fn analyze_kill_chain(
|
||||
&self,
|
||||
detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<KillChainAnalysis, GhostError> {
|
||||
let mut completed_phases = Vec::new();
|
||||
|
||||
|
||||
for technique in detected_techniques {
|
||||
for phase in &technique.technique.kill_chain_phases {
|
||||
if !completed_phases.contains(phase) {
|
||||
@@ -651,16 +681,23 @@ impl MitreAttackEngine {
|
||||
})
|
||||
}
|
||||
|
||||
fn assess_risk(&self, detected_techniques: &[DetectedTechnique]) -> Result<RiskAssessment, GhostError> {
|
||||
fn assess_risk(
|
||||
&self,
|
||||
detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<RiskAssessment, GhostError> {
|
||||
let technique_count = detected_techniques.len() as f32;
|
||||
let avg_confidence = if !detected_techniques.is_empty() {
|
||||
detected_techniques.iter().map(|dt| dt.confidence).sum::<f32>() / technique_count
|
||||
detected_techniques
|
||||
.iter()
|
||||
.map(|dt| dt.confidence)
|
||||
.sum::<f32>()
|
||||
/ technique_count
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let overall_risk_score = (technique_count * 0.3 + avg_confidence * 0.7).min(1.0);
|
||||
|
||||
|
||||
let urgency_level = if overall_risk_score > 0.8 {
|
||||
UrgencyLevel::Critical
|
||||
} else if overall_risk_score > 0.6 {
|
||||
@@ -676,17 +713,18 @@ impl MitreAttackEngine {
|
||||
attack_likelihood: avg_confidence,
|
||||
potential_impact: 0.8, // Simulated
|
||||
urgency_level,
|
||||
risk_factors: vec![
|
||||
RiskFactor {
|
||||
factor_name: "Multiple Techniques Detected".to_string(),
|
||||
risk_contribution: 0.6,
|
||||
description: "Multiple attack techniques increase overall risk".to_string(),
|
||||
},
|
||||
],
|
||||
risk_factors: vec![RiskFactor {
|
||||
factor_name: "Multiple Techniques Detected".to_string(),
|
||||
risk_contribution: 0.6,
|
||||
description: "Multiple attack techniques increase overall risk".to_string(),
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
fn recommend_mitigations(&self, detected_techniques: &[DetectedTechnique]) -> Result<Vec<MitigationRecommendation>, GhostError> {
|
||||
fn recommend_mitigations(
|
||||
&self,
|
||||
detected_techniques: &[DetectedTechnique],
|
||||
) -> Result<Vec<MitigationRecommendation>, GhostError> {
|
||||
let mut recommendations = Vec::new();
|
||||
|
||||
for technique in detected_techniques {
|
||||
@@ -709,7 +747,7 @@ impl MitreAttackEngine {
|
||||
// Simulate framework update
|
||||
self.last_update = SystemTime::now();
|
||||
self.matrix_version = "13.1".to_string();
|
||||
|
||||
|
||||
// Return number of updated techniques
|
||||
Ok(self.techniques.len())
|
||||
}
|
||||
@@ -719,6 +757,10 @@ impl MitreAttackEngine {
|
||||
}
|
||||
|
||||
pub fn get_framework_stats(&self) -> (usize, usize, usize) {
|
||||
(self.techniques.len(), self.tactics.len(), self.threat_actors.len())
|
||||
(
|
||||
self.techniques.len(),
|
||||
self.tactics.len(),
|
||||
self.threat_actors.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{ProcessInfo, MemoryRegion, GhostError};
|
||||
use crate::{GhostError, MemoryRegion, ProcessInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CloudMLEngine {
|
||||
@@ -111,8 +111,11 @@ impl CloudMLEngine {
|
||||
|
||||
// Simulate ML inference
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let threat_level = if memory_regions.iter().any(|r| r.protection.is_executable() && r.protection.is_writable()) {
|
||||
|
||||
let threat_level = if memory_regions
|
||||
.iter()
|
||||
.any(|r| r.protection.is_executable() && r.protection.is_writable())
|
||||
{
|
||||
ThreatSeverity::High
|
||||
} else if memory_regions.len() > 50 {
|
||||
ThreatSeverity::Medium
|
||||
@@ -122,13 +125,11 @@ impl CloudMLEngine {
|
||||
|
||||
let prediction = ThreatPrediction {
|
||||
threat_level,
|
||||
technique_predictions: vec![
|
||||
TechniquePrediction {
|
||||
technique_id: "T1055".to_string(),
|
||||
technique_name: "Process Injection".to_string(),
|
||||
confidence: 0.85,
|
||||
},
|
||||
],
|
||||
technique_predictions: vec![TechniquePrediction {
|
||||
technique_id: "T1055".to_string(),
|
||||
technique_name: "Process Injection".to_string(),
|
||||
confidence: 0.85,
|
||||
}],
|
||||
anomaly_score: 0.75,
|
||||
};
|
||||
|
||||
@@ -140,11 +141,14 @@ impl CloudMLEngine {
|
||||
};
|
||||
|
||||
// Cache result
|
||||
self.cache.insert(cache_key, CachedPrediction {
|
||||
result: result.clone(),
|
||||
timestamp: SystemTime::now(),
|
||||
ttl: Duration::from_secs(300), // 5 minutes
|
||||
});
|
||||
self.cache.insert(
|
||||
cache_key,
|
||||
CachedPrediction {
|
||||
result: result.clone(),
|
||||
timestamp: SystemTime::now(),
|
||||
ttl: Duration::from_secs(300), // 5 minutes
|
||||
},
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@@ -152,4 +156,4 @@ impl CloudMLEngine {
|
||||
pub fn get_model_stats(&self) -> Vec<&MLModel> {
|
||||
self.models.iter().collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{ProcessInfo, MemoryRegion, GhostError};
|
||||
use crate::{GhostError, MemoryRegion, ProcessInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, Duration};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NeuralMemoryAnalyzer {
|
||||
@@ -143,24 +142,24 @@ impl NeuralMemoryAnalyzer {
|
||||
|
||||
pub async fn analyze_memory_regions(
|
||||
&mut self,
|
||||
process: &ProcessInfo,
|
||||
_process: &ProcessInfo,
|
||||
memory_regions: &[MemoryRegion],
|
||||
) -> Result<NeuralAnalysisResult, GhostError> {
|
||||
// Extract features
|
||||
let features = self.extract_features(memory_regions)?;
|
||||
|
||||
|
||||
// Run neural ensemble
|
||||
let predictions = self.run_neural_ensemble(&features).await?;
|
||||
|
||||
|
||||
// Calculate threat probability
|
||||
let threat_probability = self.calculate_threat_probability(&predictions);
|
||||
|
||||
|
||||
// Detect patterns
|
||||
let detected_patterns = self.detect_patterns(&features)?;
|
||||
|
||||
|
||||
// Analyze evasion techniques
|
||||
let evasion_techniques = self.analyze_evasion(&features)?;
|
||||
|
||||
|
||||
Ok(NeuralAnalysisResult {
|
||||
threat_probability,
|
||||
detected_patterns,
|
||||
@@ -173,33 +172,45 @@ impl NeuralMemoryAnalyzer {
|
||||
|
||||
fn extract_features(&self, memory_regions: &[MemoryRegion]) -> Result<Vec<f32>, GhostError> {
|
||||
let mut features = Vec::new();
|
||||
|
||||
|
||||
// Basic features
|
||||
features.push(memory_regions.len() as f32);
|
||||
|
||||
|
||||
// Protection features
|
||||
let rwx_count = memory_regions.iter()
|
||||
.filter(|r| r.protection.is_readable() && r.protection.is_writable() && r.protection.is_executable())
|
||||
let rwx_count = memory_regions
|
||||
.iter()
|
||||
.filter(|r| {
|
||||
r.protection.is_readable()
|
||||
&& r.protection.is_writable()
|
||||
&& r.protection.is_executable()
|
||||
})
|
||||
.count() as f32;
|
||||
features.push(rwx_count);
|
||||
|
||||
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
async fn run_neural_ensemble(&self, features: &[f32]) -> Result<Vec<ModelPrediction>, GhostError> {
|
||||
async fn run_neural_ensemble(
|
||||
&self,
|
||||
features: &[f32],
|
||||
) -> Result<Vec<ModelPrediction>, GhostError> {
|
||||
let mut predictions = Vec::new();
|
||||
|
||||
|
||||
for network in &self.neural_networks {
|
||||
let prediction = self.simulate_neural_inference(network, features).await?;
|
||||
predictions.push(prediction);
|
||||
}
|
||||
|
||||
|
||||
Ok(predictions)
|
||||
}
|
||||
|
||||
async fn simulate_neural_inference(&self, network: &NeuralNetwork, _features: &[f32]) -> Result<ModelPrediction, GhostError> {
|
||||
async fn simulate_neural_inference(
|
||||
&self,
|
||||
network: &NeuralNetwork,
|
||||
_features: &[f32],
|
||||
) -> Result<ModelPrediction, GhostError> {
|
||||
let prediction = network.accuracy * 0.5; // Simulate prediction
|
||||
|
||||
|
||||
Ok(ModelPrediction {
|
||||
model_id: network.network_id.clone(),
|
||||
prediction,
|
||||
@@ -207,17 +218,18 @@ impl NeuralMemoryAnalyzer {
|
||||
inference_time_ms: 15.0,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn calculate_threat_probability(&self, predictions: &[ModelPrediction]) -> f32 {
|
||||
if predictions.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let weighted_sum: f32 = predictions.iter()
|
||||
|
||||
let weighted_sum: f32 = predictions
|
||||
.iter()
|
||||
.map(|p| p.prediction * p.confidence)
|
||||
.sum();
|
||||
let total_weight: f32 = predictions.iter().map(|p| p.confidence).sum();
|
||||
|
||||
|
||||
if total_weight > 0.0 {
|
||||
weighted_sum / total_weight
|
||||
} else {
|
||||
@@ -232,4 +244,4 @@ impl NeuralMemoryAnalyzer {
|
||||
fn analyze_evasion(&self, _features: &[f32]) -> Result<Vec<DetectedEvasion>, GhostError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ mod platform {
|
||||
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
|
||||
TH32CS_SNAPPROCESS,
|
||||
};
|
||||
use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION};
|
||||
use windows::Win32::System::ProcessStatus::GetProcessImageFileNameW;
|
||||
use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION};
|
||||
|
||||
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
|
||||
let mut processes = Vec::new();
|
||||
@@ -106,7 +106,7 @@ mod platform {
|
||||
unsafe {
|
||||
let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid).ok()?;
|
||||
let mut buffer = [0u16; 1024];
|
||||
|
||||
|
||||
if GetProcessImageFileNameW(handle, &mut buffer) > 0 {
|
||||
let _ = CloseHandle(handle);
|
||||
let path = String::from_utf16_lossy(
|
||||
@@ -163,13 +163,14 @@ mod platform {
|
||||
|
||||
fn get_process_info(pid: u32) -> Result<ProcessInfo> {
|
||||
let stat_path = format!("/proc/{}/stat", pid);
|
||||
let stat_content =
|
||||
fs::read_to_string(&stat_path).context("Failed to read process stat")?;
|
||||
let stat_content = fs::read_to_string(&stat_path).context("Failed to read process stat")?;
|
||||
|
||||
let (name, ppid, thread_count) = parse_stat(&stat_content)?;
|
||||
|
||||
let exe_path = format!("/proc/{}/exe", pid);
|
||||
let path = fs::read_link(&exe_path).ok().map(|p| p.to_string_lossy().into_owned());
|
||||
let path = fs::read_link(&exe_path)
|
||||
.ok()
|
||||
.map(|p| p.to_string_lossy().into_owned());
|
||||
|
||||
Ok(ProcessInfo {
|
||||
pid,
|
||||
@@ -219,7 +220,9 @@ mod platform {
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
|
||||
Err(anyhow::anyhow!("Process enumeration not supported on this platform"))
|
||||
Err(anyhow::anyhow!(
|
||||
"Process enumeration not supported on this platform"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::{GhostError, Result};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShellcodeSignature {
|
||||
pub pattern: Vec<u8>,
|
||||
@@ -17,6 +15,7 @@ pub struct ShellcodeDetection {
|
||||
}
|
||||
|
||||
/// Common shellcode patterns and signatures
|
||||
#[derive(Debug)]
|
||||
pub struct ShellcodeDetector {
|
||||
signatures: Vec<ShellcodeSignature>,
|
||||
}
|
||||
@@ -298,8 +297,12 @@ impl ShellcodeDetector {
|
||||
|
||||
// Linux x64 execve pattern
|
||||
self.signatures.push(ShellcodeSignature {
|
||||
pattern: vec![0x48, 0x31, 0xD2, 0x48, 0xBB, 0xFF, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x73, 0x68], // xor rdx, rdx; mov rbx, "/bin/sh"
|
||||
mask: vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
pattern: vec![
|
||||
0x48, 0x31, 0xD2, 0x48, 0xBB, 0xFF, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x73, 0x68,
|
||||
], // xor rdx, rdx; mov rbx, "/bin/sh"
|
||||
mask: vec![
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
],
|
||||
name: "Linux x64 execve /bin/sh",
|
||||
confidence: 0.98,
|
||||
});
|
||||
@@ -407,7 +410,7 @@ impl ShellcodeDetector {
|
||||
|
||||
let region = &data[search_start..search_end];
|
||||
let matches = self.find_pattern_matches(region, &sig.pattern, &sig.mask);
|
||||
|
||||
|
||||
if !matches.is_empty() {
|
||||
detection.signature_matches.push(sig.name.to_string());
|
||||
detection.confidence = (detection.confidence + sig.confidence).min(1.0);
|
||||
@@ -466,7 +469,11 @@ impl ShellcodeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_instruction_patterns(&self, data: &[u8], base_address: usize) -> Option<ShellcodeDetection> {
|
||||
fn check_instruction_patterns(
|
||||
&self,
|
||||
data: &[u8],
|
||||
base_address: usize,
|
||||
) -> Option<ShellcodeDetection> {
|
||||
if data.len() < 32 {
|
||||
return None;
|
||||
}
|
||||
@@ -479,10 +486,10 @@ impl ShellcodeDetector {
|
||||
match data[i] {
|
||||
0xEB => suspicious_instructions += 1, // Short jump
|
||||
0xE9 => suspicious_instructions += 1, // Near jump
|
||||
0xFF if data.get(i + 1).map_or(false, |&b| (b & 0x38) == 0x20) => {
|
||||
0xFF if data.get(i + 1).is_some_and(|&b| (b & 0x38) == 0x20) => {
|
||||
suspicious_instructions += 2; // Indirect jump
|
||||
}
|
||||
0x0F if data.get(i + 1).map_or(false, |&b| b == 0x05) => {
|
||||
0x0F if data.get(i + 1).is_some_and(|&b| b == 0x05) => {
|
||||
suspicious_instructions += 2; // SYSCALL
|
||||
}
|
||||
_ => {}
|
||||
@@ -509,4 +516,4 @@ impl Default for ShellcodeDetector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{DetectionResult, EvasionResult, ProcessInfo, ThreatContext, ThreatLevel};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{SystemTime, Duration};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::{DetectionResult, ThreatLevel, ProcessInfo, ThreatContext, EvasionResult};
|
||||
|
||||
/// Real-time Event Streaming and Alerting System
|
||||
/// Provides configurable alerting, correlation, and notification capabilities
|
||||
@@ -519,6 +519,12 @@ pub struct EventBuffer {
|
||||
retention_period: Duration,
|
||||
}
|
||||
|
||||
impl Default for EventStreamingSystem {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventStreamingSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -526,12 +532,18 @@ impl EventStreamingSystem {
|
||||
alert_manager: AlertManager::new(),
|
||||
correlation_engine: CorrelationEngine::new(),
|
||||
notification_system: NotificationSystem::new(),
|
||||
event_buffer: Arc::new(Mutex::new(EventBuffer::new(10000, Duration::from_secs(3600)))),
|
||||
event_buffer: Arc::new(Mutex::new(EventBuffer::new(
|
||||
10000,
|
||||
Duration::from_secs(3600),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a detection event
|
||||
pub async fn publish_detection_event(&mut self, detection: DetectionResult) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub async fn publish_detection_event(
|
||||
&mut self,
|
||||
detection: DetectionResult,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let event = StreamingEvent {
|
||||
event_id: format!("det_{}", uuid::Uuid::new_v4()),
|
||||
timestamp: SystemTime::now(),
|
||||
@@ -556,11 +568,16 @@ impl EventStreamingSystem {
|
||||
tags: vec!["process-injection".to_string(), "detection".to_string()],
|
||||
};
|
||||
|
||||
self.publish_event(EventChannel::DetectionEvents, event).await
|
||||
self.publish_event(EventChannel::DetectionEvents, event)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Publish an evasion detection event
|
||||
pub async fn publish_evasion_event(&mut self, evasion: EvasionResult, process: &ProcessInfo) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub async fn publish_evasion_event(
|
||||
&mut self,
|
||||
evasion: EvasionResult,
|
||||
_process: &ProcessInfo,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let severity = if evasion.sophistication_score > 0.8 {
|
||||
EventSeverity::Critical
|
||||
} else if evasion.sophistication_score > 0.6 {
|
||||
@@ -589,11 +606,16 @@ impl EventStreamingSystem {
|
||||
tags: vec!["evasion".to_string(), "anti-analysis".to_string()],
|
||||
};
|
||||
|
||||
self.publish_event(EventChannel::EvasionDetection, event).await
|
||||
self.publish_event(EventChannel::EvasionDetection, event)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Publish a generic event to specified channel
|
||||
async fn publish_event(&mut self, channel: EventChannel, event: StreamingEvent) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn publish_event(
|
||||
&mut self,
|
||||
channel: EventChannel,
|
||||
event: StreamingEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Add to event buffer for correlation
|
||||
{
|
||||
let mut buffer = self.event_buffer.lock().unwrap();
|
||||
@@ -614,9 +636,12 @@ impl EventStreamingSystem {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_correlated_incident(&mut self, incident: CorrelatedIncident) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn handle_correlated_incident(
|
||||
&mut self,
|
||||
incident: CorrelatedIncident,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Correlated incident detected: {}", incident.title);
|
||||
|
||||
|
||||
// Create alert for correlated incident
|
||||
let alert = Alert {
|
||||
alert_id: format!("inc_{}", uuid::Uuid::new_v4()),
|
||||
@@ -653,6 +678,12 @@ impl EventStreamingSystem {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventPublisher {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventPublisher {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -661,7 +692,11 @@ impl EventPublisher {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn publish(&mut self, channel: EventChannel, event: StreamingEvent) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub async fn publish(
|
||||
&mut self,
|
||||
channel: EventChannel,
|
||||
event: StreamingEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(sender) = self.channels.get(&channel) {
|
||||
sender.send(event)?;
|
||||
}
|
||||
@@ -669,18 +704,23 @@ impl EventPublisher {
|
||||
}
|
||||
|
||||
pub fn subscribe(&mut self, channel: EventChannel) -> broadcast::Receiver<StreamingEvent> {
|
||||
let sender = self.channels.entry(channel.clone())
|
||||
.or_insert_with(|| {
|
||||
let (tx, _) = broadcast::channel(1000);
|
||||
tx
|
||||
});
|
||||
|
||||
let sender = self.channels.entry(channel.clone()).or_insert_with(|| {
|
||||
let (tx, _) = broadcast::channel(1000);
|
||||
tx
|
||||
});
|
||||
|
||||
let receiver = sender.subscribe();
|
||||
*self.subscribers.entry(channel).or_insert(0) += 1;
|
||||
receiver
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AlertManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AlertManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -691,12 +731,17 @@ impl AlertManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn evaluate_alerts(&mut self, event: &StreamingEvent) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let triggered_rules: Vec<AlertRule> = self.alert_rules.iter()
|
||||
pub async fn evaluate_alerts(
|
||||
&mut self,
|
||||
event: &StreamingEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let triggered_rules: Vec<AlertRule> = self
|
||||
.alert_rules
|
||||
.iter()
|
||||
.filter(|rule| rule.enabled && self.evaluate_rule_conditions(rule, event))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
|
||||
for rule in triggered_rules {
|
||||
self.trigger_alert(&rule, event).await?;
|
||||
}
|
||||
@@ -734,7 +779,11 @@ impl AlertManager {
|
||||
})
|
||||
}
|
||||
|
||||
async fn trigger_alert(&mut self, rule: &AlertRule, event: &StreamingEvent) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn trigger_alert(
|
||||
&mut self,
|
||||
rule: &AlertRule,
|
||||
event: &StreamingEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let alert = Alert {
|
||||
alert_id: format!("alert_{}", uuid::Uuid::new_v4()),
|
||||
rule_id: rule.rule_id.clone(),
|
||||
@@ -761,7 +810,7 @@ impl AlertManager {
|
||||
|
||||
pub async fn create_alert(&mut self, alert: Alert) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Alert created: {} - {}", alert.alert_id, alert.title);
|
||||
|
||||
|
||||
let active_alert = ActiveAlert {
|
||||
alert: alert.clone(),
|
||||
escalation_level: 0,
|
||||
@@ -769,13 +818,20 @@ impl AlertManager {
|
||||
notification_count: 0,
|
||||
};
|
||||
|
||||
self.active_alerts.insert(alert.alert_id.clone(), active_alert);
|
||||
self.active_alerts
|
||||
.insert(alert.alert_id.clone(), active_alert);
|
||||
self.alert_history.push(alert);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CorrelationEngine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CorrelationEngine {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -789,13 +845,16 @@ impl CorrelationEngine {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn correlate_event(&mut self, event: &StreamingEvent) -> Result<Option<CorrelatedIncident>, Box<dyn std::error::Error>> {
|
||||
pub async fn correlate_event(
|
||||
&mut self,
|
||||
event: &StreamingEvent,
|
||||
) -> Result<Option<CorrelatedIncident>, Box<dyn std::error::Error>> {
|
||||
// Simplified correlation logic
|
||||
for rule in &self.correlation_rules {
|
||||
if let Some(incident) = self.evaluate_correlation_rule(rule, event) {
|
||||
if let Some(_incident) = self.evaluate_correlation_rule(rule, event) {
|
||||
self.incident_tracker.incident_counter += 1;
|
||||
let incident_id = format!("incident_{}", self.incident_tracker.incident_counter);
|
||||
|
||||
|
||||
let correlated_incident = CorrelatedIncident {
|
||||
incident_id: incident_id.clone(),
|
||||
timestamp: SystemTime::now(),
|
||||
@@ -810,21 +869,33 @@ impl CorrelationEngine {
|
||||
status: IncidentStatus::Open,
|
||||
};
|
||||
|
||||
self.incident_tracker.incidents.insert(incident_id, correlated_incident.clone());
|
||||
self.incident_tracker
|
||||
.incidents
|
||||
.insert(incident_id, correlated_incident.clone());
|
||||
return Ok(Some(correlated_incident));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn evaluate_correlation_rule(&self, rule: &CorrelationRule, event: &StreamingEvent) -> Option<()> {
|
||||
fn evaluate_correlation_rule(
|
||||
&self,
|
||||
_rule: &CorrelationRule,
|
||||
_event: &StreamingEvent,
|
||||
) -> Option<()> {
|
||||
// Simplified correlation rule evaluation
|
||||
// In a real implementation, this would be much more sophisticated
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NotificationSystem {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl NotificationSystem {
|
||||
pub fn new() -> Self {
|
||||
let (tx, _) = mpsc::channel(1000);
|
||||
@@ -836,20 +907,35 @@ impl NotificationSystem {
|
||||
}
|
||||
|
||||
pub fn add_email_channel(&mut self, name: String, config: SmtpConfig) {
|
||||
let channel = EmailChannel { smtp_config: config };
|
||||
let channel = EmailChannel {
|
||||
smtp_config: config,
|
||||
};
|
||||
self.channels.insert(name, Box::new(channel));
|
||||
}
|
||||
|
||||
pub fn add_slack_channel(&mut self, name: String, webhook_url: String, default_channel: String) {
|
||||
let channel = SlackChannel { webhook_url, default_channel };
|
||||
pub fn add_slack_channel(
|
||||
&mut self,
|
||||
name: String,
|
||||
webhook_url: String,
|
||||
default_channel: String,
|
||||
) {
|
||||
let channel = SlackChannel {
|
||||
webhook_url,
|
||||
default_channel,
|
||||
};
|
||||
self.channels.insert(name, Box::new(channel));
|
||||
}
|
||||
|
||||
pub fn add_webhook_channel(&mut self, name: String, endpoint_url: String, headers: HashMap<String, String>) {
|
||||
let channel = WebhookChannel {
|
||||
endpoint_url,
|
||||
headers,
|
||||
auth_token: None
|
||||
pub fn add_webhook_channel(
|
||||
&mut self,
|
||||
name: String,
|
||||
endpoint_url: String,
|
||||
headers: HashMap<String, String>,
|
||||
) {
|
||||
let channel = WebhookChannel {
|
||||
endpoint_url,
|
||||
headers,
|
||||
auth_token: None,
|
||||
};
|
||||
self.channels.insert(name, Box::new(channel));
|
||||
}
|
||||
@@ -866,11 +952,11 @@ impl EventBuffer {
|
||||
|
||||
pub fn add_event(&mut self, event: StreamingEvent) {
|
||||
self.events.push(event);
|
||||
|
||||
|
||||
// Remove old events
|
||||
let cutoff_time = SystemTime::now() - self.retention_period;
|
||||
self.events.retain(|e| e.timestamp >= cutoff_time);
|
||||
|
||||
|
||||
// Limit buffer size
|
||||
if self.events.len() > self.max_size {
|
||||
self.events.drain(0..self.events.len() - self.max_size);
|
||||
@@ -879,8 +965,9 @@ impl EventBuffer {
|
||||
|
||||
pub fn get_events_in_window(&self, window: Duration) -> Vec<&StreamingEvent> {
|
||||
let cutoff_time = SystemTime::now() - window;
|
||||
self.events.iter()
|
||||
self.events
|
||||
.iter()
|
||||
.filter(|e| e.timestamp >= cutoff_time)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
DetectionEngine, DetectionResult, MemoryProtection, MemoryRegion, ProcessInfo, ThreadInfo,
|
||||
ThreatLevel,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, Instant};
|
||||
use crate::{
|
||||
DetectionEngine, DetectionResult, ThreatLevel, ProcessInfo, MemoryRegion,
|
||||
ThreadInfo, MemoryProtection, EvasionResult, ThreatContext
|
||||
};
|
||||
|
||||
/// Comprehensive Testing Framework for Ghost Detection Engine
|
||||
/// Provides unit tests, integration tests, and performance benchmarks
|
||||
@@ -189,10 +189,10 @@ pub struct ProcessAnalysisBenchmark {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ComplexityLevel {
|
||||
Simple, // Basic process with minimal memory regions
|
||||
Moderate, // Standard process with normal memory layout
|
||||
Complex, // Process with many threads and memory regions
|
||||
Extreme, // Heavily loaded process with maximum complexity
|
||||
Simple, // Basic process with minimal memory regions
|
||||
Moderate, // Standard process with normal memory layout
|
||||
Complex, // Process with many threads and memory regions
|
||||
Extreme, // Heavily loaded process with maximum complexity
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -205,10 +205,10 @@ pub struct MemoryScanningBenchmark {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ScanAlgorithm {
|
||||
Linear,
|
||||
Boyer_Moore,
|
||||
Knuth_Morris_Pratt,
|
||||
Aho_Corasick,
|
||||
SIMD_Optimized,
|
||||
BoyerMoore,
|
||||
KnuthMorrisPratt,
|
||||
AhoCorasick,
|
||||
SimdOptimized,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -234,9 +234,9 @@ pub struct SystemScanBenchmark {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ScanDepth {
|
||||
Surface, // Basic process enumeration
|
||||
Standard, // Process + memory analysis
|
||||
Deep, // Full analysis including threads
|
||||
Surface, // Basic process enumeration
|
||||
Standard, // Process + memory analysis
|
||||
Deep, // Full analysis including threads
|
||||
Comprehensive, // All detection modules enabled
|
||||
}
|
||||
|
||||
@@ -367,6 +367,12 @@ pub struct ValidationDetails {
|
||||
pub false_negative_rate: f32,
|
||||
}
|
||||
|
||||
impl Default for TestFramework {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestFramework {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -404,24 +410,20 @@ impl TestFramework {
|
||||
thread_count: 1,
|
||||
suspicious_indicators: Vec::new(),
|
||||
},
|
||||
memory_data: vec![
|
||||
MemoryTestData {
|
||||
base_address: 0x400000,
|
||||
size: 0x10000,
|
||||
protection: MemoryProtection::ReadExecute,
|
||||
contains_shellcode: false,
|
||||
shellcode_pattern: None,
|
||||
}
|
||||
],
|
||||
thread_data: vec![
|
||||
ThreadTestData {
|
||||
tid: 5678,
|
||||
entry_point: 0x401000,
|
||||
stack_base: 0x500000,
|
||||
stack_size: 0x10000,
|
||||
is_suspicious: false,
|
||||
}
|
||||
],
|
||||
memory_data: vec![MemoryTestData {
|
||||
base_address: 0x400000,
|
||||
size: 0x10000,
|
||||
protection: MemoryProtection::ReadExecute,
|
||||
contains_shellcode: false,
|
||||
shellcode_pattern: None,
|
||||
}],
|
||||
thread_data: vec![ThreadTestData {
|
||||
tid: 5678,
|
||||
entry_point: 0x401000,
|
||||
stack_base: 0x500000,
|
||||
stack_size: 0x10000,
|
||||
is_suspicious: false,
|
||||
}],
|
||||
injection_type: None,
|
||||
}),
|
||||
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Clean),
|
||||
@@ -444,29 +446,29 @@ impl TestFramework {
|
||||
"Suspicious API calls".to_string(),
|
||||
],
|
||||
},
|
||||
memory_data: vec![
|
||||
MemoryTestData {
|
||||
base_address: 0x200000,
|
||||
size: 0x1000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: true,
|
||||
shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2
|
||||
}
|
||||
],
|
||||
thread_data: vec![
|
||||
ThreadTestData {
|
||||
tid: 1111,
|
||||
entry_point: 0x200000,
|
||||
stack_base: 0x600000,
|
||||
stack_size: 0x10000,
|
||||
is_suspicious: true,
|
||||
}
|
||||
],
|
||||
memory_data: vec![MemoryTestData {
|
||||
base_address: 0x200000,
|
||||
size: 0x1000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: true,
|
||||
shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2
|
||||
}],
|
||||
thread_data: vec![ThreadTestData {
|
||||
tid: 1111,
|
||||
entry_point: 0x200000,
|
||||
stack_base: 0x600000,
|
||||
stack_size: 0x10000,
|
||||
is_suspicious: true,
|
||||
}],
|
||||
injection_type: Some(InjectionTestType::ShellcodeInjection),
|
||||
}),
|
||||
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Malicious),
|
||||
timeout: Duration::from_secs(10),
|
||||
tags: vec!["unit".to_string(), "detection".to_string(), "malware".to_string()],
|
||||
tags: vec![
|
||||
"unit".to_string(),
|
||||
"detection".to_string(),
|
||||
"malware".to_string(),
|
||||
],
|
||||
});
|
||||
|
||||
let test_suite = TestSuite {
|
||||
@@ -477,7 +479,8 @@ impl TestFramework {
|
||||
teardown_function: None,
|
||||
};
|
||||
|
||||
self.test_suites.insert("detection_engine".to_string(), test_suite);
|
||||
self.test_suites
|
||||
.insert("detection_engine".to_string(), test_suite);
|
||||
}
|
||||
|
||||
/// Create shellcode detection tests
|
||||
@@ -496,20 +499,18 @@ impl TestFramework {
|
||||
thread_count: 1,
|
||||
suspicious_indicators: Vec::new(),
|
||||
},
|
||||
memory_data: vec![
|
||||
MemoryTestData {
|
||||
base_address: 0x300000,
|
||||
size: 0x1000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: true,
|
||||
shellcode_pattern: Some(vec![
|
||||
0x31, 0xC0, // XOR EAX, EAX
|
||||
0x50, // PUSH EAX
|
||||
0x68, 0x2F, 0x2F, 0x73, 0x68, // PUSH //sh
|
||||
0x68, 0x2F, 0x62, 0x69, 0x6E, // PUSH /bin
|
||||
]),
|
||||
}
|
||||
],
|
||||
memory_data: vec![MemoryTestData {
|
||||
base_address: 0x300000,
|
||||
size: 0x1000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: true,
|
||||
shellcode_pattern: Some(vec![
|
||||
0x31, 0xC0, // XOR EAX, EAX
|
||||
0x50, // PUSH EAX
|
||||
0x68, 0x2F, 0x2F, 0x73, 0x68, // PUSH //sh
|
||||
0x68, 0x2F, 0x62, 0x69, 0x6E, // PUSH /bin
|
||||
]),
|
||||
}],
|
||||
thread_data: Vec::new(),
|
||||
injection_type: Some(InjectionTestType::ShellcodeInjection),
|
||||
}),
|
||||
@@ -526,7 +527,8 @@ impl TestFramework {
|
||||
teardown_function: None,
|
||||
};
|
||||
|
||||
self.test_suites.insert("shellcode_detection".to_string(), test_suite);
|
||||
self.test_suites
|
||||
.insert("shellcode_detection".to_string(), test_suite);
|
||||
}
|
||||
|
||||
/// Create process hollowing detection tests
|
||||
@@ -547,15 +549,13 @@ impl TestFramework {
|
||||
"Unexpected memory layout".to_string(),
|
||||
],
|
||||
},
|
||||
memory_data: vec![
|
||||
MemoryTestData {
|
||||
base_address: 0x400000,
|
||||
size: 0x20000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: false,
|
||||
shellcode_pattern: None,
|
||||
}
|
||||
],
|
||||
memory_data: vec![MemoryTestData {
|
||||
base_address: 0x400000,
|
||||
size: 0x20000,
|
||||
protection: MemoryProtection::ReadWriteExecute,
|
||||
contains_shellcode: false,
|
||||
shellcode_pattern: None,
|
||||
}],
|
||||
thread_data: Vec::new(),
|
||||
injection_type: Some(InjectionTestType::ProcessHollowing),
|
||||
}),
|
||||
@@ -572,7 +572,8 @@ impl TestFramework {
|
||||
teardown_function: None,
|
||||
};
|
||||
|
||||
self.test_suites.insert("process_hollowing".to_string(), test_suite);
|
||||
self.test_suites
|
||||
.insert("process_hollowing".to_string(), test_suite);
|
||||
}
|
||||
|
||||
/// Create evasion detection tests
|
||||
@@ -610,7 +611,8 @@ impl TestFramework {
|
||||
teardown_function: None,
|
||||
};
|
||||
|
||||
self.test_suites.insert("evasion_detection".to_string(), test_suite);
|
||||
self.test_suites
|
||||
.insert("evasion_detection".to_string(), test_suite);
|
||||
}
|
||||
|
||||
/// Create threat intelligence tests
|
||||
@@ -626,12 +628,10 @@ impl TestFramework {
|
||||
benchmarks.push(Benchmark {
|
||||
name: "single_process_analysis".to_string(),
|
||||
description: "Benchmark single process analysis performance".to_string(),
|
||||
benchmark_function: BenchmarkFunction::ProcessAnalysis(
|
||||
ProcessAnalysisBenchmark {
|
||||
process_count: 1,
|
||||
complexity_level: ComplexityLevel::Moderate,
|
||||
}
|
||||
),
|
||||
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
|
||||
process_count: 1,
|
||||
complexity_level: ComplexityLevel::Moderate,
|
||||
}),
|
||||
warm_up_iterations: 10,
|
||||
measurement_iterations: 100,
|
||||
target_metrics: vec![
|
||||
@@ -644,12 +644,10 @@ impl TestFramework {
|
||||
benchmarks.push(Benchmark {
|
||||
name: "bulk_process_analysis".to_string(),
|
||||
description: "Benchmark bulk process analysis performance".to_string(),
|
||||
benchmark_function: BenchmarkFunction::ProcessAnalysis(
|
||||
ProcessAnalysisBenchmark {
|
||||
process_count: 100,
|
||||
complexity_level: ComplexityLevel::Simple,
|
||||
}
|
||||
),
|
||||
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
|
||||
process_count: 100,
|
||||
complexity_level: ComplexityLevel::Simple,
|
||||
}),
|
||||
warm_up_iterations: 5,
|
||||
measurement_iterations: 20,
|
||||
target_metrics: vec![
|
||||
@@ -666,7 +664,8 @@ impl TestFramework {
|
||||
baseline_measurements: HashMap::new(),
|
||||
};
|
||||
|
||||
self.benchmark_suites.insert("performance".to_string(), benchmark_suite);
|
||||
self.benchmark_suites
|
||||
.insert("performance".to_string(), benchmark_suite);
|
||||
}
|
||||
|
||||
/// Create integration tests
|
||||
@@ -714,17 +713,20 @@ impl TestFramework {
|
||||
teardown_function: None,
|
||||
};
|
||||
|
||||
self.test_suites.insert("integration".to_string(), test_suite);
|
||||
self.test_suites
|
||||
.insert("integration".to_string(), test_suite);
|
||||
}
|
||||
|
||||
/// Run all test suites
|
||||
pub fn run_all_tests(&mut self) -> TestRunReport {
|
||||
let mut report = TestRunReport::new();
|
||||
|
||||
let suite_names_and_tests: Vec<(String, TestSuite)> = self.test_suites.iter()
|
||||
|
||||
let suite_names_and_tests: Vec<(String, TestSuite)> = self
|
||||
.test_suites
|
||||
.iter()
|
||||
.map(|(name, suite)| (name.clone(), suite.clone()))
|
||||
.collect();
|
||||
|
||||
|
||||
for (suite_name, test_suite) in suite_names_and_tests {
|
||||
let suite_results = self.run_test_suite(&test_suite);
|
||||
report.add_suite_results(suite_name, suite_results);
|
||||
@@ -759,20 +761,16 @@ impl TestFramework {
|
||||
/// Run a single test case
|
||||
fn run_test_case(&mut self, test_case: &TestCase) -> TestResult {
|
||||
let start_time = Instant::now();
|
||||
|
||||
|
||||
let status = match &test_case.test_function {
|
||||
TestFunction::DetectionTest(params) => {
|
||||
self.run_detection_test(params, &test_case.expected_result)
|
||||
}
|
||||
TestFunction::PerformanceTest(params) => {
|
||||
self.run_performance_test(params)
|
||||
}
|
||||
TestFunction::PerformanceTest(params) => self.run_performance_test(params),
|
||||
TestFunction::IntegrationTest(params) => {
|
||||
self.run_integration_test(params, &test_case.expected_result)
|
||||
}
|
||||
TestFunction::StressTest(params) => {
|
||||
self.run_stress_test(params)
|
||||
}
|
||||
TestFunction::StressTest(params) => self.run_stress_test(params),
|
||||
};
|
||||
|
||||
let execution_time = start_time.elapsed();
|
||||
@@ -793,7 +791,11 @@ impl TestFramework {
|
||||
}
|
||||
|
||||
/// Run detection test
|
||||
fn run_detection_test(&self, params: &DetectionTestParams, expected: &ExpectedResult) -> TestStatus {
|
||||
fn run_detection_test(
|
||||
&self,
|
||||
params: &DetectionTestParams,
|
||||
expected: &ExpectedResult,
|
||||
) -> TestStatus {
|
||||
// Create test detection engine
|
||||
let mut engine = match DetectionEngine::new() {
|
||||
Ok(engine) => engine,
|
||||
@@ -809,24 +811,28 @@ impl TestFramework {
|
||||
thread_count: params.process_data.thread_count,
|
||||
};
|
||||
|
||||
let memory_regions: Vec<MemoryRegion> = params.memory_data.iter().map(|mem| {
|
||||
MemoryRegion {
|
||||
let memory_regions: Vec<MemoryRegion> = params
|
||||
.memory_data
|
||||
.iter()
|
||||
.map(|mem| MemoryRegion {
|
||||
base_address: mem.base_address,
|
||||
size: mem.size,
|
||||
protection: mem.protection.clone(),
|
||||
protection: mem.protection,
|
||||
region_type: "PRIVATE".to_string(),
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let threads: Vec<ThreadInfo> = params.thread_data.iter().map(|thread| {
|
||||
ThreadInfo {
|
||||
let threads: Vec<ThreadInfo> = params
|
||||
.thread_data
|
||||
.iter()
|
||||
.map(|thread| ThreadInfo {
|
||||
tid: thread.tid,
|
||||
owner_pid: process_info.pid,
|
||||
start_address: thread.entry_point,
|
||||
creation_time: 0,
|
||||
state: crate::thread::ThreadState::Running,
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Run detection
|
||||
let result = engine.analyze_process(&process_info, &memory_regions, Some(&threads));
|
||||
@@ -878,7 +884,11 @@ impl TestFramework {
|
||||
}
|
||||
|
||||
/// Run integration test
|
||||
fn run_integration_test(&self, _params: &IntegrationTestParams, _expected: &ExpectedResult) -> TestStatus {
|
||||
fn run_integration_test(
|
||||
&self,
|
||||
_params: &IntegrationTestParams,
|
||||
_expected: &ExpectedResult,
|
||||
) -> TestStatus {
|
||||
// Implementation would test component interactions
|
||||
TestStatus::Passed
|
||||
}
|
||||
@@ -901,6 +911,12 @@ pub struct TestRunReport {
|
||||
pub suite_results: HashMap<String, Vec<TestResult>>,
|
||||
}
|
||||
|
||||
impl Default for TestRunReport {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestRunReport {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -937,6 +953,12 @@ impl TestRunReport {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TestDataGenerator {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestDataGenerator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -947,7 +969,7 @@ impl TestDataGenerator {
|
||||
}
|
||||
|
||||
/// Generate synthetic test processes
|
||||
pub fn generate_test_processes(&self, count: usize) -> Vec<ProcessTestData> {
|
||||
pub fn generate_test_processes(&self, _count: usize) -> Vec<ProcessTestData> {
|
||||
// Implementation would generate realistic test process data
|
||||
Vec::new()
|
||||
}
|
||||
@@ -962,6 +984,12 @@ impl TestDataGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PerformanceProfiler {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PerformanceProfiler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -983,7 +1011,9 @@ impl PerformanceProfiler {
|
||||
|
||||
/// Stop profiling and collect results
|
||||
pub fn stop_profiling(&mut self, session_id: &str) -> Option<Vec<Measurement>> {
|
||||
self.active_profiles.remove(session_id).map(|session| session.measurements)
|
||||
self.active_profiles
|
||||
.remove(session_id)
|
||||
.map(|session| session.measurements)
|
||||
}
|
||||
|
||||
/// Record a measurement
|
||||
@@ -997,4 +1027,4 @@ impl PerformanceProfiler {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ mod platform {
|
||||
use anyhow::{Context, Result};
|
||||
use windows::Win32::Foundation::CloseHandle;
|
||||
use windows::Win32::System::Diagnostics::ToolHelp::{
|
||||
CreateToolhelp32Snapshot, Thread32First, Thread32Next, THREADENTRY32, TH32CS_SNAPTHREAD,
|
||||
CreateToolhelp32Snapshot, Thread32First, Thread32Next, TH32CS_SNAPTHREAD, THREADENTRY32,
|
||||
};
|
||||
use windows::Win32::System::Threading::{
|
||||
OpenThread, THREAD_QUERY_INFORMATION, THREAD_QUERY_LIMITED_INFORMATION,
|
||||
@@ -216,8 +216,7 @@ mod platform {
|
||||
|
||||
pub fn enumerate_threads(pid: u32) -> Result<Vec<ThreadInfo>> {
|
||||
let task_dir = format!("/proc/{}/task", pid);
|
||||
let entries =
|
||||
fs::read_dir(&task_dir).context(format!("Failed to read {}", task_dir))?;
|
||||
let entries = fs::read_dir(&task_dir).context(format!("Failed to read {}", task_dir))?;
|
||||
|
||||
let mut threads = Vec::new();
|
||||
|
||||
@@ -341,11 +340,7 @@ mod platform {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn task_for_pid(
|
||||
target_tport: mach_port_t,
|
||||
pid: i32,
|
||||
task: *mut mach_port_t,
|
||||
) -> i32;
|
||||
fn task_for_pid(target_tport: mach_port_t, pid: i32, task: *mut mach_port_t) -> i32;
|
||||
fn mach_task_self() -> mach_port_t;
|
||||
fn task_threads(
|
||||
target_task: mach_port_t,
|
||||
@@ -359,11 +354,7 @@ mod platform {
|
||||
thread_info_out_cnt: *mut u32,
|
||||
) -> i32;
|
||||
fn mach_port_deallocate(task: mach_port_t, name: mach_port_t) -> i32;
|
||||
fn vm_deallocate(
|
||||
target_task: mach_port_t,
|
||||
address: usize,
|
||||
size: usize,
|
||||
) -> i32;
|
||||
fn vm_deallocate(target_task: mach_port_t, address: usize, size: usize) -> i32;
|
||||
}
|
||||
|
||||
let mut threads = Vec::new();
|
||||
@@ -420,11 +411,9 @@ mod platform {
|
||||
|
||||
// Calculate creation time from user_time + system_time (accumulated time)
|
||||
let creation_time = if kr == 0 {
|
||||
let total_microseconds = (info.user_time.seconds as u64 * 1_000_000
|
||||
+ info.user_time.microseconds as u64)
|
||||
(info.user_time.seconds as u64 * 1_000_000 + info.user_time.microseconds as u64)
|
||||
+ (info.system_time.seconds as u64 * 1_000_000
|
||||
+ info.system_time.microseconds as u64);
|
||||
total_microseconds
|
||||
+ info.system_time.microseconds as u64)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::{DetectionResult, ProcessInfo, ThreatLevel};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::{DetectionResult, ThreatLevel, ProcessInfo};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// Threat Intelligence Integration Module
|
||||
/// Provides real-time threat context and IOC matching
|
||||
#[derive(Debug)]
|
||||
pub struct ThreatIntelligence {
|
||||
ioc_database: IocDatabase,
|
||||
threat_feeds: Vec<ThreatFeed>,
|
||||
@@ -81,6 +82,7 @@ pub struct Campaign {
|
||||
pub iocs: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IocDatabase {
|
||||
indicators: HashMap<String, IndicatorOfCompromise>,
|
||||
hash_index: HashMap<String, Vec<String>>,
|
||||
@@ -127,6 +129,7 @@ pub struct FrequencyRequirement {
|
||||
pub time_window: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThreatFeed {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
@@ -153,6 +156,7 @@ pub struct FeedCredential {
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AttributionEngine {
|
||||
threat_actors: HashMap<String, ThreatActor>,
|
||||
campaigns: HashMap<String, Campaign>,
|
||||
@@ -171,18 +175,32 @@ pub struct AttributionRule {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AttributionCondition {
|
||||
IocMatch { ioc_types: Vec<IocType>, min_matches: u32 },
|
||||
TechniquePattern { techniques: Vec<String>, correlation: f32 },
|
||||
TemporalPattern { time_windows: Vec<Duration>, frequency: u32 },
|
||||
GeographicalIndicator { regions: Vec<String>, confidence: f32 },
|
||||
IocMatch {
|
||||
ioc_types: Vec<IocType>,
|
||||
min_matches: u32,
|
||||
},
|
||||
TechniquePattern {
|
||||
techniques: Vec<String>,
|
||||
correlation: f32,
|
||||
},
|
||||
TemporalPattern {
|
||||
time_windows: Vec<Duration>,
|
||||
frequency: u32,
|
||||
},
|
||||
GeographicalIndicator {
|
||||
regions: Vec<String>,
|
||||
confidence: f32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SimilarityCalculator {
|
||||
technique_weights: HashMap<String, f32>,
|
||||
temporal_weights: HashMap<String, f32>,
|
||||
behavioral_weights: HashMap<String, f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReputationCache {
|
||||
process_reputations: HashMap<String, ProcessReputation>,
|
||||
ip_reputations: HashMap<String, IpReputation>,
|
||||
@@ -311,7 +329,7 @@ impl ThreatIntelligence {
|
||||
|
||||
// Initialize with basic IOCs
|
||||
self.load_default_iocs().await?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -321,7 +339,10 @@ impl ThreatIntelligence {
|
||||
let mut risk_score = 0.0f32;
|
||||
|
||||
// Check process name against IOCs
|
||||
if let Some(iocs) = self.ioc_database.lookup_process_name(&detection.process.name) {
|
||||
if let Some(iocs) = self
|
||||
.ioc_database
|
||||
.lookup_process_name(&detection.process.name)
|
||||
{
|
||||
matched_iocs.extend(iocs);
|
||||
}
|
||||
|
||||
@@ -349,8 +370,9 @@ impl ThreatIntelligence {
|
||||
}
|
||||
|
||||
// Perform attribution analysis
|
||||
let (threat_actor, campaign, attribution_confidence) =
|
||||
self.attribution_engine.analyze_attribution(&matched_iocs, &detection.indicators);
|
||||
let (threat_actor, campaign, attribution_confidence) = self
|
||||
.attribution_engine
|
||||
.analyze_attribution(&matched_iocs, &detection.indicators);
|
||||
|
||||
// Generate recommended actions
|
||||
let recommended_actions = self.generate_recommendations(&matched_iocs, risk_score);
|
||||
@@ -373,16 +395,19 @@ impl ThreatIntelligence {
|
||||
/// Update all threat feeds
|
||||
pub async fn update_threat_feeds(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut updates = Vec::new();
|
||||
|
||||
|
||||
for (idx, feed) in self.threat_feeds.iter().enumerate() {
|
||||
if SystemTime::now().duration_since(feed.last_update).unwrap_or_default()
|
||||
>= feed.update_interval {
|
||||
if SystemTime::now()
|
||||
.duration_since(feed.last_update)
|
||||
.unwrap_or_default()
|
||||
>= feed.update_interval
|
||||
{
|
||||
// Fetch data inline to avoid borrow issues
|
||||
let iocs = Vec::new(); // Stub implementation
|
||||
updates.push((idx, iocs));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (idx, iocs) in updates {
|
||||
self.ioc_database.update_indicators(iocs);
|
||||
self.threat_feeds[idx].last_update = SystemTime::now();
|
||||
@@ -391,7 +416,10 @@ impl ThreatIntelligence {
|
||||
}
|
||||
|
||||
/// Fetch data from a threat feed
|
||||
async fn fetch_feed_data(&self, feed: &ThreatFeed) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
async fn fetch_feed_data(
|
||||
&self,
|
||||
feed: &ThreatFeed,
|
||||
) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
// Implementation would depend on feed type
|
||||
match feed.feed_type {
|
||||
FeedType::JSON => self.fetch_json_feed(feed).await,
|
||||
@@ -401,19 +429,28 @@ impl ThreatIntelligence {
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_json_feed(&self, feed: &ThreatFeed) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
async fn fetch_json_feed(
|
||||
&self,
|
||||
feed: &ThreatFeed,
|
||||
) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
// Placeholder implementation
|
||||
// In a real implementation, this would fetch from the feed URL
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn fetch_stix_feed(&self, feed: &ThreatFeed) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
async fn fetch_stix_feed(
|
||||
&self,
|
||||
feed: &ThreatFeed,
|
||||
) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
// Placeholder implementation
|
||||
// In a real implementation, this would parse STIX/TAXII data
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn fetch_csv_feed(&self, feed: &ThreatFeed) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
async fn fetch_csv_feed(
|
||||
&self,
|
||||
feed: &ThreatFeed,
|
||||
) -> Result<Vec<IndicatorOfCompromise>, Box<dyn std::error::Error>> {
|
||||
// Placeholder implementation
|
||||
// In a real implementation, this would parse CSV threat data
|
||||
Ok(Vec::new())
|
||||
@@ -431,7 +468,10 @@ impl ThreatIntelligence {
|
||||
confidence: 0.6,
|
||||
created_date: SystemTime::now(),
|
||||
expiry_date: None,
|
||||
tags: vec!["process-injection".to_string(), "living-off-the-land".to_string()],
|
||||
tags: vec![
|
||||
"process-injection".to_string(),
|
||||
"living-off-the-land".to_string(),
|
||||
],
|
||||
mitre_techniques: vec!["T1055".to_string()],
|
||||
},
|
||||
IndicatorOfCompromise {
|
||||
@@ -467,7 +507,11 @@ impl ThreatIntelligence {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_recommendations(&self, iocs: &[IndicatorOfCompromise], risk_score: f32) -> Vec<String> {
|
||||
fn generate_recommendations(
|
||||
&self,
|
||||
iocs: &[IndicatorOfCompromise],
|
||||
risk_score: f32,
|
||||
) -> Vec<String> {
|
||||
let mut recommendations = Vec::new();
|
||||
|
||||
if risk_score > 0.8 {
|
||||
@@ -487,9 +531,15 @@ impl ThreatIntelligence {
|
||||
for ioc in iocs {
|
||||
for technique in &ioc.mitre_techniques {
|
||||
match technique.as_str() {
|
||||
"T1055" => recommendations.push("Deploy process injection countermeasures".to_string()),
|
||||
"T1055.001" => recommendations.push("Monitor DLL loading activities".to_string()),
|
||||
"T1055.002" => recommendations.push("Implement PE injection detection".to_string()),
|
||||
"T1055" => {
|
||||
recommendations.push("Deploy process injection countermeasures".to_string())
|
||||
}
|
||||
"T1055.001" => {
|
||||
recommendations.push("Monitor DLL loading activities".to_string())
|
||||
}
|
||||
"T1055.002" => {
|
||||
recommendations.push("Implement PE injection detection".to_string())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -518,12 +568,14 @@ impl IocDatabase {
|
||||
// Update indexes for fast lookup
|
||||
match ioc.ioc_type {
|
||||
IocType::FileHash => {
|
||||
self.hash_index.entry(ioc.value.clone())
|
||||
self.hash_index
|
||||
.entry(ioc.value.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ioc.id.clone());
|
||||
}
|
||||
IocType::MemorySignature | IocType::BehaviorPattern => {
|
||||
self.pattern_index.entry(ioc.value.clone())
|
||||
self.pattern_index
|
||||
.entry(ioc.value.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ioc.id.clone());
|
||||
}
|
||||
@@ -532,23 +584,33 @@ impl IocDatabase {
|
||||
}
|
||||
|
||||
pub fn lookup_process_name(&self, name: &str) -> Option<Vec<IndicatorOfCompromise>> {
|
||||
let matches: Vec<_> = self.indicators
|
||||
let matches: Vec<_> = self
|
||||
.indicators
|
||||
.values()
|
||||
.filter(|ioc| ioc.ioc_type == IocType::ProcessName && ioc.value == name)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if matches.is_empty() { None } else { Some(matches) }
|
||||
|
||||
if matches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(matches)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_process_path(&self, path: &str) -> Option<Vec<IndicatorOfCompromise>> {
|
||||
let matches: Vec<_> = self.indicators
|
||||
let matches: Vec<_> = self
|
||||
.indicators
|
||||
.values()
|
||||
.filter(|ioc| ioc.ioc_type == IocType::ProcessPath && path.contains(&ioc.value))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if matches.is_empty() { None } else { Some(matches) }
|
||||
|
||||
if matches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(matches)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_memory_signature(&self, signature: &str) -> Option<Vec<IndicatorOfCompromise>> {
|
||||
@@ -558,7 +620,11 @@ impl IocDatabase {
|
||||
.filter_map(|id| self.indicators.get(id))
|
||||
.cloned()
|
||||
.collect();
|
||||
if matches.is_empty() { None } else { Some(matches) }
|
||||
if matches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(matches)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -581,9 +647,11 @@ impl AttributionEngine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze_attribution(&self, iocs: &[IndicatorOfCompromise], indicators: &[String])
|
||||
-> (Option<ThreatActor>, Option<Campaign>, f32) {
|
||||
|
||||
pub fn analyze_attribution(
|
||||
&self,
|
||||
iocs: &[IndicatorOfCompromise],
|
||||
indicators: &[String],
|
||||
) -> (Option<ThreatActor>, Option<Campaign>, f32) {
|
||||
let mut best_actor: Option<ThreatActor> = None;
|
||||
let mut best_campaign: Option<Campaign> = None;
|
||||
let mut best_confidence = 0.0f32;
|
||||
@@ -591,7 +659,7 @@ impl AttributionEngine {
|
||||
// Analyze each attribution rule
|
||||
for rule in &self.attribution_rules {
|
||||
let confidence = self.evaluate_attribution_rule(rule, iocs, indicators);
|
||||
|
||||
|
||||
if confidence > best_confidence {
|
||||
best_confidence = confidence;
|
||||
if let Some(actor) = self.threat_actors.get(&rule.threat_actor) {
|
||||
@@ -608,29 +676,40 @@ impl AttributionEngine {
|
||||
(best_actor, best_campaign, best_confidence)
|
||||
}
|
||||
|
||||
fn evaluate_attribution_rule(&self, rule: &AttributionRule,
|
||||
iocs: &[IndicatorOfCompromise],
|
||||
indicators: &[String]) -> f32 {
|
||||
fn evaluate_attribution_rule(
|
||||
&self,
|
||||
rule: &AttributionRule,
|
||||
iocs: &[IndicatorOfCompromise],
|
||||
indicators: &[String],
|
||||
) -> f32 {
|
||||
let mut total_confidence = 0.0f32;
|
||||
let mut condition_count = 0;
|
||||
|
||||
for condition in &rule.conditions {
|
||||
match condition {
|
||||
AttributionCondition::IocMatch { ioc_types, min_matches } => {
|
||||
let matches = iocs.iter()
|
||||
AttributionCondition::IocMatch {
|
||||
ioc_types,
|
||||
min_matches,
|
||||
} => {
|
||||
let matches = iocs
|
||||
.iter()
|
||||
.filter(|ioc| ioc_types.contains(&ioc.ioc_type))
|
||||
.count() as u32;
|
||||
|
||||
|
||||
if matches >= *min_matches {
|
||||
total_confidence += rule.confidence_weight;
|
||||
}
|
||||
}
|
||||
AttributionCondition::TechniquePattern { techniques, correlation } => {
|
||||
let technique_matches = iocs.iter()
|
||||
AttributionCondition::TechniquePattern {
|
||||
techniques,
|
||||
correlation,
|
||||
} => {
|
||||
let technique_matches = iocs
|
||||
.iter()
|
||||
.flat_map(|ioc| &ioc.mitre_techniques)
|
||||
.filter(|tech| techniques.contains(tech))
|
||||
.count();
|
||||
|
||||
|
||||
if technique_matches as f32 / techniques.len() as f32 >= *correlation {
|
||||
total_confidence += rule.confidence_weight;
|
||||
}
|
||||
@@ -667,4 +746,4 @@ impl ReputationCache {
|
||||
cache_ttl: Duration::from_secs(3600), // 1 hour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{ProcessInfo, MemoryRegion, GhostError};
|
||||
use crate::{GhostError, MemoryRegion, ProcessInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::SystemTime;
|
||||
@@ -87,22 +87,20 @@ impl DynamicYaraEngine {
|
||||
|
||||
pub async fn update_rules(&mut self) -> Result<usize, GhostError> {
|
||||
let mut updated_count = 0;
|
||||
|
||||
|
||||
for source in &mut self.sources {
|
||||
if !source.enabled {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Simulate rule download
|
||||
let new_rules = vec![
|
||||
YaraRule {
|
||||
name: format!("generic_malware_{}", updated_count + 1),
|
||||
content: "rule generic_malware { condition: true }".to_string(),
|
||||
source: source.name.clone(),
|
||||
threat_level: ThreatLevel::Medium,
|
||||
last_updated: SystemTime::now(),
|
||||
},
|
||||
];
|
||||
let new_rules = vec![YaraRule {
|
||||
name: format!("generic_malware_{}", updated_count + 1),
|
||||
content: "rule generic_malware { condition: true }".to_string(),
|
||||
source: source.name.clone(),
|
||||
threat_level: ThreatLevel::Medium,
|
||||
last_updated: SystemTime::now(),
|
||||
}];
|
||||
|
||||
self.rules.extend(new_rules);
|
||||
source.rule_count = self.rules.len();
|
||||
@@ -125,7 +123,7 @@ impl DynamicYaraEngine {
|
||||
// Simulate YARA scanning
|
||||
for (i, region) in memory_regions.iter().enumerate() {
|
||||
bytes_scanned += region.size;
|
||||
|
||||
|
||||
// Simulate finding suspicious patterns
|
||||
if region.protection.is_executable() && region.protection.is_writable() {
|
||||
matches.push(RuleMatch {
|
||||
@@ -138,9 +136,7 @@ impl DynamicYaraEngine {
|
||||
}
|
||||
}
|
||||
|
||||
let scan_time_ms = start_time.elapsed()
|
||||
.unwrap_or_default()
|
||||
.as_millis() as u64;
|
||||
let scan_time_ms = start_time.elapsed().unwrap_or_default().as_millis() as u64;
|
||||
|
||||
Ok(YaraScanResult {
|
||||
matches,
|
||||
@@ -156,4 +152,4 @@ impl DynamicYaraEngine {
|
||||
pub fn get_sources(&self) -> &[YaraRuleSource] {
|
||||
&self.sources
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user