macos_unifiedlogs/
uuidtext.rs1use log::error;
9use nom::Needed;
10use nom::bytes::complete::take;
11use nom::number::complete::le_u32;
12use serde::{Deserialize, Serialize};
13use std::mem::size_of;
14
15#[derive(Debug, Serialize, Deserialize, Default)]
16pub struct UUIDText {
17 pub uuid: String,
18 pub signature: u32,
19 pub unknown_major_version: u32,
20 pub unknown_minor_version: u32,
21 pub number_entries: u32,
22 pub entry_descriptors: Vec<UUIDTextEntry>,
23 pub footer_data: Vec<u8>, }
25#[derive(Debug, Serialize, Deserialize, Default)]
26pub struct UUIDTextEntry {
27 pub range_start_offset: u32,
28 pub entry_size: u32,
29}
30impl UUIDText {
31 pub fn parse_uuidtext(data: &[u8]) -> nom::IResult<&[u8], UUIDText> {
33 let mut uuidtext_data = UUIDText::default();
34
35 let expected_uuidtext_signature = 0x66778899;
36 let (input, signature) = take(size_of::<u32>())(data)?;
37 let (_, uuidtext_signature) = le_u32(signature)?;
38
39 if expected_uuidtext_signature != uuidtext_signature {
40 error!(
41 "[macos-unifiedlogs] Incorrect UUIDText header signature. Expected {expected_uuidtext_signature}. Got: {uuidtext_signature}"
42 );
43 return Err(nom::Err::Incomplete(Needed::Unknown));
44 }
45
46 let (input, unknown_major_version) = take(size_of::<u32>())(input)?;
47 let (input, unknown_minor_version) = take(size_of::<u32>())(input)?;
48 let (mut input, number_entries) = take(size_of::<u32>())(input)?;
49
50 let (_, uuidtext_unknown_major_version) = le_u32(unknown_major_version)?;
51 let (_, uuidtext_unknown_minor_version) = le_u32(unknown_minor_version)?;
52 let (_, uuidtext_number_entries) = le_u32(number_entries)?;
53
54 uuidtext_data.signature = uuidtext_signature;
55 uuidtext_data.unknown_major_version = uuidtext_unknown_major_version;
56 uuidtext_data.unknown_minor_version = uuidtext_unknown_minor_version;
57 uuidtext_data.number_entries = uuidtext_number_entries;
58
59 let mut count = 0;
60 while count < uuidtext_number_entries {
61 let (entry_input, range_start_offset) = take(size_of::<u32>())(input)?;
62 let (entry_input, entry_size) = take(size_of::<u32>())(entry_input)?;
63
64 let (_, uuidtext_range_start_offset) = le_u32(range_start_offset)?;
65 let (_, uuidtext_entry_size) = le_u32(entry_size)?;
66
67 let entry_data = UUIDTextEntry {
68 range_start_offset: uuidtext_range_start_offset,
69 entry_size: uuidtext_entry_size,
70 };
71 uuidtext_data.entry_descriptors.push(entry_data);
72
73 input = entry_input;
74 count += 1;
75 }
76 uuidtext_data.footer_data = input.to_vec();
77 Ok((input, uuidtext_data))
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use crate::uuidtext::UUIDText;
84 use std::fs;
85 use std::path::PathBuf;
86
87 #[test]
88 fn test_parse_uuidtext_big_sur() {
89 let mut test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
90 test_path.push("tests/test_data/UUIDText/Big Sur/1FE459BBDC3E19BBF82D58415A2AE9");
91
92 let buffer = fs::read(test_path).unwrap();
93
94 let (_, uuidtext_data) = UUIDText::parse_uuidtext(&buffer).unwrap();
95 assert_eq!(uuidtext_data.signature, 0x66778899);
96 assert_eq!(uuidtext_data.unknown_major_version, 2);
97 assert_eq!(uuidtext_data.unknown_minor_version, 1);
98 assert_eq!(uuidtext_data.number_entries, 2);
99 assert_eq!(uuidtext_data.entry_descriptors[0].entry_size, 617);
100 assert_eq!(uuidtext_data.entry_descriptors[1].entry_size, 2301);
101
102 assert_eq!(uuidtext_data.entry_descriptors[0].range_start_offset, 32048);
103 assert_eq!(uuidtext_data.entry_descriptors[1].range_start_offset, 29747);
104 assert_eq!(uuidtext_data.footer_data.len(), 2987);
105 }
106
107 #[test]
108 fn test_parse_uuidtext_high_sierra() {
109 let mut test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
110 test_path.push("tests/test_data/UUIDText/High Sierra/425A2E5B5531B98918411B4379EE5F");
111
112 let buffer = fs::read(test_path).unwrap();
113
114 let (_, uuidtext_data) = UUIDText::parse_uuidtext(&buffer).unwrap();
115 assert_eq!(uuidtext_data.signature, 0x66778899);
116 assert_eq!(uuidtext_data.unknown_major_version, 2);
117 assert_eq!(uuidtext_data.unknown_minor_version, 1);
118 assert_eq!(uuidtext_data.number_entries, 1);
119 assert_eq!(uuidtext_data.entry_descriptors[0].entry_size, 2740);
120 assert_eq!(uuidtext_data.entry_descriptors[0].range_start_offset, 21132);
121
122 assert_eq!(uuidtext_data.footer_data.len(), 2951);
123 }
124
125 #[test]
126 #[should_panic(expected = "Incomplete(Unknown)")]
127 fn test_bad_header() {
128 let mut test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
129 test_path
130 .push("tests/test_data/Bad Data/UUIDText/Bad_Header_1FE459BBDC3E19BBF82D58415A2AE9");
131
132 let buffer = fs::read(test_path).unwrap();
133 let (_, _) = UUIDText::parse_uuidtext(&buffer).unwrap();
134 }
135
136 #[test]
137 #[should_panic(expected = "Eof")]
138 fn test_bad_content() {
139 let mut test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
140 test_path
141 .push("tests/test_data/Bad Data/UUIDText/Bad_Content_1FE459BBDC3E19BBF82D58415A2AE9");
142
143 let buffer = fs::read(test_path).unwrap();
144 let (_, _) = UUIDText::parse_uuidtext(&buffer).unwrap();
145 }
146
147 #[test]
148 #[should_panic(expected = "Incomplete(Unknown)")]
149 fn test_bad_file() {
150 let mut test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
151 test_path.push("tests/test_data/Bad Data/UUIDText/Badfile.txt");
152
153 let buffer = fs::read(test_path).unwrap();
154 let (_, _) = UUIDText::parse_uuidtext(&buffer).unwrap();
155 }
156}