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