use alloc::vec;
use alloc::vec::Vec;
use super::{DetectionContext, EventType, Severity, MitreTactic};
#[derive(Debug, Clone)]
pub struct DetectionRule {
pub id: u32,
pub name: [u8; 64],
pub severity: Severity,
pub tactic: MitreTactic,
pub technique_id: [u8; 8],
pub enabled: bool,
pub rule_type: RuleType,
pub conditions: Vec<RuleCondition>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RuleType {
ProcessCreation,
ThreadCreation,
ProcessAccess,
ImageLoad,
FileOp,
RegistryMod,
Network,
MemoryMod,
Composite,
}
#[derive(Debug, Clone)]
pub struct RuleCondition {
pub field: ConditionField,
pub operator: ConditionOperator,
pub value: ConditionValue,
}
#[derive(Debug, Clone, Copy)]
pub enum ConditionField {
ProcessName,
ParentProcessName,
ImagePath,
CommandLine,
TargetProcessName,
RegistryPath,
RegistryValue,
FilePath,
FileExtension,
RemoteAddress,
RemotePort,
IsElevated,
IsSystem,
}
#[derive(Debug, Clone, Copy)]
pub enum ConditionOperator {
Equals,
NotEquals,
Contains,
StartsWith,
EndsWith,
Matches, GreaterThan,
LessThan,
In, }
#[derive(Debug, Clone)]
pub enum ConditionValue {
String([u8; 256]),
StringList(Vec<[u8; 256]>),
Number(u64),
Boolean(bool),
}
impl DetectionRule {
pub fn matches_event_type(&self, event_type: EventType) -> bool {
match self.rule_type {
RuleType::ProcessCreation => matches!(event_type, EventType::ProcessCreate),
RuleType::ThreadCreation => matches!(event_type, EventType::ThreadCreate),
RuleType::ProcessAccess => matches!(event_type, EventType::HandleOp),
RuleType::ImageLoad => matches!(event_type, EventType::ImageLoad),
RuleType::FileOp => matches!(event_type, EventType::FileOp),
RuleType::RegistryMod => matches!(event_type, EventType::RegistryOp),
RuleType::Network => matches!(event_type, EventType::NetworkOp),
RuleType::MemoryMod => matches!(event_type, EventType::MemoryOp),
RuleType::Composite => true, }
}
pub fn evaluate_conditions(&self, context: &DetectionContext) -> bool {
if self.conditions.is_empty() {
return false;
}
for condition in &self.conditions {
if !evaluate_condition(condition, context) {
return false;
}
}
true
}
}
fn evaluate_condition(condition: &RuleCondition, context: &DetectionContext) -> bool {
match condition.field {
ConditionField::ProcessName => {
match_string(&context.process_name, &condition.operator, &condition.value)
}
ConditionField::IsElevated => {
if let ConditionValue::Boolean(expected) = condition.value {
context.is_elevated == expected
} else {
false
}
}
ConditionField::IsSystem => {
if let ConditionValue::Boolean(expected) = condition.value {
context.is_system == expected
} else {
false
}
}
_ => {
false
}
}
}
fn match_string(value: &[u8], operator: &ConditionOperator, expected: &ConditionValue) -> bool {
let ConditionValue::String(expected_bytes) = expected else {
return false;
};
let value_len = value.iter().position(|&b| b == 0).unwrap_or(value.len());
let expected_len = expected_bytes.iter().position(|&b| b == 0).unwrap_or(expected_bytes.len());
let value_slice = &value[..value_len];
let expected_slice = &expected_bytes[..expected_len];
match operator {
ConditionOperator::Equals => value_slice == expected_slice,
ConditionOperator::NotEquals => value_slice != expected_slice,
ConditionOperator::Contains => {
if expected_len > value_len {
return false;
}
for i in 0..=(value_len - expected_len) {
if &value_slice[i..i + expected_len] == expected_slice {
return true;
}
}
false
}
ConditionOperator::StartsWith => {
value_len >= expected_len && &value_slice[..expected_len] == expected_slice
}
ConditionOperator::EndsWith => {
value_len >= expected_len && &value_slice[value_len - expected_len..] == expected_slice
}
_ => false,
}
}
pub mod rulesets {
use super::*;
pub fn credential_theft_rules() -> Vec<DetectionRule> {
vec![
DetectionRule {
id: 100,
name: *b"LSASS_Memory_Access\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.01",
enabled: true,
rule_type: RuleType::ProcessAccess,
conditions: vec![],
},
DetectionRule {
id: 101,
name: *b"SAM_Registry_Access\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::CredentialAccess,
technique_id: *b"T1003.02",
enabled: true,
rule_type: RuleType::RegistryMod,
conditions: vec![],
},
]
}
pub fn injection_rules() -> Vec<DetectionRule> {
vec![
DetectionRule {
id: 200,
name: *b"CreateRemoteThread_Injection\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.01",
enabled: true,
rule_type: RuleType::ThreadCreation,
conditions: vec![],
},
DetectionRule {
id: 201,
name: *b"APC_Injection\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"T1055.04",
enabled: true,
rule_type: RuleType::MemoryMod,
conditions: vec![],
},
DetectionRule {
id: 202,
name: *b"Process_Hollowing\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::DefenseEvasion,
technique_id: *b"T1055.12",
enabled: true,
rule_type: RuleType::MemoryMod,
conditions: vec![],
},
]
}
pub fn persistence_rules() -> Vec<DetectionRule> {
vec![
DetectionRule {
id: 300,
name: *b"Registry_Run_Key_Modification\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.01",
enabled: true,
rule_type: RuleType::RegistryMod,
conditions: vec![],
},
DetectionRule {
id: 301,
name: *b"Scheduled_Task_Creation\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"T1053.05",
enabled: true,
rule_type: RuleType::ProcessCreation,
conditions: vec![],
},
DetectionRule {
id: 302,
name: *b"Service_Creation\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"T1543.03",
enabled: true,
rule_type: RuleType::RegistryMod,
conditions: vec![],
},
]
}
pub fn defense_evasion_rules() -> Vec<DetectionRule> {
vec![
DetectionRule {
id: 400,
name: *b"AMSI_Bypass_Attempt\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.01",
enabled: true,
rule_type: RuleType::MemoryMod,
conditions: vec![],
},
DetectionRule {
id: 401,
name: *b"ETW_Patching\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.01",
enabled: true,
rule_type: RuleType::MemoryMod,
conditions: vec![],
},
]
}
pub fn ransomware_rules() -> Vec<DetectionRule> {
vec![
DetectionRule {
id: 500,
name: *b"High_Entropy_File_Write\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::Impact,
technique_id: *b"T1486 ",
enabled: true,
rule_type: RuleType::FileOp,
conditions: vec![],
},
DetectionRule {
id: 501,
name: *b"Mass_File_Rename\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::Impact,
technique_id: *b"T1486 ",
enabled: true,
rule_type: RuleType::FileOp,
conditions: vec![],
},
DetectionRule {
id: 502,
name: *b"Shadow_Copy_Deletion\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"T1490 ",
enabled: true,
rule_type: RuleType::ProcessCreation,
conditions: vec![],
},
]
}
}