///|
/// BINXML / substitution type codes used by python-evtx `NODE_TYPES`.
pub fn variant_type_name(type_code : Int) -> String {
let code = type_code & 0x7F
match code {
0x00 => "Null"
0x01 => "WString"
0x02 => "String"
0x03 => "Int8"
0x04 => "UInt8"
0x05 => "Int16"
0x06 => "UInt16"
0x07 => "Int32"
0x08 => "UInt32"
0x09 => "Int64"
0x0A => "UInt64"
0x0B => "Float32"
0x0C => "Float64"
0x0D => "Boolean"
0x0E => "Binary"
0x0F => "Guid"
0x10 => "Size"
0x11 => "FileTime"
0x12 => "SystemTime"
0x13 => "SID"
0x14 => "HexInt32"
0x15 => "HexInt64"
0x21 => "BXml"
_ => "Unknown"
}
}
///|
fn is_array_type(type_code : Int) -> Bool {
(type_code & 0x80) != 0
}
///|
fn format_wstring_payload(
data : Bytes,
pos : Int,
length : Int?,
) -> Result[(String, Int), EvtxError] {
match length {
None => {
let (count, next) = match read_u16(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
let text = rstrip_utf16_nuls(
match read_utf16_units(data, next, count) {
Ok(v) => v
Err(e) => return Err(e)
},
)
Ok((text, 2 + count * 2))
}
Some(n) => {
let text = rstrip_utf16_nuls(
match read_utf16_bytes(data, pos, n) {
Ok(v) => v
Err(e) => return Err(e)
},
)
Ok((text, n))
}
}
}
///|
fn format_ansi_payload(
data : Bytes,
pos : Int,
length : Int?,
) -> Result[(String, Int), EvtxError] {
match length {
None => {
let (count, next) = match read_u16(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
let _ = match need(data, next, count, "ansi") {
Ok(v) => v
Err(e) => return Err(e)
}
let sb = StringBuilder()
for i = 0; i < count; i = i + 1 {
let cp = data[next + i].to_int()
if cp != 0 {
sb.write_char(cp.unsafe_to_char())
}
}
Ok((sb.to_string(), 2 + count))
}
Some(n) => {
let _ = match need(data, pos, n, "ansi-sub") {
Ok(v) => v
Err(e) => return Err(e)
}
let sb = StringBuilder()
for i = 0; i < n; i = i + 1 {
let cp = data[pos + i].to_int()
if cp != 0 {
sb.write_char(cp.unsafe_to_char())
}
}
Ok((sb.to_string(), n))
}
}
}
///|
fn format_binary_payload(
data : Bytes,
pos : Int,
length : Int?,
) -> Result[(String, Int), EvtxError] {
match length {
None => {
let (size, next) = match read_u32(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
let n = size.reinterpret_as_int()
let _ = match need(data, next, n, "binary") {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((base64_encode(bytes_slice(data, next, next + n)), 4 + n))
}
Some(n) => {
let _ = match need(data, pos, n, "binary-sub") {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((base64_encode(bytes_slice(data, pos, pos + n)), n))
}
}
}
///|
fn format_size_payload(
data : Bytes,
pos : Int,
length : Int?,
) -> Result[(String, Int), EvtxError] {
let n = match length {
None => 8
Some(v) => v
}
if n == 4 {
let (v, _) = match read_u32(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 4))
} else {
let (v, _) = match read_u64(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), n))
}
}
///|
fn format_wstring_array(
data : Bytes,
pos : Int,
length : Int?,
) -> Result[(String, Int), EvtxError] {
let n = match length {
None => {
let (count, _) = match read_u16(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
2 + count
}
Some(v) => v
}
let start = match length {
None => pos + 2
Some(_) => pos
}
let payload_len = match length {
None => n - 2
Some(v) => v
}
let _ = match need(data, start, payload_len, "wstring-array") {
Ok(v) => v
Err(e) => return Err(e)
}
let out = StringBuilder()
let mut ofs = 0
while ofs + 1 < payload_len {
let mut end = ofs
while end + 1 < payload_len {
let (unit, _) = match read_u16(data, start + end) {
Ok(v) => v
Err(e) => return Err(e)
}
if unit == 0 {
break
}
end = end + 2
}
let chars = (end - ofs) / 2
let piece = match read_utf16_units(data, start + ofs, chars) {
Ok(v) => v
Err(e) => return Err(e)
}
out.write_string("")
out.write_string(piece)
out.write_string("\n")
ofs = end + 2
}
Ok((out.to_string(), n))
}
///|
/// Format a variant payload using python-evtx `VariantTypeNode.string()` rules.
pub fn format_variant(
data : Bytes,
pos : Int,
type_code : Int,
length : Int?,
style : TimeStyle,
) -> Result[(String, Int), EvtxError] {
if is_array_type(type_code) && (type_code & 0x7F) == 0x01 {
return format_wstring_array(data, pos, length)
}
let code = type_code & 0x7F
match code {
0x00 => {
let n = match length {
None => 0
Some(v) => v
}
Ok(("", n))
}
0x01 => format_wstring_payload(data, pos, length)
0x02 => format_ansi_payload(data, pos, length)
0x03 => {
let v = match read_i8_at(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 1))
}
0x04 => {
let (v, _) = match read_u8(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 1))
}
0x05 => {
let v = match read_i16_at(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 2))
}
0x06 => {
let (v, _) = match read_u16(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 2))
}
0x07 => {
let v = match read_i32_at(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 4))
}
0x08 => {
let (v, _) = match read_u32(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 4))
}
0x09 => {
let v = match read_i64_at(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 8))
}
0x0A => {
let (v, _) = match read_u64(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((v.to_string(), 8))
}
0x0B => {
let (v, _) = match read_u32(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok(
(
"0x" +
hex_byte((v >> 24).reinterpret_as_int() & 255) +
hex_byte((v >> 16).reinterpret_as_int() & 255) +
hex_byte((v >> 8).reinterpret_as_int() & 255) +
hex_byte(v.reinterpret_as_int() & 255),
4,
),
)
}
0x0C => {
let _ = match need(data, pos, 8, "f64") {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((hex_encode(bytes_slice(data, pos, pos + 8)), 8))
}
0x0D => {
let v = match read_i32_at(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
if v > 0 {
Ok(("True", 4))
} else {
Ok(("False", 4))
}
}
0x0E => format_binary_payload(data, pos, length)
0x0F =>
Ok(
(
match format_guid_node(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
},
16,
),
)
0x10 => format_size_payload(data, pos, length)
0x11 => {
let (ft, _) = match read_u64(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((format_filetime(ft, style), 8))
}
0x12 =>
Ok(
(
match format_systemtime(data, pos, style) {
Ok(v) => v
Err(e) => return Err(e)
},
16,
),
)
0x13 => {
let n = match length {
None => 0
Some(v) => v
}
let (text, used) = match format_sid(data, pos, n) {
Ok(v) => v
Err(e) => return Err(e)
}
Ok((text, used))
}
0x14 =>
Ok(
(
match hex_int32_string(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
},
4,
),
)
0x15 =>
Ok(
(
match hex_int64_string(data, pos) {
Ok(v) => v
Err(e) => return Err(e)
},
8,
),
)
0x21 => {
let n = match length {
None => 0
Some(v) => v
}
Ok(("", n))
}
_ => Err(UnsupportedType(type_code, pos))
}
}