use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SysmonEventType {
ProcessCreate,
FileCreateTime,
NetworkConnect,
ServiceStateChange,
ProcessTerminate,
DriverLoad,
ImageLoad,
CreateRemoteThread,
RawAccessRead,
ProcessAccess,
FileCreate,
RegistryAddOrDelete,
RegistryValueSet,
RegistryRename,
FileCreateStreamHash,
ConfigChange,
PipeCreated,
PipeConnected,
WmiFilter,
WmiConsumer,
WmiBinding,
DnsQuery,
FileDelete,
ClipboardChange,
ProcessTampering,
FileDeleteDetected,
FileBlockExecutable,
FileBlockShredding,
FileExecutableDetected,
Error,
Unknown,
}
impl SysmonEventType {
pub fn from_event_id(id: u32) -> Self {
match id {
1 => Self::ProcessCreate,
2 => Self::FileCreateTime,
3 => Self::NetworkConnect,
4 => Self::ServiceStateChange,
5 => Self::ProcessTerminate,
6 => Self::DriverLoad,
7 => Self::ImageLoad,
8 => Self::CreateRemoteThread,
9 => Self::RawAccessRead,
10 => Self::ProcessAccess,
11 => Self::FileCreate,
12 => Self::RegistryAddOrDelete,
13 => Self::RegistryValueSet,
14 => Self::RegistryRename,
15 => Self::FileCreateStreamHash,
16 => Self::ConfigChange,
17 => Self::PipeCreated,
18 => Self::PipeConnected,
19 => Self::WmiFilter,
20 => Self::WmiConsumer,
21 => Self::WmiBinding,
22 => Self::DnsQuery,
23 => Self::FileDelete,
24 => Self::ClipboardChange,
25 => Self::ProcessTampering,
26 => Self::FileDeleteDetected,
27 => Self::FileBlockExecutable,
28 => Self::FileBlockShredding,
29 => Self::FileExecutableDetected,
255 => Self::Error,
_ => Self::Unknown,
}
}
pub fn display_name(&self) -> &'static str {
match self {
Self::ProcessCreate => "Process Create",
Self::FileCreateTime => "File Create Time",
Self::NetworkConnect => "Network Connect",
Self::ServiceStateChange => "Service State Change",
Self::ProcessTerminate => "Process Terminate",
Self::DriverLoad => "Driver Load",
Self::ImageLoad => "Image Load",
Self::CreateRemoteThread => "Create Remote Thread",
Self::RawAccessRead => "Raw Access Read",
Self::ProcessAccess => "Process Access",
Self::FileCreate => "File Create",
Self::RegistryAddOrDelete => "Registry Add/Delete",
Self::RegistryValueSet => "Registry Value Set",
Self::RegistryRename => "Registry Rename",
Self::FileCreateStreamHash => "File Stream Hash",
Self::ConfigChange => "Config Change",
Self::PipeCreated => "Pipe Created",
Self::PipeConnected => "Pipe Connected",
Self::WmiFilter => "WMI Filter",
Self::WmiConsumer => "WMI Consumer",
Self::WmiBinding => "WMI Binding",
Self::DnsQuery => "DNS Query",
Self::FileDelete => "File Delete (Archived)",
Self::ClipboardChange => "Clipboard Change",
Self::ProcessTampering => "Process Tampering",
Self::FileDeleteDetected => "File Delete Detected",
Self::FileBlockExecutable => "File Block Executable",
Self::FileBlockShredding => "File Block Shredding",
Self::FileExecutableDetected => "File Executable Detected",
Self::Error => "Sysmon Error",
Self::Unknown => "Unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SysmonSeverity {
Info,
Warning,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonEvent {
pub id: u64,
pub event_id: u32,
pub event_type: SysmonEventType,
pub event_type_display: String,
pub severity: SysmonSeverity,
pub timestamp: String,
pub timestamp_ms: Option<i64>,
pub computer: Option<String>,
pub record_id: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rule_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub utc_time: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub process_guid: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub process_id: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command_line: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hashes: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_command_line: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_process_id: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub protocol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_ip: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub destination_ip: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub destination_port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub destination_hostname: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_object: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub details: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub query_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub query_results: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub granted_access: Option<String>,
pub message: String,
pub source_file: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonEventTypeCount {
pub event_id: u32,
pub event_type: SysmonEventType,
pub display_name: String,
pub count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonSummary {
pub total_events: u64,
pub event_type_counts: Vec<SysmonEventTypeCount>,
pub unique_processes: u64,
pub unique_computers: u64,
pub earliest_timestamp: Option<String>,
pub latest_timestamp: Option<String>,
pub source_files: Vec<String>,
pub parse_errors: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonConfig {
pub schema_version: Option<String>,
pub hash_algorithms: Option<String>,
pub found: bool,
pub last_config_change: Option<String>,
pub configuration_xml: Option<String>,
pub sysmon_version: Option<String>,
pub active_event_types: Vec<SysmonEventTypeCount>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TimeBucket {
pub timestamp: String,
pub timestamp_ms: i64,
pub count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RankedItem {
pub name: String,
pub count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SecuritySummary {
pub total_warnings: u64,
pub total_errors: u64,
pub events_by_type: Vec<RankedItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonDashboardData {
pub timeline_minute: Vec<TimeBucket>,
pub timeline_hourly: Vec<TimeBucket>,
pub timeline_daily: Vec<TimeBucket>,
pub top_processes: Vec<RankedItem>,
pub top_destinations: Vec<RankedItem>,
pub top_ports: Vec<RankedItem>,
pub top_dns_queries: Vec<RankedItem>,
pub security_events: SecuritySummary,
pub top_target_files: Vec<RankedItem>,
pub top_registry_keys: Vec<RankedItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SysmonAnalysisResult {
pub events: Vec<SysmonEvent>,
pub summary: SysmonSummary,
pub config: SysmonConfig,
pub dashboard: SysmonDashboardData,
pub source_path: String,
}