1use crate::binxml::ir_json::render_json_record;
2use crate::binxml::ir_xml::render_xml_record;
3use crate::err::{DeserializationError, DeserializationResult, EvtxError, Result};
4use crate::model::ir::IrTree;
5use crate::utils::ByteCursor;
6use crate::utils::bytes;
7use crate::utils::windows::filetime_to_timestamp;
8use crate::{EvtxChunk, ParserSettings};
9
10pub use jiff::Timestamp;
11#[allow(unused)]
12pub use jiff::tz::Offset;
13use std::io::Cursor;
14use std::sync::Arc;
15
16pub type RecordId = u64;
17
18pub(crate) const EVTX_RECORD_HEADER_SIZE: usize = 24;
19
20#[derive(Debug, Clone)]
21pub struct EvtxRecord<'a> {
22 pub chunk: &'a EvtxChunk<'a>,
23 pub event_record_id: RecordId,
24 pub timestamp: Timestamp,
25 pub tree: IrTree<'a>,
26 pub binxml_offset: u64,
27 pub binxml_size: u32,
28 pub settings: Arc<ParserSettings>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct EvtxRecordHeader {
33 pub data_size: u32,
34 pub event_record_id: RecordId,
35 pub timestamp: Timestamp,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct SerializedEvtxRecord<T> {
40 pub event_record_id: RecordId,
41 pub timestamp: Timestamp,
42 pub data: T,
43}
44
45impl EvtxRecordHeader {
46 pub fn from_bytes_at(buf: &[u8], offset: usize) -> DeserializationResult<EvtxRecordHeader> {
47 let _ = bytes::slice_r(buf, offset, EVTX_RECORD_HEADER_SIZE, "EVTX record header")?;
48
49 let magic = bytes::read_array_r::<4>(buf, offset, "record header magic")?;
50 if &magic != b"\x2a\x2a\x00\x00" {
51 return Err(DeserializationError::InvalidEvtxRecordHeaderMagic { magic });
52 }
53
54 let size = bytes::read_u32_le_r(buf, offset + 4, "record.data_size")?;
55 let record_id = bytes::read_u64_le_r(buf, offset + 8, "record.event_record_id")?;
56 let filetime = bytes::read_u64_le_r(buf, offset + 16, "record.filetime")?;
57
58 let timestamp = filetime_to_timestamp(filetime)?;
59
60 Ok(EvtxRecordHeader {
61 data_size: size,
62 event_record_id: record_id,
63 timestamp,
64 })
65 }
66
67 pub fn from_bytes(buf: &[u8]) -> DeserializationResult<EvtxRecordHeader> {
68 Self::from_bytes_at(buf, 0)
69 }
70
71 pub fn from_reader(input: &mut Cursor<&[u8]>) -> DeserializationResult<EvtxRecordHeader> {
72 let start = input.position() as usize;
73 let buf = input.get_ref();
74 let header = Self::from_bytes_at(buf, start)?;
75 input.set_position((start + EVTX_RECORD_HEADER_SIZE) as u64);
76 Ok(header)
77 }
78
79 pub fn record_data_size(&self) -> Result<u32> {
80 let decal = EVTX_RECORD_HEADER_SIZE as u32 + 4;
83 if self.data_size < decal {
84 return Err(EvtxError::InvalidDataSize {
85 length: self.data_size,
86 expected: decal,
87 });
88 }
89 Ok(self.data_size - decal)
90 }
91}
92
93impl<'a> EvtxRecord<'a> {
94 pub fn into_json_value(self) -> Result<SerializedEvtxRecord<serde_json::Value>> {
96 let event_record_id = self.event_record_id;
97 let timestamp = self.timestamp;
98 let record_with_json = self.into_json()?;
99
100 Ok(SerializedEvtxRecord {
101 event_record_id,
102 timestamp,
103 data: serde_json::from_str(&record_with_json.data)
104 .map_err(crate::err::SerializationError::from)?,
105 })
106 }
107
108 pub fn into_json(self) -> Result<SerializedEvtxRecord<String>> {
110 let capacity_hint = self.binxml_size as usize * 2;
112 let buf = Vec::with_capacity(capacity_hint);
113
114 let event_record_id = self.event_record_id;
115 let timestamp = self.timestamp;
116
117 let mut writer = buf;
118 render_json_record(&self.tree, &self.settings, &mut writer).map_err(|e| {
119 EvtxError::FailedToParseRecord {
120 record_id: event_record_id,
121 source: Box::new(e),
122 }
123 })?;
124 let data = String::from_utf8(writer).map_err(crate::err::SerializationError::from)?;
125
126 Ok(SerializedEvtxRecord {
127 event_record_id,
128 timestamp,
129 data,
130 })
131 }
132
133 pub fn into_xml(self) -> Result<SerializedEvtxRecord<String>> {
135 let capacity_hint = self.binxml_size as usize * 2;
136 let buf = Vec::with_capacity(capacity_hint);
137
138 let event_record_id = self.event_record_id;
139 let timestamp = self.timestamp;
140
141 let mut writer = buf;
142 render_xml_record(&self.tree, &self.settings, &mut writer).map_err(|e| {
143 EvtxError::FailedToParseRecord {
144 record_id: event_record_id,
145 source: Box::new(e),
146 }
147 })?;
148
149 let data = String::from_utf8(writer).map_err(crate::err::SerializationError::from)?;
150
151 Ok(SerializedEvtxRecord {
152 event_record_id,
153 timestamp,
154 data,
155 })
156 }
157
158 pub fn template_instances(&self) -> Result<Vec<crate::binxml::BinXmlTemplateValues<'a>>> {
163 use crate::binxml::name::BinXmlNameEncoding;
164 use crate::binxml::tokens::{
165 read_attribute_cursor, read_entity_ref_cursor, read_fragment_header_cursor,
166 read_open_start_element_cursor, read_processing_instruction_data_cursor,
167 read_processing_instruction_target_cursor, read_substitution_descriptor_cursor,
168 read_template_values_cursor,
169 };
170
171 let ansi_codec = self.settings.get_ansi_codec();
172 let mut out: Vec<crate::binxml::BinXmlTemplateValues<'a>> = Vec::new();
173
174 let mut cursor = ByteCursor::with_pos(self.chunk.data, self.binxml_offset as usize)?;
175 let mut data_read: u32 = 0;
176 let data_size = self.binxml_size;
177 let mut eof = false;
178
179 while !eof && data_read < data_size {
180 let start = cursor.position();
181 let token_byte = cursor.u8()?;
182
183 match token_byte {
184 0x00 => {
185 eof = true;
186 }
187 0x0c => {
188 let template = read_template_values_cursor(
189 &mut cursor,
190 Some(self.chunk),
191 ansi_codec,
192 &self.chunk.arena,
193 )?;
194 out.push(template);
195 }
196 0x01 => {
197 let _ = read_open_start_element_cursor(
198 &mut cursor,
199 false,
200 false,
201 BinXmlNameEncoding::Offset,
202 )?;
203 }
204 0x41 => {
205 let _ = read_open_start_element_cursor(
206 &mut cursor,
207 true,
208 false,
209 BinXmlNameEncoding::Offset,
210 )?;
211 }
212 0x02..=0x04 => {
213 }
215 0x05 | 0x45 => {
216 let _ = crate::binxml::value_variant::BinXmlValue::from_binxml_cursor_in(
217 &mut cursor,
218 Some(self.chunk),
219 None,
220 ansi_codec,
221 &self.chunk.arena,
222 )?;
223 }
224 0x06 | 0x46 => {
225 let _ = read_attribute_cursor(&mut cursor, BinXmlNameEncoding::Offset)?;
226 }
227 0x09 | 0x49 => {
228 let _ = read_entity_ref_cursor(&mut cursor, BinXmlNameEncoding::Offset)?;
229 }
230 0x0a => {
231 let _ = read_processing_instruction_target_cursor(
232 &mut cursor,
233 BinXmlNameEncoding::Offset,
234 )?;
235 }
236 0x0b => {
237 let _ = read_processing_instruction_data_cursor(&mut cursor)?;
238 }
239 0x0d => {
240 let _ = read_substitution_descriptor_cursor(&mut cursor, false)?;
241 }
242 0x0e => {
243 let _ = read_substitution_descriptor_cursor(&mut cursor, true)?;
244 }
245 0x0f => {
246 let _ = read_fragment_header_cursor(&mut cursor)?;
247 }
248 0x07 | 0x47 => {
249 return Err(DeserializationError::UnimplementedToken {
250 name: "CDataSection",
251 offset: cursor.position(),
252 }
253 .into());
254 }
255 0x08 | 0x48 => {
256 return Err(DeserializationError::UnimplementedToken {
257 name: "CharReference",
258 offset: cursor.position(),
259 }
260 .into());
261 }
262 _ => {
263 return Err(DeserializationError::InvalidToken {
264 value: token_byte,
265 offset: cursor.position(),
266 }
267 .into());
268 }
269 }
270
271 let total_read = cursor.position() - start;
272 data_read = data_read.saturating_add(total_read as u32);
273 }
274
275 Ok(out)
276 }
277}