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