pub mod rules;
pub mod behavior;
pub mod heuristics;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU64, Ordering};
use wdk::println;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Severity {
Info = 0,
Low = 1,
Medium = 2,
High = 3,
Critical = 4,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u16)]
pub enum MitreTactic {
InitialAccess = 1,
Execution = 2,
Persistence = 3,
PrivilegeEscalation = 4,
DefenseEvasion = 5,
CredentialAccess = 6,
Discovery = 7,
LateralMovement = 8,
Collection = 9,
CommandAndControl = 11,
Exfiltration = 10,
Impact = 40,
}
#[derive(Debug, Clone)]
pub struct Alert {
pub id: u64,
pub rule_id: u32,
pub severity: Severity,
pub tactic: MitreTactic,
pub technique_id: [u8; 8],
pub title: [u8; 128],
pub description: [u8; 512],
pub source_pid: u32,
pub target_pid: Option<u32>,
pub timestamp: u64,
pub event_ids: Vec<u64>,
pub action: RecommendedAction,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RecommendedAction {
Log,
Alert,
Block,
Terminate,
Quarantine,
Isolate,
}
#[derive(Debug, Clone)]
pub struct DetectionContext {
pub pid: u32,
pub ppid: u32,
pub process_name: [u8; 64],
pub image_path: [u16; 260],
pub command_line: [u16; 512],
pub user_sid: [u8; 68],
pub is_elevated: bool,
pub is_system: bool,
pub session_id: u32,
}
pub struct DetectionEngine {
rules: Vec<rules::DetectionRule>,
analyzers: Vec<Box<dyn behavior::BehaviorAnalyzer>>,
alert_counter: AtomicU64,
alert_callback: Option<fn(&Alert)>,
stats: DetectionStats,
process_cache: BTreeMap<u32, ProcessActivity>,
}
#[derive(Debug, Default)]
pub struct DetectionStats {
pub events_processed: AtomicU64,
pub alerts_generated: AtomicU64,
pub operations_blocked: AtomicU64,
pub false_positives: AtomicU64,
}
#[derive(Debug, Clone)]
pub struct ProcessActivity {
pub pid: u32,
pub create_time: u64,
pub children: Vec<u32>,
pub threads_created: u32,
pub remote_threads: u32,
pub images_loaded: u32,
pub files_accessed: u32,
pub registry_ops: u32,
pub network_conns: u32,
pub suspicious_count: u32,
pub last_activity: u64,
}
impl DetectionEngine {
pub fn new() -> Self {
Self {
rules: Vec::new(),
analyzers: Vec::new(),
alert_counter: AtomicU64::new(1),
alert_callback: None,
stats: DetectionStats::default(),
process_cache: BTreeMap::new(),
}
}
pub fn load_default_rules(&mut self) {
self.add_rule(rules::DetectionRule {
id: 1,
name: *b"RemoteThreadInjection\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
severity: Severity::High,
tactic: MitreTactic::DefenseEvasion,
technique_id: *b"T1055 ",
enabled: true,
rule_type: rules::RuleType::ThreadCreation,
conditions: Vec::new(),
});
self.add_rule(rules::DetectionRule {
id: 2,
name: *b"LsassAccess\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
severity: Severity::Critical,
tactic: MitreTactic::CredentialAccess,
technique_id: *b"T1003 ",
enabled: true,
rule_type: rules::RuleType::ProcessAccess,
conditions: Vec::new(),
});
self.add_rule(rules::DetectionRule {
id: 3,
name: *b"RegistryRunKey\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
severity: Severity::Medium,
tactic: MitreTactic::Persistence,
technique_id: *b"T1547 ",
enabled: true,
rule_type: rules::RuleType::RegistryMod,
conditions: Vec::new(),
});
self.add_rule(rules::DetectionRule {
id: 4,
name: *b"AmsiBypass\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
severity: Severity::High,
tactic: MitreTactic::DefenseEvasion,
technique_id: *b"T1562 ",
enabled: true,
rule_type: rules::RuleType::MemoryMod,
conditions: Vec::new(),
});
self.add_rule(rules::DetectionRule {
id: 5,
name: *b"RansomwareIndicator\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
severity: Severity::Critical,
tactic: MitreTactic::Impact,
technique_id: *b"T1486 ",
enabled: true,
rule_type: rules::RuleType::FileOp,
conditions: Vec::new(),
});
println!("[Leviathan] Loaded {} detection rules", self.rules.len());
}
pub fn add_rule(&mut self, rule: rules::DetectionRule) {
self.rules.push(rule);
}
pub fn set_alert_callback(&mut self, callback: fn(&Alert)) {
self.alert_callback = Some(callback);
}
pub fn process_event(
&mut self,
event_type: EventType,
context: &DetectionContext,
event_data: &[u8],
) -> Option<Alert> {
self.stats.events_processed.fetch_add(1, Ordering::Relaxed);
self.update_activity(context.pid, event_type);
for rule in &self.rules {
if !rule.enabled {
continue;
}
if let Some(alert) = self.evaluate_rule(rule, event_type, context, event_data) {
self.stats.alerts_generated.fetch_add(1, Ordering::Relaxed);
if let Some(callback) = self.alert_callback {
callback(&alert);
}
return Some(alert);
}
}
for analyzer in &self.analyzers {
if let Some(alert) = analyzer.analyze(context, &self.process_cache) {
self.stats.alerts_generated.fetch_add(1, Ordering::Relaxed);
return Some(alert);
}
}
None
}
fn evaluate_rule(
&self,
rule: &rules::DetectionRule,
event_type: EventType,
context: &DetectionContext,
_event_data: &[u8],
) -> Option<Alert> {
if !rule.matches_event_type(event_type) {
return None;
}
if !rule.evaluate_conditions(context) {
return None;
}
let alert_id = self.alert_counter.fetch_add(1, Ordering::SeqCst);
Some(Alert {
id: alert_id,
rule_id: rule.id,
severity: rule.severity,
tactic: rule.tactic,
technique_id: rule.technique_id,
title: {
let mut title = [0u8; 128];
let len = core::cmp::min(rule.name.len(), 128);
title[..len].copy_from_slice(&rule.name[..len]);
title
},
description: [0u8; 512],
source_pid: context.pid,
target_pid: None,
timestamp: get_timestamp(),
event_ids: Vec::new(),
action: severity_to_action(rule.severity),
})
}
fn update_activity(&mut self, pid: u32, event_type: EventType) {
let activity = self.process_cache.entry(pid).or_insert_with(|| ProcessActivity {
pid,
create_time: get_timestamp(),
children: Vec::new(),
threads_created: 0,
remote_threads: 0,
images_loaded: 0,
files_accessed: 0,
registry_ops: 0,
network_conns: 0,
suspicious_count: 0,
last_activity: 0,
});
activity.last_activity = get_timestamp();
match event_type {
EventType::ProcessCreate => activity.children.push(pid),
EventType::ThreadCreate => activity.threads_created += 1,
EventType::ImageLoad => activity.images_loaded += 1,
EventType::FileOp => activity.files_accessed += 1,
EventType::RegistryOp => activity.registry_ops += 1,
EventType::NetworkOp => activity.network_conns += 1,
_ => {}
}
}
pub fn cleanup_cache(&mut self, max_age_ms: u64) {
let now = get_timestamp();
let threshold = now.saturating_sub(max_age_ms * 10000);
self.process_cache.retain(|_, activity| {
activity.last_activity > threshold
});
}
pub fn get_stats(&self) -> &DetectionStats {
&self.stats
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EventType {
ProcessCreate,
ProcessExit,
ThreadCreate,
ThreadExit,
ImageLoad,
FileOp,
RegistryOp,
NetworkOp,
MemoryOp,
HandleOp,
}
fn severity_to_action(severity: Severity) -> RecommendedAction {
match severity {
Severity::Info => RecommendedAction::Log,
Severity::Low => RecommendedAction::Log,
Severity::Medium => RecommendedAction::Alert,
Severity::High => RecommendedAction::Block,
Severity::Critical => RecommendedAction::Terminate,
}
}
fn get_timestamp() -> u64 {
0
}