Skip to main content

evtx/binxml/
value_variant.rs

1use crate::err::{DeserializationError, DeserializationResult as Result};
2use crate::evtx_chunk::EvtxChunk;
3use crate::utils::invalid_data;
4use crate::utils::windows::{filetime_to_timestamp, read_systime, systime_from_bytes};
5use crate::utils::{ByteCursor, Utf16LeSlice};
6
7use bumpalo::Bump;
8use encoding::EncodingRef;
9use jiff::Timestamp;
10use log::{trace, warn};
11use std::fmt::{self, Display};
12use std::io::Cursor;
13use std::string::ToString;
14use winstructs::guid::Guid;
15
16/// Borrowed SID bytes (used to avoid heap allocation in the hot path).
17#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct SidRef<'a> {
19    bytes: &'a [u8],
20}
21
22impl<'a> SidRef<'a> {
23    pub fn new(bytes: &'a [u8]) -> Self {
24        Self { bytes }
25    }
26
27    pub fn as_bytes(&self) -> &'a [u8] {
28        self.bytes
29    }
30}
31
32impl fmt::Display for SidRef<'_> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        let bytes = self.bytes;
35        if bytes.len() < 8 {
36            return write!(f, "S-?");
37        }
38        let revision = bytes[0];
39        let sub_count = bytes[1] as usize;
40
41        // IdentifierAuthority is a 48-bit big-endian integer.
42        let mut authority: u64 = 0;
43        for &b in &bytes[2..8] {
44            authority = (authority << 8) | u64::from(b);
45        }
46
47        write!(f, "S-{}-{}", revision, authority)?;
48
49        let mut off = 8usize;
50        for _ in 0..sub_count {
51            if off + 4 > bytes.len() {
52                break;
53            }
54            let sub =
55                u32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]]);
56            write!(f, "-{}", sub)?;
57            off += 4;
58        }
59        Ok(())
60    }
61}
62
63#[derive(Debug, PartialOrd, PartialEq, Clone)]
64pub enum BinXmlValue<'a> {
65    NullType,
66    /// UTF-16LE string slice (BinXML `StringType`).
67    StringType(Utf16LeSlice<'a>),
68    /// ANSI string decoded to UTF-8, stored as a borrowed slice (typically bump-allocated).
69    AnsiStringType(&'a str),
70    Int8Type(i8),
71    UInt8Type(u8),
72    Int16Type(i16),
73    UInt16Type(u16),
74    Int32Type(i32),
75    UInt32Type(u32),
76    Int64Type(i64),
77    UInt64Type(u64),
78    Real32Type(f32),
79    Real64Type(f64),
80    BoolType(bool),
81    BinaryType(&'a [u8]),
82    GuidType(Guid),
83    SizeTType(usize),
84    FileTimeType(Timestamp),
85    SysTimeType(Timestamp),
86    SidType(SidRef<'a>),
87    HexInt32Type(u32),
88    HexInt64Type(u64),
89    EvtHandle,
90    /// Raw BinXML fragment bytes (no length prefix).
91    ///
92    /// This is stored as a slice into the chunk data and parsed on demand by higher-level code.
93    BinXmlType(&'a [u8]),
94    EvtXml,
95    /// Array of UTF-16LE strings (null-terminated items).
96    StringArrayType(&'a [Utf16LeSlice<'a>]),
97    AnsiStringArrayType,
98    Int8ArrayType(&'a [i8]),
99    UInt8ArrayType(&'a [u8]),
100    Int16ArrayType(&'a [i16]),
101    UInt16ArrayType(&'a [u16]),
102    Int32ArrayType(&'a [i32]),
103    UInt32ArrayType(&'a [u32]),
104    Int64ArrayType(&'a [i64]),
105    UInt64ArrayType(&'a [u64]),
106    Real32ArrayType(&'a [f32]),
107    Real64ArrayType(&'a [f64]),
108    BoolArrayType(&'a [bool]),
109    BinaryArrayType,
110    GuidArrayType(&'a [Guid]),
111    SizeTArrayType,
112    FileTimeArrayType(&'a [Timestamp]),
113    SysTimeArrayType(&'a [Timestamp]),
114    SidArrayType(&'a [SidRef<'a>]),
115    HexInt32ArrayType(&'a [u32]),
116    HexInt64ArrayType(&'a [u64]),
117    EvtArrayHandle,
118    BinXmlArrayType,
119    EvtXmlArrayType,
120}
121
122#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy)]
123pub enum BinXmlValueType {
124    NullType,
125    StringType,
126    AnsiStringType,
127    Int8Type,
128    UInt8Type,
129    Int16Type,
130    UInt16Type,
131    Int32Type,
132    UInt32Type,
133    Int64Type,
134    UInt64Type,
135    Real32Type,
136    Real64Type,
137    BoolType,
138    BinaryType,
139    GuidType,
140    SizeTType,
141    FileTimeType,
142    SysTimeType,
143    SidType,
144    HexInt32Type,
145    HexInt64Type,
146    EvtHandle,
147    BinXmlType,
148    EvtXmlType,
149    StringArrayType,
150    AnsiStringArrayType,
151    Int8ArrayType,
152    UInt8ArrayType,
153    Int16ArrayType,
154    UInt16ArrayType,
155    Int32ArrayType,
156    UInt32ArrayType,
157    Int64ArrayType,
158    UInt64ArrayType,
159    Real32ArrayType,
160    Real64ArrayType,
161    BoolArrayType,
162    BinaryArrayType,
163    GuidArrayType,
164    SizeTArrayType,
165    FileTimeArrayType,
166    SysTimeArrayType,
167    SidArrayType,
168    HexInt32ArrayType,
169    HexInt64ArrayType,
170    EvtHandleArray,
171    BinXmlArrayType,
172    EvtXmlArrayType,
173}
174
175impl BinXmlValueType {
176    /// Lookup table for O(1) byte-to-type conversion without indirect branches.
177    const LOOKUP: [Option<BinXmlValueType>; 256] = {
178        use BinXmlValueType::*;
179        let mut t: [Option<BinXmlValueType>; 256] = [None; 256];
180        t[0x00] = Some(NullType);
181        t[0x01] = Some(StringType);
182        t[0x02] = Some(AnsiStringType);
183        t[0x03] = Some(Int8Type);
184        t[0x04] = Some(UInt8Type);
185        t[0x05] = Some(Int16Type);
186        t[0x06] = Some(UInt16Type);
187        t[0x07] = Some(Int32Type);
188        t[0x08] = Some(UInt32Type);
189        t[0x09] = Some(Int64Type);
190        t[0x0a] = Some(UInt64Type);
191        t[0x0b] = Some(Real32Type);
192        t[0x0c] = Some(Real64Type);
193        t[0x0d] = Some(BoolType);
194        t[0x0e] = Some(BinaryType);
195        t[0x0f] = Some(GuidType);
196        t[0x10] = Some(SizeTType);
197        t[0x11] = Some(FileTimeType);
198        t[0x12] = Some(SysTimeType);
199        t[0x13] = Some(SidType);
200        t[0x14] = Some(HexInt32Type);
201        t[0x15] = Some(HexInt64Type);
202        t[0x20] = Some(EvtHandle);
203        t[0x21] = Some(BinXmlType);
204        t[0x23] = Some(EvtXmlType);
205        t[0x81] = Some(StringArrayType);
206        t[0x82] = Some(AnsiStringArrayType);
207        t[0x83] = Some(Int8ArrayType);
208        t[0x84] = Some(UInt8ArrayType);
209        t[0x85] = Some(Int16ArrayType);
210        t[0x86] = Some(UInt16ArrayType);
211        t[0x87] = Some(Int32ArrayType);
212        t[0x88] = Some(UInt32ArrayType);
213        t[0x89] = Some(Int64ArrayType);
214        t[0x8a] = Some(UInt64ArrayType);
215        t[0x8b] = Some(Real32ArrayType);
216        t[0x8c] = Some(Real64ArrayType);
217        t[0x8d] = Some(BoolArrayType);
218        t[0x8e] = Some(BinaryArrayType);
219        t[0x8f] = Some(GuidArrayType);
220        t[0x90] = Some(SizeTArrayType);
221        t[0x91] = Some(FileTimeArrayType);
222        t[0x92] = Some(SysTimeArrayType);
223        t[0x93] = Some(SidArrayType);
224        t[0x94] = Some(HexInt32ArrayType);
225        t[0x95] = Some(HexInt64ArrayType);
226        t
227    };
228
229    #[inline]
230    pub fn from_u8(byte: u8) -> Option<BinXmlValueType> {
231        Self::LOOKUP[byte as usize]
232    }
233}
234
235impl<'a> BinXmlValue<'a> {
236    pub(crate) fn from_binxml_cursor_in(
237        cursor: &mut ByteCursor<'a>,
238        chunk: Option<&'a EvtxChunk<'a>>,
239        size: Option<u16>,
240        ansi_codec: EncodingRef,
241        arena: &'a Bump,
242    ) -> Result<BinXmlValue<'a>> {
243        let value_type_token = cursor.u8()?;
244
245        let value_type = BinXmlValueType::from_u8(value_type_token).ok_or_else(|| {
246            DeserializationError::InvalidValueVariant {
247                value: value_type_token,
248                offset: cursor.position(),
249            }
250        })?;
251
252        let data = Self::deserialize_value_type_cursor_in(
253            &value_type,
254            cursor,
255            chunk,
256            size,
257            ansi_codec,
258            arena,
259        )?;
260
261        Ok(data)
262    }
263
264    pub fn from_binxml_stream_in(
265        cursor: &mut Cursor<&'a [u8]>,
266        chunk: Option<&'a EvtxChunk<'a>>,
267        size: Option<u16>,
268        ansi_codec: EncodingRef,
269        arena: &'a Bump,
270    ) -> Result<BinXmlValue<'a>> {
271        let start = cursor.position() as usize;
272        let buf = *cursor.get_ref();
273        let mut c = ByteCursor::with_pos(buf, start)?;
274        let v = Self::from_binxml_cursor_in(&mut c, chunk, size, ansi_codec, arena)?;
275        cursor.set_position(c.position());
276        Ok(v)
277    }
278
279    pub(crate) fn deserialize_value_type_cursor(
280        value_type: &BinXmlValueType,
281        cursor: &mut ByteCursor<'a>,
282        chunk: Option<&'a EvtxChunk<'a>>,
283        size: Option<u16>,
284        ansi_codec: EncodingRef,
285        arena: &'a Bump,
286    ) -> Result<BinXmlValue<'a>> {
287        Self::deserialize_value_type_cursor_in(value_type, cursor, chunk, size, ansi_codec, arena)
288    }
289
290    pub(crate) fn deserialize_value_type_cursor_in(
291        value_type: &BinXmlValueType,
292        cursor: &mut ByteCursor<'a>,
293        chunk: Option<&'a EvtxChunk<'a>>,
294        size: Option<u16>,
295        ansi_codec: EncodingRef,
296        arena: &'a Bump,
297    ) -> Result<BinXmlValue<'a>> {
298        let _ = chunk;
299        trace!(
300            "Offset `0x{offset:08x} ({offset}): {value_type:?}, {size:?}",
301            offset = cursor.position(),
302            value_type = value_type,
303            size = size
304        );
305
306        let value = match (value_type, size) {
307            (BinXmlValueType::NullType, _) => BinXmlValue::NullType,
308
309            (BinXmlValueType::StringType, Some(sz)) => {
310                let sz_bytes = usize::from(sz);
311                let s = if sz_bytes == 0 {
312                    None
313                } else if !sz_bytes.is_multiple_of(2) {
314                    return Err(invalid_data("sized utf-16 string", cursor.position()));
315                } else {
316                    cursor.utf16_by_char_count(sz_bytes / 2, "<string_value>")?
317                };
318                BinXmlValue::StringType(s.unwrap_or_else(Utf16LeSlice::empty))
319            }
320            (BinXmlValueType::StringType, None) => {
321                let s = cursor.len_prefixed_utf16_string(false, "<string_value>")?;
322                BinXmlValue::StringType(s.unwrap_or_else(Utf16LeSlice::empty))
323            }
324
325            (BinXmlValueType::AnsiStringType, Some(sz)) => {
326                let sz_bytes = usize::from(sz);
327                let raw = cursor.take_bytes(sz_bytes, "<ansi_string_value>")?;
328                // Filter embedded NUL bytes (historical behavior).
329                let mut filtered = bumpalo::collections::Vec::with_capacity_in(sz_bytes, arena);
330                for &b in raw {
331                    if b != 0 {
332                        filtered.push(b);
333                    }
334                }
335                let filtered = filtered.into_bump_slice();
336                let decoded = ansi_codec
337                    .decode(filtered, encoding::DecoderTrap::Strict)
338                    .map_err(|m| DeserializationError::AnsiDecodeError {
339                        encoding_used: ansi_codec.name(),
340                        inner_message: m.to_string(),
341                    })?;
342                BinXmlValue::AnsiStringType(arena.alloc_str(&decoded))
343            }
344            // AnsiString are always sized according to docs
345            (BinXmlValueType::AnsiStringType, None) => {
346                return Err(DeserializationError::UnimplementedValueVariant {
347                    name: "AnsiString".to_owned(),
348                    size: None,
349                    offset: cursor.position(),
350                });
351            }
352
353            (BinXmlValueType::Int8Type, _) => BinXmlValue::Int8Type(cursor.u8()? as i8),
354            (BinXmlValueType::UInt8Type, _) => BinXmlValue::UInt8Type(cursor.u8()?),
355
356            (BinXmlValueType::Int16Type, _) => {
357                BinXmlValue::Int16Type(i16::from_le_bytes(cursor.array::<2>("i16")?))
358            }
359            (BinXmlValueType::UInt16Type, _) => BinXmlValue::UInt16Type(cursor.u16()?),
360
361            (BinXmlValueType::Int32Type, _) => {
362                BinXmlValue::Int32Type(i32::from_le_bytes(cursor.array::<4>("i32")?))
363            }
364            (BinXmlValueType::UInt32Type, _) => BinXmlValue::UInt32Type(cursor.u32()?),
365
366            (BinXmlValueType::Int64Type, _) => {
367                BinXmlValue::Int64Type(i64::from_le_bytes(cursor.array::<8>("i64")?))
368            }
369            (BinXmlValueType::UInt64Type, _) => BinXmlValue::UInt64Type(cursor.u64()?),
370
371            (BinXmlValueType::Real32Type, _) => {
372                BinXmlValue::Real32Type(f32::from_le_bytes(cursor.array::<4>("f32")?))
373            }
374            (BinXmlValueType::Real64Type, _) => {
375                BinXmlValue::Real64Type(f64::from_le_bytes(cursor.array::<8>("f64")?))
376            }
377
378            (BinXmlValueType::BoolType, _) => {
379                let raw = i32::from_le_bytes(cursor.array::<4>("bool")?);
380                let v = match raw {
381                    0 => false,
382                    1 => true,
383                    other => {
384                        warn!(
385                            "invalid boolean value {} at offset {}; treating as {}",
386                            other,
387                            cursor.position(),
388                            other != 0
389                        );
390                        other != 0
391                    }
392                };
393                BinXmlValue::BoolType(v)
394            }
395
396            (BinXmlValueType::GuidType, _) => {
397                let bytes = cursor.take_bytes(16, "guid")?;
398                let guid = Guid::from_buffer(bytes)
399                    .map_err(|_| invalid_data("guid", cursor.position()))?;
400                BinXmlValue::GuidType(guid)
401            }
402
403            (BinXmlValueType::SizeTType, Some(4)) => {
404                let v = u32::from_le_bytes(cursor.array::<4>("sizet32")?);
405                BinXmlValue::HexInt32Type(v)
406            }
407            (BinXmlValueType::SizeTType, Some(8)) => {
408                let v = u64::from_le_bytes(cursor.array::<8>("sizet64")?);
409                BinXmlValue::HexInt64Type(v)
410            }
411            (BinXmlValueType::SizeTType, _) => {
412                return Err(DeserializationError::UnimplementedValueVariant {
413                    name: "SizeT".to_owned(),
414                    size,
415                    offset: cursor.position(),
416                });
417            }
418
419            (BinXmlValueType::FileTimeType, _) => {
420                BinXmlValue::FileTimeType(filetime_to_timestamp(cursor.u64()?)?)
421            }
422            (BinXmlValueType::SysTimeType, _) => BinXmlValue::SysTimeType(read_systime(cursor)?),
423            (BinXmlValueType::SidType, _) => BinXmlValue::SidType(cursor.read_sid_ref()?),
424
425            (BinXmlValueType::HexInt32Type, _) => {
426                let v = u32::from_le_bytes(cursor.array::<4>("hex32")?);
427                BinXmlValue::HexInt32Type(v)
428            }
429            (BinXmlValueType::HexInt64Type, _) => {
430                let v = u64::from_le_bytes(cursor.array::<8>("hex64")?);
431                BinXmlValue::HexInt64Type(v)
432            }
433
434            (BinXmlValueType::BinXmlType, size) => {
435                let payload = match size {
436                    Some(sz) => {
437                        if sz == 0 {
438                            &[]
439                        } else {
440                            cursor.take_bytes(usize::from(sz), "binxml_payload")?
441                        }
442                    }
443                    None => {
444                        let payload_len = cursor.u16_named("binxml_payload_len")? as usize;
445                        if payload_len == 0 {
446                            &[]
447                        } else {
448                            cursor.take_bytes(payload_len, "binxml_payload")?
449                        }
450                    }
451                };
452                BinXmlValue::BinXmlType(payload)
453            }
454
455            (BinXmlValueType::BinaryType, Some(sz)) => {
456                let bytes = cursor.take_bytes(usize::from(sz), "binary")?;
457                BinXmlValue::BinaryType(bytes)
458            }
459
460            // The array types are always sized.
461            (BinXmlValueType::StringArrayType, Some(sz)) => {
462                let size_usize = usize::from(sz);
463                if size_usize == 0 {
464                    return Ok(BinXmlValue::StringArrayType(&[]));
465                }
466                let start = cursor.pos();
467                let end = start.saturating_add(size_usize);
468                let mut out = bumpalo::collections::Vec::new_in(arena);
469                while cursor.pos() < end {
470                    let s = cursor.null_terminated_utf16_string("string_array")?;
471                    out.push(s);
472                }
473                BinXmlValue::StringArrayType(out.into_bump_slice())
474            }
475            (BinXmlValueType::Int8ArrayType, Some(sz)) => {
476                let bytes = cursor.take_bytes(usize::from(sz), "i8_array")?;
477                let out = arena.alloc_slice_fill_iter(bytes.iter().map(|&b| b as i8));
478                BinXmlValue::Int8ArrayType(out)
479            }
480            (BinXmlValueType::UInt8ArrayType, Some(sz)) => {
481                let bytes = cursor.take_bytes(usize::from(sz), "u8_array")?;
482                BinXmlValue::UInt8ArrayType(bytes)
483            }
484            (BinXmlValueType::Int16ArrayType, Some(sz)) => {
485                let out = cursor.read_sized_slice_aligned_in::<2, _>(
486                    sz,
487                    "i16_array",
488                    arena,
489                    |_off, b| Ok(i16::from_le_bytes(*b)),
490                )?;
491                BinXmlValue::Int16ArrayType(out)
492            }
493            (BinXmlValueType::UInt16ArrayType, Some(sz)) => {
494                let out = cursor.read_sized_slice_aligned_in::<2, _>(
495                    sz,
496                    "u16_array",
497                    arena,
498                    |_off, b| Ok(u16::from_le_bytes(*b)),
499                )?;
500                BinXmlValue::UInt16ArrayType(out)
501            }
502            (BinXmlValueType::Int32ArrayType, Some(sz)) => {
503                let out = cursor.read_sized_slice_aligned_in::<4, _>(
504                    sz,
505                    "i32_array",
506                    arena,
507                    |_off, b| Ok(i32::from_le_bytes(*b)),
508                )?;
509                BinXmlValue::Int32ArrayType(out)
510            }
511            (BinXmlValueType::UInt32ArrayType, Some(sz)) => {
512                let out = cursor.read_sized_slice_aligned_in::<4, _>(
513                    sz,
514                    "u32_array",
515                    arena,
516                    |_off, b| Ok(u32::from_le_bytes(*b)),
517                )?;
518                BinXmlValue::UInt32ArrayType(out)
519            }
520            (BinXmlValueType::Int64ArrayType, Some(sz)) => {
521                let out = cursor.read_sized_slice_aligned_in::<8, _>(
522                    sz,
523                    "i64_array",
524                    arena,
525                    |_off, b| Ok(i64::from_le_bytes(*b)),
526                )?;
527                BinXmlValue::Int64ArrayType(out)
528            }
529            (BinXmlValueType::UInt64ArrayType, Some(sz)) => {
530                let out = cursor.read_sized_slice_aligned_in::<8, _>(
531                    sz,
532                    "u64_array",
533                    arena,
534                    |_off, b| Ok(u64::from_le_bytes(*b)),
535                )?;
536                BinXmlValue::UInt64ArrayType(out)
537            }
538            (BinXmlValueType::Real32ArrayType, Some(sz)) => {
539                let out = cursor.read_sized_slice_aligned_in::<4, _>(
540                    sz,
541                    "f32_array",
542                    arena,
543                    |_off, b| Ok(f32::from_le_bytes(*b)),
544                )?;
545                BinXmlValue::Real32ArrayType(out)
546            }
547            (BinXmlValueType::Real64ArrayType, Some(sz)) => {
548                let out = cursor.read_sized_slice_aligned_in::<8, _>(
549                    sz,
550                    "f64_array",
551                    arena,
552                    |_off, b| Ok(f64::from_le_bytes(*b)),
553                )?;
554                BinXmlValue::Real64ArrayType(out)
555            }
556            (BinXmlValueType::BoolArrayType, Some(sz)) => {
557                let out = cursor.read_sized_slice_aligned_in::<4, _>(
558                    sz,
559                    "bool_array",
560                    arena,
561                    |off, b| {
562                        let raw = i32::from_le_bytes(*b);
563                        Ok(match raw {
564                            0 => false,
565                            1 => true,
566                            other => {
567                                warn!(
568                                    "invalid boolean value {} at offset {}; treating as {}",
569                                    other,
570                                    off,
571                                    other != 0
572                                );
573                                other != 0
574                            }
575                        })
576                    },
577                )?;
578                BinXmlValue::BoolArrayType(out)
579            }
580            (BinXmlValueType::GuidArrayType, Some(sz)) => {
581                let out = cursor.read_sized_slice_aligned_in::<16, _>(
582                    sz,
583                    "guid_array",
584                    arena,
585                    |off, b| Guid::from_buffer(b).map_err(|_| invalid_data("guid", off)),
586                )?;
587                BinXmlValue::GuidArrayType(out)
588            }
589            (BinXmlValueType::FileTimeArrayType, Some(sz)) => {
590                let out = cursor.read_sized_slice_aligned_in::<8, _>(
591                    sz,
592                    "filetime_array",
593                    arena,
594                    |_off, b| filetime_to_timestamp(u64::from_le_bytes(*b)),
595                )?;
596                BinXmlValue::FileTimeArrayType(out)
597            }
598            (BinXmlValueType::SysTimeArrayType, Some(sz)) => {
599                let out = cursor.read_sized_slice_aligned_in::<16, _>(
600                    sz,
601                    "systime_array",
602                    arena,
603                    |_off, b| systime_from_bytes(b),
604                )?;
605                BinXmlValue::SysTimeArrayType(out)
606            }
607            (BinXmlValueType::SidArrayType, Some(sz)) => {
608                let size_usize = usize::from(sz);
609                if size_usize == 0 {
610                    return Ok(BinXmlValue::SidArrayType(&[]));
611                }
612                let start_pos = cursor.pos();
613                let mut out = bumpalo::collections::Vec::with_capacity_in(size_usize / 8, arena);
614                while (cursor.pos() - start_pos) < size_usize {
615                    out.push(cursor.read_sid_ref()?);
616                }
617                BinXmlValue::SidArrayType(out.into_bump_slice())
618            }
619            (BinXmlValueType::HexInt32ArrayType, Some(sz)) => {
620                let out = cursor.read_sized_slice_aligned_in::<4, _>(
621                    sz,
622                    "hex32_array",
623                    arena,
624                    |_off, b| Ok(u32::from_le_bytes(*b)),
625                )?;
626                BinXmlValue::HexInt32ArrayType(out)
627            }
628            (BinXmlValueType::HexInt64ArrayType, Some(sz)) => {
629                let out = cursor.read_sized_slice_aligned_in::<8, _>(
630                    sz,
631                    "hex64_array",
632                    arena,
633                    |_off, b| Ok(u64::from_le_bytes(*b)),
634                )?;
635                BinXmlValue::HexInt64ArrayType(out)
636            }
637
638            _ => {
639                return Err(DeserializationError::UnimplementedValueVariant {
640                    name: format!("{:?}", value_type),
641                    size,
642                    offset: cursor.position(),
643                });
644            }
645        };
646
647        Ok(value)
648    }
649
650    pub fn deserialize_value_type_in(
651        value_type: &BinXmlValueType,
652        cursor: &mut Cursor<&'a [u8]>,
653        chunk: Option<&'a EvtxChunk<'a>>,
654        size: Option<u16>,
655        ansi_codec: EncodingRef,
656        arena: &'a Bump,
657    ) -> Result<BinXmlValue<'a>> {
658        let start = cursor.position() as usize;
659        let buf = *cursor.get_ref();
660        let mut c = ByteCursor::with_pos(buf, start)?;
661        let v = Self::deserialize_value_type_cursor(
662            value_type, &mut c, chunk, size, ansi_codec, arena,
663        )?;
664        cursor.set_position(c.position());
665        Ok(v)
666    }
667}
668
669impl<'a> BinXmlValue<'a> {
670    /// Return `Some(len)` when this value is one of the array types we support for
671    /// MS-EVEN6 ยง3.1.4.7.5 "array substitution" expansion.
672    #[inline]
673    pub(crate) fn expandable_array_len(&self) -> Option<usize> {
674        match self {
675            // Strings are stored as UTF-16LE slices in EVTX.
676            BinXmlValue::StringArrayType(v) => Some(v.len()),
677
678            // Numeric/bool GUID/time/SID arrays.
679            BinXmlValue::Int8ArrayType(v) => Some(v.len()),
680            BinXmlValue::UInt8ArrayType(v) => Some(v.len()),
681            BinXmlValue::Int16ArrayType(v) => Some(v.len()),
682            BinXmlValue::UInt16ArrayType(v) => Some(v.len()),
683            BinXmlValue::Int32ArrayType(v) => Some(v.len()),
684            BinXmlValue::UInt32ArrayType(v) => Some(v.len()),
685            BinXmlValue::Int64ArrayType(v) => Some(v.len()),
686            BinXmlValue::UInt64ArrayType(v) => Some(v.len()),
687            BinXmlValue::Real32ArrayType(v) => Some(v.len()),
688            BinXmlValue::Real64ArrayType(v) => Some(v.len()),
689            BinXmlValue::BoolArrayType(v) => Some(v.len()),
690            BinXmlValue::GuidArrayType(v) => Some(v.len()),
691            BinXmlValue::FileTimeArrayType(v) => Some(v.len()),
692            BinXmlValue::SysTimeArrayType(v) => Some(v.len()),
693            BinXmlValue::SidArrayType(v) => Some(v.len()),
694            BinXmlValue::HexInt32ArrayType(v) => Some(v.len()),
695            BinXmlValue::HexInt64ArrayType(v) => Some(v.len()),
696
697            // Not currently expanded:
698            // - `AnsiStringArrayType`, `BinaryArrayType`, `SizeTArrayType`, and the handle/binxml
699            //   array-ish variants don't have a typed slice representation here.
700            _ => None,
701        }
702    }
703
704    /// For an expandable array value, return the scalar `BinXmlValue` for item `idx`.
705    #[inline]
706    pub(crate) fn array_item_as_value(&self, idx: usize) -> Option<BinXmlValue<'a>> {
707        match self {
708            BinXmlValue::StringArrayType(items) => {
709                items.get(idx).copied().map(BinXmlValue::StringType)
710            }
711            BinXmlValue::Int8ArrayType(items) => items.get(idx).copied().map(BinXmlValue::Int8Type),
712            BinXmlValue::UInt8ArrayType(items) => {
713                items.get(idx).copied().map(BinXmlValue::UInt8Type)
714            }
715            BinXmlValue::Int16ArrayType(items) => {
716                items.get(idx).copied().map(BinXmlValue::Int16Type)
717            }
718            BinXmlValue::UInt16ArrayType(items) => {
719                items.get(idx).copied().map(BinXmlValue::UInt16Type)
720            }
721            BinXmlValue::Int32ArrayType(items) => {
722                items.get(idx).copied().map(BinXmlValue::Int32Type)
723            }
724            BinXmlValue::UInt32ArrayType(items) => {
725                items.get(idx).copied().map(BinXmlValue::UInt32Type)
726            }
727            BinXmlValue::Int64ArrayType(items) => {
728                items.get(idx).copied().map(BinXmlValue::Int64Type)
729            }
730            BinXmlValue::UInt64ArrayType(items) => {
731                items.get(idx).copied().map(BinXmlValue::UInt64Type)
732            }
733            BinXmlValue::Real32ArrayType(items) => {
734                items.get(idx).copied().map(BinXmlValue::Real32Type)
735            }
736            BinXmlValue::Real64ArrayType(items) => {
737                items.get(idx).copied().map(BinXmlValue::Real64Type)
738            }
739            BinXmlValue::BoolArrayType(items) => items.get(idx).copied().map(BinXmlValue::BoolType),
740            // `Guid` is not `Copy` in this codebase.
741            BinXmlValue::GuidArrayType(items) => items.get(idx).cloned().map(BinXmlValue::GuidType),
742            BinXmlValue::FileTimeArrayType(items) => {
743                items.get(idx).copied().map(BinXmlValue::FileTimeType)
744            }
745            BinXmlValue::SysTimeArrayType(items) => {
746                items.get(idx).copied().map(BinXmlValue::SysTimeType)
747            }
748            BinXmlValue::SidArrayType(items) => items.get(idx).copied().map(BinXmlValue::SidType),
749            BinXmlValue::HexInt32ArrayType(items) => {
750                items.get(idx).copied().map(BinXmlValue::HexInt32Type)
751            }
752            BinXmlValue::HexInt64ArrayType(items) => {
753                items.get(idx).copied().map(BinXmlValue::HexInt64Type)
754            }
755            _ => None,
756        }
757    }
758}
759
760impl<'a> Display for BinXmlValue<'a> {
761    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
762        let mut vr = crate::binxml::value_render::ValueRenderer::default();
763        let mut writer = Vec::new();
764        vr.write_json_value_text(&mut writer, self)
765            .map_err(|_| fmt::Error)?;
766
767        match self {
768            BinXmlValue::EvtHandle | BinXmlValue::BinXmlType(_) | BinXmlValue::EvtXml => Ok(()),
769            _ => write!(f, "{}", String::from_utf8(writer).unwrap()),
770        }
771    }
772}