Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 62 additions & 15 deletions src/sensor/windows/etw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,25 @@ struct EtwProviders;
impl EtwProviders {
const KERNEL_PROCESS_GUID: &'static str = "22fb2cd6-0e7b-422b-a0c7-2fad1fd0e716";
const KERNEL_NETWORK_GUID: &'static str = "7dd42a49-5329-4832-8dfd-43d979153a88";
const KERNEL_FILE_GUID: &'static str = "edd08927-9cc4-4e65-b970-63462d3f77bd";
const KERNEL_FILE_GUID: &'static str = "edd08927-9cc4-4e65-b970-c2560fb5c289";
const KERNEL_REGISTRY_GUID: &'static str = "70eb4f03-c1de-4f73-a051-33d13d5413bd";
const DNS_CLIENT_GUID: &'static str = "1c95126e-7eea-49a9-a3fe-a378b03ddb4d";
const POWERSHELL_GUID: &'static str = "A0C1853B-5C40-4B15-8766-3CF1C58F985A";
const WMI_ACTIVITY_GUID: &'static str = "1418EF04-B0B4-4623-BF7E-D74AB47BBDAA";
const SERVICE_CONTROL_MANAGER_GUID: &'static str = "555908d1-a6d7-4695-8e1e-26931d2012f4";
const TASK_SCHEDULER_GUID: &'static str = "de7b24ea-73c8-4a09-985d-5bdadcfa9017";

// Keyword names follow the Microsoft-Windows-Kernel-File manifest.
const KERNEL_FILE_KEYWORD_FILENAME: u64 = 0x0010;
const KERNEL_FILE_KEYWORD_CREATE: u64 = 0x0080;
const KERNEL_FILE_KEYWORD_DELETE: u64 = 0x0200;
const KERNEL_FILE_KEYWORD_RENAME: u64 = 0x0400;
const KERNEL_FILE_KEYWORD_SETINFO: u64 = 0x0800;
const KERNEL_FILE_KEYWORD_WRITE: u64 = 0x0200;
const KERNEL_FILE_KEYWORD_DELETE_PATH: u64 = 0x0400;
const KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH: u64 = 0x0800;
const FILE_KEYWORDS: u64 = Self::KERNEL_FILE_KEYWORD_FILENAME
| Self::KERNEL_FILE_KEYWORD_CREATE
| Self::KERNEL_FILE_KEYWORD_DELETE
| Self::KERNEL_FILE_KEYWORD_RENAME
| Self::KERNEL_FILE_KEYWORD_SETINFO;
| Self::KERNEL_FILE_KEYWORD_WRITE
| Self::KERNEL_FILE_KEYWORD_DELETE_PATH
| Self::KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH;

const REG_KEYWORD_CREATE_KEY: u64 = 0x1000;
const REG_KEYWORD_SET_VALUE_KEY: u64 = 0x2000;
Expand Down Expand Up @@ -165,6 +166,34 @@ impl EtwProviders {
}
}

/// Microsoft-Windows-Kernel-File manifest event IDs.
const KERNEL_FILE_EVENT_CREATE: u16 = 12;
const KERNEL_FILE_EVENT_DELETE_PATH: u16 = 26;
const KERNEL_FILE_EVENT_RENAME_PATH: u16 = 27;
const KERNEL_FILE_EVENT_SET_LINK_PATH: u16 = 28;

/// The manifest provider emits file events with opcode 0, so routing must use
/// manifest event IDs rather than MOF FileIo opcodes.
fn kernel_file_action(event_id: u16) -> SensorAction {
match event_id {
KERNEL_FILE_EVENT_CREATE => SensorAction::Create,
KERNEL_FILE_EVENT_DELETE_PATH => SensorAction::Delete,
KERNEL_FILE_EVENT_RENAME_PATH => SensorAction::Rename,
_ => SensorAction::Modify,
}
}

/// DeletePath, RenamePath, and SetLinkPath carry their path in `FilePath`;
/// the remaining file events use `FileName`.
fn kernel_file_path_property(event_id: u16) -> &'static str {
match event_id {
KERNEL_FILE_EVENT_DELETE_PATH
| KERNEL_FILE_EVENT_RENAME_PATH
| KERNEL_FILE_EVENT_SET_LINK_PATH => "FilePath",
_ => "FileName",
}
}

