sysmon/
lib.rs

1extern crate anyhow;
2extern crate chrono;
3#[macro_use]
4extern crate derive_is_enum_variant;
5extern crate serde;
6#[macro_use]
7extern crate serde_derive;
8extern crate serde_xml_rs;
9extern crate uuid;
10
11use std::collections::HashMap;
12use std::convert::TryFrom;
13
14use anyhow::{anyhow, Result};
15use chrono::prelude::*;
16use failure::_core::ops::Deref;
17use serde::{Deserialize, Deserializer};
18use serde::de::Error as SerdeError;
19
20macro_rules! get_or_err {
21    ($map:ident, $field_name:expr) => {
22
23        match $map.remove($field_name) {
24            Some(field) => field,
25            None => return Err(anyhow!("No field: {}", $field_name))
26        }
27
28    };
29
30    ($map:ident, $field_name:expr, $maperr:expr) => {
31
32        match $map.remove($field_name) {
33            Some(field) => field,
34            None => return Err(anyhow!("No field: {}", $field_name)).map_err($maperr)
35        }
36
37    };
38}
39
40#[derive(Debug, Clone, Hash)]
41#[derive(is_enum_variant)]
42pub enum Event {
43    ProcessCreate(ProcessCreateEvent),
44    FileCreate(FileCreateEvent),
45    InboundNetwork(NetworkEvent),
46    OutboundNetwork(NetworkEvent),
47}
48
49impl Event {
50    pub fn from_str(s: impl AsRef<str>) -> Result<Self> {
51        let s = s.as_ref();
52        serde_xml_rs::from_str::<ProcessCreateEvent>(s)
53            .map(|p| Event::ProcessCreate(p))
54            .or_else(|_|
55                serde_xml_rs::from_str::<FileCreateEvent>(s)
56                    .map(|f| Event::FileCreate(f))
57            )
58            .or_else(|_|
59                serde_xml_rs::from_str::<NetworkEvent>(s)
60                    .map(|n| {
61                        if n.event_data.initiated {
62                           Event::OutboundNetwork(n)
63
64                        } else {
65                           Event::InboundNetwork(n)
66                        }
67                    })
68            )
69            .map_err(|e| anyhow!("Error : {:?} {}", e, s))
70    }
71}
72
73
74#[derive(Debug, Deserialize, Clone, Hash)]
75pub struct Provider {
76    #[serde(rename = "Name")]
77    pub provider_name: String,
78    #[serde(rename = "Guid")]
79    pub provider_guid: String,
80}
81
82
83#[derive(Debug, Deserialize, Clone, Hash)]
84pub struct EventId {
85    #[serde(rename = "$value")]
86    pub event_id: u8,
87}
88
89#[derive(Debug, Deserialize, Clone, Hash)]
90pub struct Level {
91    #[serde(rename = "$value")]
92    pub level: String,
93}
94
95
96#[derive(Debug, Deserialize, Clone, Hash)]
97pub struct Task {
98    #[serde(rename = "$value")]
99    pub task: String,
100}
101
102
103#[derive(Debug, Deserialize, Clone, Hash)]
104pub struct Version {
105    #[serde(rename = "$value")]
106    pub version: String,
107}
108
109
110#[derive(Debug, Deserialize, Clone, Hash)]
111pub struct Opcode {
112    #[serde(rename = "$value")]
113    pub opcode: String,
114}
115
116#[derive(Debug, Deserialize, Clone, Hash)]
117pub struct Keywords {
118    #[serde(rename = "$value")]
119    pub keywords: String,
120}
121
122#[derive(Debug, Deserialize, Clone, Hash)]
123pub struct TimeCreated {
124    #[serde(rename = "SystemTime")]
125    pub system_time: String,
126}
127
128#[derive(Debug, Deserialize, Clone, Hash)]
129pub struct EventRecordId {
130    #[serde(rename = "$value")]
131    pub event_record_id: u32,
132}
133
134#[derive(Debug, Deserialize, Clone, Hash)]
135pub struct Execution {
136    #[serde(rename = "ProcessID")]
137    pub process_id: String,
138    #[serde(rename = "ThreadID")]
139    pub thread_id: String,
140}
141
142#[derive(Debug, Deserialize, Clone, Hash)]
143pub struct Channel {
144    #[serde(rename = "$value")]
145    pub value: String,
146}
147
148#[derive(Debug, Deserialize, Clone, Hash)]
149pub struct Computer {
150    #[serde(rename = "$value")]
151    pub computer: String,
152}
153
154#[derive(Debug, Deserialize, Clone, Hash)]
155pub struct Security {
156    #[serde(rename = "UserID")]
157    pub security: String,
158}
159
160#[derive(Debug, Deserialize, Clone, Hash)]
161pub struct System {
162    /// <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385F-C22A-43E0-BF4C-06F5698FFBD9}" />
163    #[serde(rename = "Provider")]
164    pub provider: Provider,
165    /// <EventID>1</EventID>
166    #[serde(rename = "EventID")]
167    pub event_id: EventId,
168    /// <Version>5</Version>
169    #[serde(rename = "Version")]
170    pub version: Version,
171    /// <Level>4</Level>
172    #[serde(rename = "Level")]
173    pub level: Level,
174    /// <Task>1</Task>
175    #[serde(rename = "Task")]
176    pub task: Task,
177    /// <Opcode>0</Opcode>
178    #[serde(rename = "Opcode")]
179    pub opcode: Opcode,
180    /// <Keywords>0x8000000000000000</Keywords>
181    #[serde(rename = "Keywords")]
182    pub keywords: Keywords,
183    /// <TimeCreated SystemTime="2017-04-28T22:08:22.025812200Z" />
184    #[serde(rename = "TimeCreated")]
185    pub time_created: TimeCreated,
186    /// <EventRecordID>9947</EventRecordID>
187    #[serde(rename = "EventRecordID")]
188    pub event_record_id: EventRecordId,
189    /// <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
190    /// <Execution ProcessID="3216" ThreadID="3964" />
191    #[serde(rename = "Execution")]
192    pub execution: Execution,
193    /// <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
194    #[serde(rename = "Channel")]
195    pub channel: Channel,
196    /// <Computer>rfsH.lab.local</Computer>
197    #[serde(rename = "Computer")]
198    pub computer: Computer,
199    /// <Security UserID="S-1-5-18" />
200    #[serde(rename = "Security")]
201    pub security: Security,
202}
203
204#[derive(Debug, Deserialize, Clone, Hash)]
205pub struct UtcTime {
206    #[serde(rename = "$value")]
207    pub utc_time: String
208}
209
210impl Deref for UtcTime {
211    type Target = str;
212
213    fn deref(&self) -> &str {
214        &self.utc_time
215    }
216}
217
218
219#[derive(Debug, Deserialize, Clone, Hash)]
220pub struct ProcessGuid {
221    pub process_guid: uuid::Uuid,
222}
223
224impl ProcessGuid {
225    pub fn get_creation_timestamp(&self) -> u64 {
226        let guid = self.process_guid.as_bytes();
227
228        const OFF: usize = 4;
229
230        // big endian :|
231        let b = [guid[OFF + 1], guid[OFF], guid[OFF + 3], guid[OFF + 2]];
232
233        let ts = i32::from_le_bytes(b);
234        Utc.timestamp(ts as i64, 0).timestamp() as u64
235
236    }
237}
238
239#[derive(Debug, Deserialize, Clone, Hash)]
240pub struct Image {
241    pub image: String,
242}
243
244impl Deref for Image {
245    type Target = str;
246
247    fn deref(&self) -> &str {
248        &self.image
249    }
250}
251
252#[derive(Debug, Deserialize, Clone, Hash)]
253pub struct CommandLine {
254    pub command_line: String,
255}
256
257impl Deref for CommandLine {
258    type Target = str;
259
260    fn deref(&self) -> &str {
261        &self.command_line
262    }
263}
264
265#[derive(Debug, Deserialize, Clone, Hash)]
266pub struct CurrentDirectory {
267    pub current_directory: String,
268}
269
270impl Deref for CurrentDirectory {
271    type Target = str;
272
273    fn deref(&self) -> &str {
274        &self.current_directory
275    }
276}
277
278#[derive(Debug, Deserialize, Clone, Hash)]
279pub struct User {
280    pub user: String,
281}
282
283impl Deref for User {
284    type Target = str;
285
286    fn deref(&self) -> &str {
287        &self.user
288    }
289}
290
291#[derive(Debug, Deserialize, Clone, Hash)]
292pub struct LogonGuid {
293    pub logon_guid: uuid::Uuid,
294}
295
296impl Deref for LogonGuid {
297    type Target = uuid::Uuid;
298
299    fn deref(&self) -> &Self::Target {
300        &self.logon_guid
301    }
302}
303
304#[derive(Debug, Deserialize, Clone, Hash)]
305pub struct LogonId {
306    pub logon_id: String,
307}
308
309impl Deref for LogonId {
310    type Target = str;
311
312    fn deref(&self) -> &str {
313        &self.logon_id
314    }
315}
316
317#[derive(Debug, Deserialize, Clone, Hash)]
318pub struct TerminalSessionId {
319    pub terminal_session_id: String,
320}
321
322impl Deref for TerminalSessionId {
323    type Target = str;
324
325    fn deref(&self) -> &str {
326        &self.terminal_session_id
327    }
328}
329
330#[derive(Debug, Deserialize, Clone, Hash)]
331pub struct IntegrityLevel {
332    pub integrity_level: String,
333}
334
335impl Deref for IntegrityLevel {
336    type Target = str;
337
338    fn deref(&self) -> &str {
339        &self.integrity_level
340    }
341}
342
343#[derive(Debug, Deserialize, Clone, Hash)]
344pub struct Hashes {
345    pub hashes: String,
346}
347
348impl Deref for Hashes {
349    type Target = str;
350
351    fn deref(&self) -> &str {
352        &self.hashes
353    }
354}
355
356
357#[derive(Debug, Deserialize, Clone, Hash)]
358pub struct TargetFilename {
359    pub target_filename: String,
360}
361
362impl Deref for TargetFilename {
363    type Target = str;
364
365    fn deref(&self) -> &str {
366        &self.target_filename
367    }
368}
369
370
371#[derive(Debug, Deserialize, Clone, Hash)]
372pub struct ProcessCreateEventData {
373    /// <Data Name="UtcTime">2017-04-28 22:08:22.025</Data>
374    pub utc_time: UtcTime,
375    /// <Data Name="ProcessGuid">{A23EAE89-BD56-5903-0000-0010E9D95E00}</Data>
376    pub process_guid: ProcessGuid,
377    /// <Data Name="ProcessId">6228</Data>
378    pub process_id: u64,
379    /// <Data Name="Image">C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Data>
380    pub image: Image,
381    /// <Data Name="CommandLine">"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type=utility --lang=en-US --no-sandbox --service-request-channel-token=F47498BBA884E523FA93E623C4569B94 --mojo-platform-channel-handle=3432 /prefetch:8</Data>
382    pub command_line: CommandLine,
383    /// <Data Name="CurrentDirectory">C:\Program Files (x86)\Google\Chrome\Application\58.0.3029.81\</Data>
384    pub current_directory: CurrentDirectory,
385    /// <Data Name="User">LAB\rsmith</Data>
386    pub user: User,
387    /// <Data Name="LogonGuid">{A23EAE89-B357-5903-0000-002005EB0700}</Data>
388    pub logon_guid: LogonGuid,
389    /// <Data Name="LogonId">0x7eb05</Data>
390    pub logon_id: LogonId,
391    /// <Data Name="TerminalSessionId">1</Data>
392    pub terminal_session_id: TerminalSessionId,
393    /// <Data Name="IntegrityLevel">Medium</Data>
394    pub integrity_level: IntegrityLevel,
395    /// <Data Name="Hashes">SHA256=6055A20CF7EC81843310AD37700FF67B2CF8CDE3DCE68D54BA42934177C10B57</Data>
396    pub hashes: Hashes,
397    /// <Data Name="ParentProcessGuid">{A23EAE89-BD28-5903-0000-00102F345D00}</Data>
398    pub parent_process_guid: ProcessGuid,
399    /// <Data Name="ParentProcessId">13220</Data>
400    pub parent_process_id: u64,
401    /// <Data Name="ParentImage">C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Data>
402    pub parent_image: Image,
403    /// <Data Name="ParentCommandLine">"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" </Data>
404    pub parent_command_line: CommandLine,
405}
406
407#[derive(Debug, Deserialize, Clone, Hash)]
408pub struct ProcessCreateEvent {
409    #[serde(rename = "System")]
410    pub system: System,
411    #[serde(rename = "EventData", deserialize_with="from_intermediary_data")]
412    pub event_data: ProcessCreateEventData,
413}
414
415
416
417
418#[derive(Debug, Deserialize, Clone, Hash)]
419pub struct FileCreateEventData {
420    pub utc_time: UtcTime,
421    pub process_guid: ProcessGuid,
422    pub process_id: u64,
423    pub image: Image,
424    pub target_filename: String,
425    pub creation_utc_time: UtcTime,
426}
427
428#[derive(Debug, Deserialize, Clone, Hash)]
429pub struct FileCreateEvent {
430    #[serde(rename = "System")]
431    pub system: System,
432
433    #[serde(rename = "EventData", deserialize_with="from_intermediary_data")]
434    pub event_data: FileCreateEventData
435}
436
437
438#[derive(Debug, Deserialize, Clone, Hash)]
439pub struct NetworkEventData {
440    pub utc_time: UtcTime,
441    pub process_guid: ProcessGuid,
442    pub process_id: u64,
443    pub image: Image,
444    pub user: Option<User>,
445    pub protocol: String,
446    pub initiated: bool,
447    pub source_is_ipv6: String,
448    pub source_ip: String,
449    pub source_hostname: Option<String>,
450    pub source_port: u16,
451    pub source_port_name: Option<String>,
452    pub destination_is_ipv6: String,
453    pub destination_ip: String,
454    pub destination_hostname: Option<String>,
455    pub destination_port: u16,
456    pub destination_port_name: Option<String>,
457}
458
459#[derive(Debug, Deserialize, Clone, Hash)]
460pub struct NetworkEvent {
461    #[serde(rename = "System")]
462    pub system: System,
463    #[serde(rename = "EventData", deserialize_with="from_intermediary_data")]
464    pub event_data: NetworkEventData,
465}
466
467impl TryFrom<IntermediaryEventData> for ProcessCreateEventData {
468    type Error = anyhow::Error;
469
470    fn try_from(inter: IntermediaryEventData) -> Result<Self> {
471        let mut m = HashMap::with_capacity(inter.data.len());
472
473        for data in inter.data {
474            if let Some(value) = data.value {
475//                if (value == "4") {
476//                    panic!("{} {}", data.name, value.len());
477//                }
478                m.insert(data.name, value);
479            }
480        }
481
482        let process_id = get_or_err!(m, "ProcessId");
483        let process_id: u64 = process_id.parse()?;
484
485        let parent_process_id = get_or_err!(m, "ParentProcessId");
486        let parent_process_id: u64 = parent_process_id.parse()?;
487
488
489        Ok(
490            ProcessCreateEventData {
491                utc_time: UtcTime {utc_time: get_or_err!(m, "UtcTime") },
492                process_guid: ProcessGuid {
493                    process_guid: uuid::Uuid::parse_str(&get_or_err!(m, "ProcessGuid")[1..37])?
494                },
495                process_id,
496                image: Image { image: get_or_err!(m, "Image") },
497                command_line: CommandLine { command_line: get_or_err!(m, "CommandLine") },
498                current_directory: CurrentDirectory { current_directory: get_or_err!(m, "CurrentDirectory") },
499                user: User { user: get_or_err!(m, "User") },
500                logon_guid: LogonGuid {
501                    logon_guid: uuid::Uuid::parse_str(&get_or_err!(m, "LogonGuid")[1..37])?
502                },
503                logon_id: LogonId { logon_id: get_or_err!(m, "LogonId") },
504                terminal_session_id: TerminalSessionId { terminal_session_id: get_or_err!(m, "TerminalSessionId") },
505                integrity_level: IntegrityLevel { integrity_level: get_or_err!(m, "IntegrityLevel") },
506                hashes: Hashes { hashes: get_or_err!(m, "Hashes") },
507                parent_process_guid: ProcessGuid {
508                    process_guid:  uuid::Uuid::parse_str(&get_or_err!(m, "ParentProcessGuid")[1..37])?
509                },
510                parent_process_id,
511                parent_image: Image { image: get_or_err!(m, "ParentImage") },
512                parent_command_line: CommandLine { command_line: get_or_err!(m, "ParentCommandLine") },
513            }
514        )
515    }
516}
517
518impl TryFrom<IntermediaryEventData> for FileCreateEventData {
519    type Error = anyhow::Error;
520
521    fn try_from(inter: IntermediaryEventData) -> Result<Self> {
522        let mut m = HashMap::with_capacity(inter.data.len());
523
524        for data in inter.data {
525            if let Some(value) = data.value {
526                m.insert(data.name, value);
527            }
528        }
529
530        let process_id = get_or_err!(m, "ProcessId");
531        let process_id = process_id.parse()?;
532
533        Ok(
534            FileCreateEventData {
535                utc_time: UtcTime { utc_time: get_or_err!(m, "UtcTime") },
536                process_guid: ProcessGuid {
537                    process_guid:  uuid::Uuid::parse_str(&get_or_err!(m, "ProcessGuid")[1..37])?
538                },
539                process_id,
540                image: Image { image: get_or_err!(m, "Image") },
541                creation_utc_time: UtcTime { utc_time: get_or_err!(m, "CreationUtcTime") },
542                target_filename: get_or_err!(m, "TargetFilename"),
543            }
544        )
545    }
546}
547
548impl TryFrom<IntermediaryEventData> for NetworkEventData {
549    type Error = anyhow::Error;
550
551    fn try_from(inter: IntermediaryEventData) -> Result<Self> {
552        let mut m = HashMap::with_capacity(inter.data.len());
553
554        for data in inter.data {
555            if let Some(value) = data.value {
556                m.insert(data.name, value);
557            }
558        }
559
560        let user = m.remove("User")
561            .map(|user| User { user });
562
563        Ok(
564            NetworkEventData {
565                utc_time: UtcTime {utc_time: get_or_err!(m, "UtcTime")},
566                process_guid: ProcessGuid {
567                    process_guid:  uuid::Uuid::parse_str(&get_or_err!(m, "ProcessGuid")[1..37])?
568                },
569                process_id: get_or_err!(m, "ProcessId").parse()?,
570                image: Image { image: get_or_err!(m, "Image") },
571                user,
572                protocol: get_or_err!(m, "Protocol"),
573                source_is_ipv6: get_or_err!(m, "SourceIsIpv6"),
574                source_ip: get_or_err!(m, "SourceIp"),
575                source_hostname: m.remove("SourceHostname"),
576                source_port_name: m.remove("SourcePortName"),
577                destination_is_ipv6: get_or_err!(m, "DestinationIsIpv6"),
578                destination_ip: get_or_err!(m, "DestinationIp"),
579                destination_hostname: m.remove("DestinationHostname"),
580                destination_port_name: m.remove("DestinationPortName"),
581                initiated: get_or_err!(m, "Initiated").parse()?,
582                source_port: get_or_err!(m, "SourcePort").parse()?,
583                destination_port: get_or_err!(m, "DestinationPort").parse()?,
584            }
585        )
586    }
587}
588
589fn from_intermediary_data<'de, D, T>(deserializer: D) -> Result<T, D::Error>
590    where
591        D: Deserializer<'de>,
592        T: TryFrom<IntermediaryEventData>,
593{
594    let s: IntermediaryEventData = Deserialize::deserialize(deserializer)?;
595    T::try_from(s).map_err(|_| SerdeError::custom("Failed to deserialize") )
596}
597
598#[derive(Debug, Deserialize, Clone, Hash)]
599pub struct Data {
600    #[serde(rename = "Name")]
601    pub name: String,
602    #[serde(rename = "$value")]
603    pub value: Option<String>,
604
605}
606
607
608#[derive(Debug, Deserialize, Clone, Hash)]
609pub struct IntermediaryEventData {
610    #[serde(rename = "Data")]
611    pub data: Vec<Data>
612}
613
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618    use std::fs::File;
619    use std::io::{BufReader, BufRead};
620
621    const NETWORK_EVENT: &str = r#"
622    <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
623        <System>
624            <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385F-C22A-43E0-BF4C-06F5698FFBD9}" />
625            <EventID>3</EventID>
626            <Version>5</Version>
627            <Level>4</Level>
628            <Task>3</Task>
629            <Opcode>0</Opcode>
630            <Keywords>0x8000000000000000</Keywords>
631            <TimeCreated SystemTime="2017-04-28T22:12:23.657698300Z" />
632            <EventRecordID>10953</EventRecordID>
633            <Correlation />
634            <Execution ProcessID="3216" ThreadID="3976" />
635            <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
636            <Computer>rfsH.lab.local</Computer>
637            <Security UserID="S-1-5-18" />
638        </System>
639        <EventData>
640            <Data Name="UtcTime">2017-04-28 22:12:22.557</Data>
641            <Data Name="ProcessGuid">{A23EAE89-BD28-5903-0000-00102F345D00}</Data>
642            <Data Name="ProcessId">13220</Data>
643            <Data Name="Image">C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Data>
644            <Data Name="User">LAB\rsmith</Data>
645            <Data Name="Protocol">tcp</Data>
646            <Data Name="Initiated">true</Data>
647            <Data Name="SourceIsIpv6">false</Data>
648            <Data Name="SourceIp">192.168.1.250</Data>
649            <Data Name="SourceHostname">rfsH.lab.local</Data>
650            <Data Name="SourcePort">3328</Data>
651            <Data Name="SourcePortName"></Data>
652            <Data Name="DestinationIsIpv6">false</Data>
653            <Data Name="DestinationIp">104.130.229.150</Data>
654            <Data Name="DestinationHostname"></Data>
655            <Data Name="DestinationPort">443</Data>
656            <Data Name="DestinationPortName">https</Data>
657        </EventData>
658    </Event>
659    "#;
660
661    const FILE_CREATE: &str = r#"
662        <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
663        <System>
664            <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385F-C22A-43E0-BF4C-06F5698FFBD9}" />
665            <EventID>11</EventID>
666            <Version>2</Version>
667            <Level>4</Level>
668            <Task>11</Task>
669            <Opcode>0</Opcode>
670            <Keywords>0x8000000000000000</Keywords>
671            <TimeCreated SystemTime="2017-05-13T19:44:55.314125100Z" />
672            <EventRecordID>734181</EventRecordID>
673            <Correlation />
674            <Execution ProcessID="2848" ThreadID="3520" />
675            <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
676            <Computer>rfsH.lab.local</Computer>
677            <Security UserID="S-1-5-18" />
678        </System>
679        <EventData>
680            <Data Name="UtcTime">2017-05-13 19:44:55.313</Data>
681            <Data Name="ProcessGuid">{A23EAE89-6237-5917-0000-0010300E6601}</Data>
682            <Data Name="ProcessId">19200</Data>
683            <Data Name="Image">C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorsvw.exe</Data>
684            <Data Name="TargetFilename">C:\Windows\assembly\NativeImages_v4.0.30319_64\Temp\4b00-0\AxImp.exe</Data>
685            <Data Name="CreationUtcTime">2017-05-13 19:44:55.313</Data>
686        </EventData>
687        </Event>
688    "#;
689
690    const PROCESS_CREATE: &str = r#"
691    <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
692        <System>
693            <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385F-C22A-43E0-BF4C-06F5698FFBD9}" />
694            <EventID>1</EventID>
695            <Version>5</Version>
696            <Level>4</Level>
697            <Task>1</Task>
698            <Opcode>0</Opcode>
699            <Keywords>0x8000000000000000</Keywords>
700            <TimeCreated SystemTime="2017-04-28T22:08:22.025812200Z" />
701            <EventRecordID>9947</EventRecordID>
702            <Correlation />
703            <Execution ProcessID="3216" ThreadID="3964" />
704            <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
705            <Computer>rfsH.lab.local</Computer>
706            <Security UserID="S-1-5-18" />
707        </System>
708        <EventData>
709            <Data Name="UtcTime">2017-04-28 22:08:22.025</Data>
710            <Data Name="ProcessGuid">{A23EAE89-BD56-5903-0000-0010E9D95E00}</Data>
711            <Data Name="ProcessId">6228</Data>
712            <Data Name="Image">C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Data>
713            <Data Name="CommandLine">"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type=utility --lang=en-US --no-sandbox --service-request-channel-token=F47498BBA884E523FA93E623C4569B94 --mojo-platform-channel-handle=3432 /prefetch:8</Data>
714            <Data Name="CurrentDirectory">C:\Program Files (x86)\Google\Chrome\Application\58.0.3029.81\</Data>
715            <Data Name="User">LAB\rsmith</Data>
716            <Data Name="LogonGuid">{A23EAE89-B357-5903-0000-002005EB0700}</Data>
717            <Data Name="LogonId">0x7eb05</Data>
718            <Data Name="TerminalSessionId">1</Data>
719            <Data Name="IntegrityLevel">Medium</Data>
720            <Data Name="Hashes">SHA256=6055A20CF7EC81843310AD37700FF67B2CF8CDE3DCE68D54BA42934177C10B57</Data>
721            <Data Name="ParentProcessGuid">{A23EAE89-BD28-5903-0000-00102F345D00}</Data>
722            <Data Name="ParentProcessId">13220</Data>
723            <Data Name="ParentImage">C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Data>
724            <Data Name="ParentCommandLine">"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" </Data>
725        </EventData>
726    </Event>
727    "#;
728
729    const HEADER: &'static str = r#"
730        <System>
731            <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385F-C22A-43E0-BF4C-06F5698FFBD9}" />
732            <EventID>1</EventID>
733            <Version>5</Version>
734            <Level>4</Level>
735            <Task>1</Task>
736            <Opcode>0</Opcode>
737            <Keywords>0x8000000000000000</Keywords>
738            <TimeCreated SystemTime="2017-04-28T22:08:22.025812200Z" />
739            <EventRecordID>9947</EventRecordID>
740            <Correlation />
741            <Execution ProcessID="3216" ThreadID="3964" />
742            <Channel>Microsoft-Windows-Sysmon/Operational</Channel>
743            <Computer>rfsH.lab.local</Computer>
744            <Security UserID="S-1-5-18" />
745        </System>
746    "#;
747
748    #[test]
749    fn system() {
750        serde_xml_rs::from_str::<System>(HEADER).unwrap();
751    }
752
753    #[test]
754    fn process_create_event() {
755        serde_xml_rs::from_str::<ProcessCreateEvent>(PROCESS_CREATE).unwrap();
756    }
757
758    #[test]
759    fn file_create_event() {
760        serde_xml_rs::from_str::<FileCreateEvent>(FILE_CREATE).unwrap();
761    }
762
763    #[test]
764    fn network_event() {
765        serde_xml_rs::from_str::<NetworkEvent>(NETWORK_EVENT).unwrap();
766    }
767
768    #[test]
769    fn event_type() {
770        assert!(Event::from_str(NETWORK_EVENT).unwrap().is_outbound_network());
771        assert!(Event::from_str(FILE_CREATE).unwrap().is_file_create());
772        assert!(Event::from_str(PROCESS_CREATE).unwrap().is_process_create());
773    }
774
775    #[test]
776    fn parse_all() -> Result<()> {
777        let reader = BufReader::new(File::open("./test_data/events6.xml")?);
778
779        for event in reader.lines() {
780            if let Ok(event) = event {
781                if event.contains("EventID>1<") || event.contains("EventID>3<")  || event.contains("EventID>11<") {
782                    let parsed: Result<Event, _> = Event::from_str(&event);
783                    if let Err(e) = parsed {
784                        Err(e)?;
785                    }
786                }
787            }
788        }
789
790        Ok(())
791    }
792}
793
794