use crate::err::{self, Result};
use crate::evtx_parser::ReadSeek;
use encoding::EncodingRef;
use snafu::{OptionExt, ResultExt};
pub use byteorder::{LittleEndian, ReadBytesExt};
use crate::binxml::deserializer::BinXmlDeserializer;
use crate::guid::Guid;
use crate::model::deserialized::BinXMLDeserializedTokens;
use crate::ntsid::Sid;
use crate::utils::{
datetime_from_filetime, read_ansi_encoded_string, read_len_prefixed_utf16_string,
read_null_terminated_utf16_string, read_systemtime, read_utf16_by_size,
};
use chrono::{DateTime, Utc};
use log::trace;
use serde_json::{json, Value};
use std::borrow::Cow;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::string::ToString;
use crate::evtx_chunk::EvtxChunk;
use std::fmt::Write;
#[derive(Debug, PartialOrd, PartialEq, Clone)]
pub enum BinXmlValue<'a> {
NullType,
StringType(Cow<'a, str>),
AnsiStringType(Cow<'a, str>),
Int8Type(i8),
UInt8Type(u8),
Int16Type(i16),
UInt16Type(u16),
Int32Type(i32),
UInt32Type(u32),
Int64Type(i64),
UInt64Type(u64),
Real32Type(f32),
Real64Type(f64),
BoolType(bool),
BinaryType(&'a [u8]),
GuidType(Guid),
SizeTType(usize),
FileTimeType(DateTime<Utc>),
SysTimeType(DateTime<Utc>),
SidType(Sid),
HexInt32Type(Cow<'a, str>),
HexInt64Type(Cow<'a, str>),
EvtHandle,
BinXmlType(Vec<BinXMLDeserializedTokens<'a>>),
EvtXml,
StringArrayType(Vec<Cow<'a, str>>),
AnsiStringArrayType,
Int8ArrayType(Vec<i8>),
UInt8ArrayType(Vec<u8>),
Int16ArrayType(Vec<i16>),
UInt16ArrayType(Vec<u16>),
Int32ArrayType(Vec<i32>),
UInt32ArrayType(Vec<u32>),
Int64ArrayType(Vec<i64>),
UInt64ArrayType(Vec<u64>),
Real32ArrayType(Vec<f32>),
Real64ArrayType(Vec<f64>),
BoolArrayType(Vec<bool>),
BinaryArrayType,
GuidArrayType(Vec<Guid>),
SizeTArrayType,
FileTimeArrayType(Vec<DateTime<Utc>>),
SysTimeArrayType(Vec<DateTime<Utc>>),
SidArrayType(Vec<Sid>),
HexInt32ArrayType(Vec<Cow<'a, str>>),
HexInt64ArrayType(Vec<Cow<'a, str>>),
EvtArrayHandle,
BinXmlArrayType,
EvtXmlArrayType,
}
#[derive(Debug, PartialOrd, PartialEq, Clone)]
pub enum BinXmlValueType {
NullType,
StringType,
AnsiStringType,
Int8Type,
UInt8Type,
Int16Type,
UInt16Type,
Int32Type,
UInt32Type,
Int64Type,
UInt64Type,
Real32Type,
Real64Type,
BoolType,
BinaryType,
GuidType,
SizeTType,
FileTimeType,
SysTimeType,
SidType,
HexInt32Type,
HexInt64Type,
EvtHandle,
BinXmlType,
EvtXmlType,
StringArrayType,
AnsiStringArrayType,
Int8ArrayType,
UInt8ArrayType,
Int16ArrayType,
UInt16ArrayType,
Int32ArrayType,
UInt32ArrayType,
Int64ArrayType,
UInt64ArrayType,
Real32ArrayType,
Real64ArrayType,
BoolArrayType,
BinaryArrayType,
GuidArrayType,
SizeTArrayType,
FileTimeArrayType,
SysTimeArrayType,
SidArrayType,
HexInt32ArrayType,
HexInt64ArrayType,
EvtHandleArray,
BinXmlArrayType,
EvtXmlArrayType,
}
impl BinXmlValueType {
pub fn from_u8(byte: u8) -> Option<BinXmlValueType> {
match byte {
0x00 => Some(BinXmlValueType::NullType),
0x01 => Some(BinXmlValueType::StringType),
0x02 => Some(BinXmlValueType::AnsiStringType),
0x03 => Some(BinXmlValueType::Int8Type),
0x04 => Some(BinXmlValueType::UInt8Type),
0x05 => Some(BinXmlValueType::Int16Type),
0x06 => Some(BinXmlValueType::UInt16Type),
0x07 => Some(BinXmlValueType::Int32Type),
0x08 => Some(BinXmlValueType::UInt32Type),
0x09 => Some(BinXmlValueType::Int64Type),
0x0a => Some(BinXmlValueType::UInt64Type),
0x0b => Some(BinXmlValueType::Real32Type),
0x0c => Some(BinXmlValueType::Real64Type),
0x0d => Some(BinXmlValueType::BoolType),
0x0e => Some(BinXmlValueType::BinaryType),
0x0f => Some(BinXmlValueType::GuidType),
0x10 => Some(BinXmlValueType::SizeTType),
0x11 => Some(BinXmlValueType::FileTimeType),
0x12 => Some(BinXmlValueType::SysTimeType),
0x13 => Some(BinXmlValueType::SidType),
0x14 => Some(BinXmlValueType::HexInt32Type),
0x15 => Some(BinXmlValueType::HexInt64Type),
0x20 => Some(BinXmlValueType::EvtHandle),
0x21 => Some(BinXmlValueType::BinXmlType),
0x23 => Some(BinXmlValueType::EvtXmlType),
0x81 => Some(BinXmlValueType::StringArrayType),
0x82 => Some(BinXmlValueType::AnsiStringArrayType),
0x83 => Some(BinXmlValueType::Int8ArrayType),
0x84 => Some(BinXmlValueType::UInt8ArrayType),
0x85 => Some(BinXmlValueType::Int16ArrayType),
0x86 => Some(BinXmlValueType::UInt16ArrayType),
0x87 => Some(BinXmlValueType::Int32ArrayType),
0x88 => Some(BinXmlValueType::UInt32ArrayType),
0x89 => Some(BinXmlValueType::Int64ArrayType),
0x8a => Some(BinXmlValueType::UInt64ArrayType),
0x8b => Some(BinXmlValueType::Real32ArrayType),
0x8c => Some(BinXmlValueType::Real64ArrayType),
0x8d => Some(BinXmlValueType::BoolArrayType),
0x8e => Some(BinXmlValueType::BinaryArrayType),
0x8f => Some(BinXmlValueType::GuidArrayType),
0x90 => Some(BinXmlValueType::SizeTArrayType),
0x91 => Some(BinXmlValueType::FileTimeArrayType),
0x92 => Some(BinXmlValueType::SysTimeArrayType),
0x93 => Some(BinXmlValueType::SidArrayType),
0x94 => Some(BinXmlValueType::HexInt32ArrayType),
0x95 => Some(BinXmlValueType::HexInt64ArrayType),
_ => None,
}
}
}
impl<'a> BinXmlValue<'a> {
pub fn from_binxml_stream(
cursor: &mut Cursor<&'a [u8]>,
chunk: Option<&'a EvtxChunk<'a>>,
size: Option<u16>,
ansi_codec: EncodingRef,
) -> Result<BinXmlValue<'a>> {
let value_type_token = try_read!(cursor, u8);
let value_type =
BinXmlValueType::from_u8(value_type_token).context(err::InvalidValueVariant {
value: value_type_token,
offset: cursor.position(),
})?;
let data = Self::deserialize_value_type(&value_type, cursor, chunk, size, ansi_codec)?;
Ok(data)
}
pub fn deserialize_value_type(
value_type: &BinXmlValueType,
cursor: &mut Cursor<&'a [u8]>,
chunk: Option<&'a EvtxChunk<'a>>,
size: Option<u16>,
ansi_codec: EncodingRef,
) -> Result<BinXmlValue<'a>> {
trace!("deserialize_value_type: {:?}, {:?}", value_type, size);
let value = match (value_type, size) {
(BinXmlValueType::NullType, _) => BinXmlValue::NullType,
(BinXmlValueType::StringType, Some(sz)) => BinXmlValue::StringType(Cow::Owned(
read_utf16_by_size(cursor, u64::from(sz))
.context(err::FailedToDecodeUTF16String {
offset: cursor.position(),
})?
.unwrap_or_else(|| "".to_owned()),
)),
(BinXmlValueType::StringType, None) => {
BinXmlValue::StringType(try_read!(cursor, utf_16_str))
}
(BinXmlValueType::AnsiStringType, Some(sz)) => BinXmlValue::AnsiStringType(Cow::Owned(
read_ansi_encoded_string(cursor, u64::from(sz), ansi_codec)?
.unwrap_or_else(|| "".to_owned()),
)),
(BinXmlValueType::AnsiStringType, None) => err::UnimplementedValueVariant {
name: "AnsiString",
size: None,
offset: cursor.position(),
}
.fail()?,
(BinXmlValueType::Int8Type, _) => BinXmlValue::Int8Type(try_read!(cursor, i8)),
(BinXmlValueType::UInt8Type, _) => BinXmlValue::UInt8Type(try_read!(cursor, u8)),
(BinXmlValueType::Int16Type, _) => BinXmlValue::Int16Type(try_read!(cursor, i16)),
(BinXmlValueType::UInt16Type, _) => BinXmlValue::UInt16Type(try_read!(cursor, u16)),
(BinXmlValueType::Int32Type, _) => BinXmlValue::Int32Type(try_read!(cursor, i32)),
(BinXmlValueType::UInt32Type, _) => BinXmlValue::UInt32Type(try_read!(cursor, u32)),
(BinXmlValueType::Int64Type, _) => BinXmlValue::Int64Type(try_read!(cursor, i64)),
(BinXmlValueType::UInt64Type, _) => BinXmlValue::UInt64Type(try_read!(cursor, u64)),
(BinXmlValueType::Real32Type, _) => BinXmlValue::Real32Type(try_read!(cursor, f32)),
(BinXmlValueType::Real64Type, _) => BinXmlValue::Real64Type(try_read!(cursor, f64)),
(BinXmlValueType::BoolType, _) => BinXmlValue::BoolType(try_read!(cursor, bool)),
(BinXmlValueType::GuidType, _) => BinXmlValue::GuidType(try_read!(cursor, guid)),
(BinXmlValueType::SizeTType, _) => err::UnimplementedValueVariant {
name: "SizeT",
size,
offset: cursor.position(),
}
.fail()?,
(BinXmlValueType::FileTimeType, _) => {
BinXmlValue::FileTimeType(try_read!(cursor, filetime))
}
(BinXmlValueType::SysTimeType, _) => {
BinXmlValue::SysTimeType(try_read!(cursor, systime))
}
(BinXmlValueType::SidType, _) => BinXmlValue::SidType(try_read!(cursor, sid)),
(BinXmlValueType::HexInt32Type, _) => {
BinXmlValue::HexInt32Type(try_read!(cursor, hex32))
}
(BinXmlValueType::HexInt64Type, _) => {
BinXmlValue::HexInt64Type(try_read!(cursor, hex64))
}
(BinXmlValueType::BinXmlType, None) => {
let tokens = BinXmlDeserializer::read_binxml_fragment(
cursor, chunk, None, true, ansi_codec,
)?;
BinXmlValue::BinXmlType(tokens)
}
(BinXmlValueType::BinXmlType, Some(sz)) => {
let tokens = BinXmlDeserializer::read_binxml_fragment(
cursor,
chunk,
Some(u32::from(sz)),
true,
ansi_codec,
)?;
BinXmlValue::BinXmlType(tokens)
}
(BinXmlValueType::BinaryType, Some(sz)) => {
let data = *cursor.get_ref();
let bytes =
&data[cursor.position() as usize..(cursor.position() + u64::from(sz)) as usize];
cursor.seek(SeekFrom::Current(i64::from(sz)))?;
BinXmlValue::BinaryType(bytes)
}
(BinXmlValueType::StringArrayType, Some(sz)) => BinXmlValue::StringArrayType(
try_read_sized_array!(cursor, null_terminated_utf_16_str, sz),
),
(BinXmlValueType::Int8ArrayType, Some(sz)) => {
BinXmlValue::Int8ArrayType(try_read_sized_array!(cursor, i8, sz))
}
(BinXmlValueType::UInt8ArrayType, Some(sz)) => {
let mut data = vec![0; sz as usize];
cursor.read_exact(&mut data)?;
BinXmlValue::UInt8ArrayType(data)
}
(BinXmlValueType::Int16ArrayType, Some(sz)) => {
BinXmlValue::Int16ArrayType(try_read_sized_array!(cursor, i16, sz))
}
(BinXmlValueType::UInt16ArrayType, Some(sz)) => {
BinXmlValue::UInt16ArrayType(try_read_sized_array!(cursor, u16, sz))
}
(BinXmlValueType::Int32ArrayType, Some(sz)) => {
BinXmlValue::Int32ArrayType(try_read_sized_array!(cursor, i32, sz))
}
(BinXmlValueType::UInt32ArrayType, Some(sz)) => {
BinXmlValue::UInt32ArrayType(try_read_sized_array!(cursor, u32, sz))
}
(BinXmlValueType::Int64ArrayType, Some(sz)) => {
BinXmlValue::Int64ArrayType(try_read_sized_array!(cursor, i64, sz))
}
(BinXmlValueType::UInt64ArrayType, Some(sz)) => {
BinXmlValue::UInt64ArrayType(try_read_sized_array!(cursor, u64, sz))
}
(BinXmlValueType::Real32ArrayType, Some(sz)) => {
BinXmlValue::Real32ArrayType(try_read_sized_array!(cursor, f32, sz))
}
(BinXmlValueType::Real64ArrayType, Some(sz)) => {
BinXmlValue::Real64ArrayType(try_read_sized_array!(cursor, f64, sz))
}
(BinXmlValueType::BoolArrayType, Some(sz)) => {
BinXmlValue::BoolArrayType(try_read_sized_array!(cursor, bool, sz))
}
(BinXmlValueType::GuidArrayType, Some(sz)) => {
BinXmlValue::GuidArrayType(try_read_sized_array!(cursor, guid, sz))
}
(BinXmlValueType::FileTimeArrayType, Some(sz)) => {
BinXmlValue::FileTimeArrayType(try_read_sized_array!(cursor, filetime, sz))
}
(BinXmlValueType::SysTimeArrayType, Some(sz)) => {
BinXmlValue::SysTimeArrayType(try_read_sized_array!(cursor, systime, sz))
}
(BinXmlValueType::SidArrayType, Some(sz)) => {
BinXmlValue::SidArrayType(try_read_sized_array!(cursor, sid, sz))
}
(BinXmlValueType::HexInt32ArrayType, Some(sz)) => {
BinXmlValue::HexInt32ArrayType(try_read_sized_array!(cursor, hex32, sz))
}
(BinXmlValueType::HexInt64ArrayType, Some(sz)) => {
BinXmlValue::HexInt64ArrayType(try_read_sized_array!(cursor, hex64, sz))
}
_ => err::UnimplementedValueVariant {
name: format!("{:?}", value_type),
size,
offset: cursor.position(),
}
.fail()?,
};
Ok(value)
}
}
fn to_delimited_list<N: ToString>(ns: impl AsRef<Vec<N>>) -> String {
ns.as_ref()
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join(",")
}
impl<'c> Into<serde_json::Value> for BinXmlValue<'c> {
fn into(self) -> Value {
match self {
BinXmlValue::NullType => Value::Null,
BinXmlValue::StringType(s) => json!(s.into_owned()),
BinXmlValue::AnsiStringType(s) => json!(s.into_owned()),
BinXmlValue::Int8Type(num) => json!(num),
BinXmlValue::UInt8Type(num) => json!(num),
BinXmlValue::Int16Type(num) => json!(num),
BinXmlValue::UInt16Type(num) => json!(num),
BinXmlValue::Int32Type(num) => json!(num),
BinXmlValue::UInt32Type(num) => json!(num),
BinXmlValue::Int64Type(num) => json!(num),
BinXmlValue::UInt64Type(num) => json!(num),
BinXmlValue::Real32Type(num) => json!(num),
BinXmlValue::Real64Type(num) => json!(num),
BinXmlValue::BoolType(num) => json!(num),
BinXmlValue::BinaryType(bytes) => {
let repr: String = bytes.iter().map(|b| format!("{:02X}", b)).collect();
json!(repr)
}
BinXmlValue::GuidType(guid) => json!(guid.to_string()),
BinXmlValue::FileTimeType(tm) => json!(tm),
BinXmlValue::SysTimeType(tm) => json!(tm),
BinXmlValue::SidType(sid) => json!(sid.to_string()),
BinXmlValue::HexInt32Type(hex_string) => json!(hex_string),
BinXmlValue::HexInt64Type(hex_string) => json!(hex_string),
BinXmlValue::StringArrayType(s) => json!(s),
BinXmlValue::Int8ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt8ArrayType(numbers) => json!(numbers),
BinXmlValue::Int16ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt16ArrayType(numbers) => json!(numbers),
BinXmlValue::Int32ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt32ArrayType(numbers) => json!(numbers),
BinXmlValue::Int64ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt64ArrayType(numbers) => json!(numbers),
BinXmlValue::Real32ArrayType(numbers) => json!(numbers),
BinXmlValue::Real64ArrayType(numbers) => json!(numbers),
BinXmlValue::BoolArrayType(bools) => json!(bools),
BinXmlValue::GuidArrayType(guids) => {
json!(guids.iter().map(Guid::to_string).collect::<Vec<String>>())
}
BinXmlValue::FileTimeArrayType(filetimes) => json!(filetimes),
BinXmlValue::SysTimeArrayType(systimes) => json!(systimes),
BinXmlValue::SidArrayType(sids) => {
json!(sids.iter().map(Sid::to_string).collect::<Vec<String>>())
}
BinXmlValue::HexInt32ArrayType(hex_strings) => json!(hex_strings),
BinXmlValue::HexInt64ArrayType(hex_strings) => json!(hex_strings),
BinXmlValue::EvtHandle => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::BinXmlType(_) => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::EvtXml => panic!("Unsupported conversion, call `expand_templates` first"),
_ => unimplemented!("{:?}", self),
}
}
}
impl<'c> Into<serde_json::Value> for &'c BinXmlValue<'c> {
fn into(self) -> Value {
match self {
BinXmlValue::NullType => Value::Null,
BinXmlValue::StringType(s) => json!(s.as_ref()),
BinXmlValue::AnsiStringType(s) => json!(s.as_ref()),
BinXmlValue::Int8Type(num) => json!(num),
BinXmlValue::UInt8Type(num) => json!(num),
BinXmlValue::Int16Type(num) => json!(num),
BinXmlValue::UInt16Type(num) => json!(num),
BinXmlValue::Int32Type(num) => json!(num),
BinXmlValue::UInt32Type(num) => json!(num),
BinXmlValue::Int64Type(num) => json!(num),
BinXmlValue::UInt64Type(num) => json!(num),
BinXmlValue::Real32Type(num) => json!(num),
BinXmlValue::Real64Type(num) => json!(num),
BinXmlValue::BoolType(num) => json!(num),
BinXmlValue::BinaryType(bytes) => {
let repr: String = bytes.iter().map(|b| format!("{:02X}", b)).collect();
json!(repr)
}
BinXmlValue::GuidType(guid) => json!(guid.to_string()),
BinXmlValue::FileTimeType(tm) => json!(tm),
BinXmlValue::SysTimeType(tm) => json!(tm),
BinXmlValue::SidType(sid) => json!(sid.to_string()),
BinXmlValue::HexInt32Type(hex_string) => json!(hex_string),
BinXmlValue::HexInt64Type(hex_string) => json!(hex_string),
BinXmlValue::StringArrayType(s) => json!(s),
BinXmlValue::Int8ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt8ArrayType(numbers) => json!(numbers),
BinXmlValue::Int16ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt16ArrayType(numbers) => json!(numbers),
BinXmlValue::Int32ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt32ArrayType(numbers) => json!(numbers),
BinXmlValue::Int64ArrayType(numbers) => json!(numbers),
BinXmlValue::UInt64ArrayType(numbers) => json!(numbers),
BinXmlValue::Real32ArrayType(numbers) => json!(numbers),
BinXmlValue::Real64ArrayType(numbers) => json!(numbers),
BinXmlValue::BoolArrayType(bools) => json!(bools),
BinXmlValue::GuidArrayType(guids) => {
json!(guids.iter().map(Guid::to_string).collect::<Vec<String>>())
}
BinXmlValue::FileTimeArrayType(filetimes) => json!(filetimes),
BinXmlValue::SysTimeArrayType(systimes) => json!(systimes),
BinXmlValue::SidArrayType(sids) => {
json!(sids.iter().map(Sid::to_string).collect::<Vec<String>>())
}
BinXmlValue::HexInt32ArrayType(hex_strings) => json!(hex_strings),
BinXmlValue::HexInt64ArrayType(hex_strings) => json!(hex_strings),
BinXmlValue::EvtHandle => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::BinXmlType(_) => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::EvtXml => panic!("Unsupported conversion, call `expand_templates` first"),
_ => unimplemented!("{:?}", self),
}
}
}
impl<'a> BinXmlValue<'a> {
pub fn as_cow_str(&self) -> Cow<str> {
match self {
BinXmlValue::NullType => Cow::Borrowed(""),
BinXmlValue::StringType(s) => Cow::Borrowed(s.as_ref()),
BinXmlValue::AnsiStringType(s) => Cow::Borrowed(s.as_ref()),
BinXmlValue::Int8Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::UInt8Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::Int16Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::UInt16Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::Int32Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::UInt32Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::Int64Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::UInt64Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::Real32Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::Real64Type(num) => Cow::Owned(num.to_string()),
BinXmlValue::BoolType(num) => Cow::Owned(num.to_string()),
BinXmlValue::BinaryType(bytes) => {
let mut repr = String::with_capacity(bytes.len() * 2);
for b in bytes.iter() {
write!(repr, "{:02X}", b).expect("Writing to a String cannot fail");
}
Cow::Owned(repr)
}
BinXmlValue::GuidType(guid) => Cow::Owned(guid.to_string()),
BinXmlValue::SizeTType(sz) => Cow::Owned(sz.to_string()),
BinXmlValue::FileTimeType(tm) => Cow::Owned(tm.to_string()),
BinXmlValue::SysTimeType(tm) => Cow::Owned(tm.to_string()),
BinXmlValue::SidType(sid) => Cow::Owned(sid.to_string()),
BinXmlValue::HexInt32Type(hex_string) => hex_string.clone(),
BinXmlValue::HexInt64Type(hex_string) => hex_string.clone(),
BinXmlValue::StringArrayType(s) => Cow::Owned(s.join(",")),
BinXmlValue::Int8ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::UInt8ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::Int16ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::UInt16ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::Int32ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::UInt32ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::Int64ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::UInt64ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::Real32ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::Real64ArrayType(numbers) => Cow::Owned(to_delimited_list(numbers)),
BinXmlValue::BoolArrayType(bools) => Cow::Owned(to_delimited_list(bools)),
BinXmlValue::GuidArrayType(guids) => Cow::Owned(to_delimited_list(guids)),
BinXmlValue::FileTimeArrayType(filetimes) => Cow::Owned(to_delimited_list(filetimes)),
BinXmlValue::SysTimeArrayType(systimes) => Cow::Owned(to_delimited_list(systimes)),
BinXmlValue::SidArrayType(sids) => Cow::Owned(to_delimited_list(sids)),
BinXmlValue::HexInt32ArrayType(hex_strings) => Cow::Owned(hex_strings.join(",")),
BinXmlValue::HexInt64ArrayType(hex_strings) => Cow::Owned(hex_strings.join(",")),
BinXmlValue::EvtHandle => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::BinXmlType(_) => {
panic!("Unsupported conversion, call `expand_templates` first")
}
BinXmlValue::EvtXml => panic!("Unsupported conversion, call `expand_templates` first"),
_ => unimplemented!("{:?}", self),
}
}
}