struct EtwRouting {
kernel_process_guid: GUID,
guid_to_category: HashMap<GUID, EventCategory>,
Expand Down Expand Up @@ -216,12 +245,7 @@ impl EtwRouting {
id if id >= 15 => SensorAction::Connect,
_ => return None,
},
EventCategory::File => match record.opcode() {
64 | 65 => SensorAction::Create,
70 | 72 => SensorAction::Delete,
71 => SensorAction::Rename,
_ => SensorAction::Modify,
},
EventCategory::File => kernel_file_action(record.event_id()),
EventCategory::Registry => match record.opcode() {
36 => SensorAction::Create,
38 | 41 => SensorAction::Delete,
Expand Down Expand Up @@ -509,8 +533,14 @@ fn decode_file(parser: &Parser, record: &EventRecord) -> Option<DecodedEtwEvent>
let mappings = field_maps::file_event_mappings();
let fields = FileEventFields {
source_filename: None,
target_filename: try_get_string(parser, mappings.get_etw_field("TargetFilename")?)
.map(|path| convert_nt_to_dos(&path)),
target_filename: try_get_string_any(
parser,
&[
kernel_file_path_property(record.event_id()),
mappings.get_etw_field("TargetFilename")?,
],
)
.map(|path| convert_nt_to_dos(&path)),
process_id: try_get_uint(parser, mappings.get_etw_field("ProcessId")?),
image: try_get_string(parser, mappings.get_etw_field("Image")?)
.map(|path| convert_nt_to_dos(&path)),
Expand Down Expand Up @@ -825,6 +855,23 @@ fn parse_optional_u32(value: Option<&str>) -> Option<u32> {
mod tests {
use super::*;

#[test]
fn kernel_file_event_ids_route_to_actions() {
assert_eq!(kernel_file_action(12), SensorAction::Create);
assert_eq!(kernel_file_action(16), SensorAction::Modify);
assert_eq!(kernel_file_action(26), SensorAction::Delete);
assert_eq!(kernel_file_action(27), SensorAction::Rename);
}

#[test]
fn kernel_file_path_events_read_file_path() {
assert_eq!(kernel_file_path_property(12), "FileName");
assert_eq!(kernel_file_path_property(16), "FileName");
assert_eq!(kernel_file_path_property(26), "FilePath");
assert_eq!(kernel_file_path_property(27), "FilePath");
assert_eq!(kernel_file_path_property(28), "FilePath");
}

#[test]
fn provider_guids_are_unique() {
let providers = EtwProviders::all();
Expand Down
55 changes: 44 additions & 11 deletions src/sensor/windows/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,9 @@ fn action_code_for_record(
_ => 0,
},
},
EventCategory::File => match record.opcode() {
64 | 65 | 70 | 71 | 72 => record.opcode(),
_ => match action {
SensorAction::Create => 64,
SensorAction::Delete => 70,
SensorAction::Rename => 71,
SensorAction::Modify => 65,
_ => 0,
},
},
// The manifest-based Kernel-File provider emits events with opcode 0,
// so the action code comes from the routed action, not the opcode.
EventCategory::File => file_action_code(action),
EventCategory::Registry => match record.opcode() {
36 | 38 | 39 | 41 => record.opcode(),
_ => match action {
Expand All @@ -68,6 +61,19 @@ fn action_code_for_record(
}
}

/// Maps a routed file action to the platform-shared action code scheme
/// (64 = create, 65 = modify, 70 = delete, 71 = rename) used by the Linux
/// and macOS sensors as well.
fn file_action_code(action: SensorAction) -> u8 {
match action {
SensorAction::Create => 64,
SensorAction::Delete => 70,
SensorAction::Rename => 71,
SensorAction::Modify => 65,
_ => 0,
}
}

fn raw_event_id_for_record(category: EventCategory, action_code: u8, record: &EventRecord) -> u16 {
match category {
EventCategory::Process | EventCategory::ImageLoad => record.event_id(),
Expand Down Expand Up @@ -119,8 +125,9 @@ pub fn map_to_sysmon_id(category: EventCategory, action_code: u8, raw_event_id:

#[cfg(test)]
mod tests {
use super::map_to_sysmon_id;
use super::{file_action_code, map_to_sysmon_id};
use crate::models::EventCategory;
use crate::sensor::SensorAction;

#[test]
fn process_start_maps_to_sysmon_1() {
Expand All @@ -132,6 +139,32 @@ mod tests {
assert_eq!(map_to_sysmon_id(EventCategory::File, 64, 999), 11);
}

#[test]
fn file_actions_map_to_shared_action_codes() {
assert_eq!(file_action_code(SensorAction::Create), 64);
assert_eq!(file_action_code(SensorAction::Modify), 65);
assert_eq!(file_action_code(SensorAction::Delete), 70);
assert_eq!(file_action_code(SensorAction::Rename), 71);
}

#[test]
fn file_delete_action_maps_to_sysmon_23() {
let code = file_action_code(SensorAction::Delete);
assert_eq!(
map_to_sysmon_id(EventCategory::File, code, u16::from(code)),
23
);
}

#[test]
fn file_rename_action_keeps_event_id_71() {
let code = file_action_code(SensorAction::Rename);
assert_eq!(
map_to_sysmon_id(EventCategory::File, code, u16::from(code)),
71
);
}

#[test]
fn registry_setvalue_maps_to_sysmon_13() {
assert_eq!(map_to_sysmon_id(EventCategory::Registry, 39, 999), 13);
Expand Down
Loading