1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::{convert::TryFrom, fmt::Display};

use anyhow::bail;
use darling::FromMeta;
use evtx::SerializedEvtxRecord;
use quote::quote;
use serde::Serialize;
use serde_json::Value;

use super::EvtxFieldView;

#[derive(PartialEq, Eq, Clone, Debug, Serialize)]
pub struct ProcessId(pub u64);

impl TryFrom<&SerializedEvtxRecord<Value>> for ProcessId {
    type Error = anyhow::Error;

    fn try_from(record: &SerializedEvtxRecord<Value>) -> Result<Self, Self::Error> {
        let process_id = &record.data["Event"]["System"]["Execution"]["#attributes"]["ProcessID"];

        let process_id = match process_id.get("#text") {
            Some(eid) => eid,
            None => process_id,
        };

        if let Some(process_id) = process_id.as_u64() {
            let id: u64 = process_id;
            Ok(Self(id))
        } else {
            bail!("event id cannot be converted to u64: {process_id}")
        }
    }
}

impl ProcessId {
    pub fn value(&self) -> u64 {
        self.0
    }
}

pub const PROCESS_ID_MAX_LENGTH: usize = 10;
impl EvtxFieldView for ProcessId {
    fn maximum_display_length(&self) -> usize {
        PROCESS_ID_MAX_LENGTH
    }

    fn value_with_padding(&self) -> String {
        format!("{:10}", self.0)
    }
}

impl From<ProcessId> for u64 {
    fn from(me: ProcessId) -> Self {
        me.0
    }
}

impl From<u64> for ProcessId {
    fn from(id: u64) -> Self {
        Self(id)
    }
}

impl Display for ProcessId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl FromMeta for ProcessId {
    fn from_value(value: &darling::export::syn::Lit) -> darling::Result<Self> {
        match value {
            darling::export::syn::Lit::Int(lit) => Ok(Self::from(lit.base10_parse::<u64>()?)),
            _ => Err(darling::Error::unknown_value("invalid process id")),
        }
    }
}

impl quote::ToTokens for ProcessId {
    fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
        let me = self.0;
        tokens.extend(quote!(
            {
                use dfirtk_eventdata::ProcessId;
                ProcessId(#me)
            }
        ))
    }
}