1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
6use std::io::{self, Cursor, Read, Write};
7
8use crate::error::{Error, Result};
9use crate::structures::{calculate_checksum, BASE_BLOCK_SIZE, REGF_SIGNATURE};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u32)]
14pub enum FileType {
15 Primary = 0,
17 TransactionLog = 1,
19 TransactionLogLegacy = 2,
21 TransactionLogNew = 6,
23}
24
25impl TryFrom<u32> for FileType {
26 type Error = Error;
27
28 fn try_from(value: u32) -> Result<Self> {
29 match value {
30 0 => Ok(FileType::Primary),
31 1 => Ok(FileType::TransactionLog),
32 2 => Ok(FileType::TransactionLogLegacy),
33 6 => Ok(FileType::TransactionLogNew),
34 _ => Err(Error::CorruptHive(format!("Unknown file type: {}", value))),
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(u32)]
42pub enum FileFormat {
43 DirectMemoryLoad = 1,
45}
46
47impl TryFrom<u32> for FileFormat {
48 type Error = Error;
49
50 fn try_from(value: u32) -> Result<Self> {
51 match value {
52 1 => Ok(FileFormat::DirectMemoryLoad),
53 _ => Err(Error::CorruptHive(format!("Unknown file format: {}", value))),
54 }
55 }
56}
57
58bitflags::bitflags! {
59 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
61 pub struct BaseBlockFlags: u32 {
62 const KTM_LOCKED = 0x00000001;
64 const DEFRAGMENTED_OR_LAYERED = 0x00000002;
66 }
67}
68
69pub const OFRG_SIGNATURE: &[u8; 4] = b"OfRg";
71
72pub mod reorganized_bits {
74 pub const DEFRAGMENTED: u64 = 0x01;
76 pub const ACCESS_HISTORY_CLEARED: u64 = 0x02;
78 pub const SPECIAL_BITS_MASK: u64 = 0x03;
80}
81
82#[derive(Debug, Clone, Default)]
85pub struct OfflineRegistryInfo {
86 pub present: bool,
88 pub flags: u32,
90 pub serialization_timestamp: u64,
92}
93
94#[derive(Debug, Clone)]
98pub struct BaseBlock {
99 pub signature: [u8; 4],
101 pub primary_sequence: u32,
103 pub secondary_sequence: u32,
105 pub last_written: u64,
107 pub major_version: u32,
109 pub minor_version: u32,
111 pub file_type: u32,
113 pub file_format: u32,
115 pub root_cell_offset: u32,
117 pub hive_bins_data_size: u32,
119 pub clustering_factor: u32,
121 pub file_name: [u8; 64],
123 pub rm_id: [u8; 16],
125 pub log_id: [u8; 16],
127 pub flags: u32,
129 pub tm_id: [u8; 16],
131 pub guid_signature: [u8; 4],
133 pub last_reorganized: u64,
135 pub checksum: u32,
137 pub thaw_tm_id: [u8; 16],
139 pub thaw_rm_id: [u8; 16],
141 pub thaw_log_id: [u8; 16],
143 pub boot_type: u32,
145 pub boot_recover: u32,
147 pub offline_registry: OfflineRegistryInfo,
149}
150
151impl Default for BaseBlock {
152 fn default() -> Self {
153 Self {
154 signature: *REGF_SIGNATURE,
155 primary_sequence: 1,
156 secondary_sequence: 1,
157 last_written: 0,
158 major_version: 1,
159 minor_version: 6,
160 file_type: 0,
161 file_format: 1,
162 root_cell_offset: 32, hive_bins_data_size: 4096,
164 clustering_factor: 1,
165 file_name: [0; 64],
166 rm_id: [0; 16],
167 log_id: [0; 16],
168 flags: 0,
169 tm_id: [0; 16],
170 guid_signature: [0; 4],
171 last_reorganized: 0,
172 checksum: 0,
173 thaw_tm_id: [0; 16],
174 thaw_rm_id: [0; 16],
175 thaw_log_id: [0; 16],
176 boot_type: 0,
177 boot_recover: 0,
178 offline_registry: OfflineRegistryInfo::default(),
179 }
180 }
181}
182
183impl BaseBlock {
184 pub fn parse(data: &[u8]) -> Result<Self> {
186 if data.len() < BASE_BLOCK_SIZE {
187 return Err(Error::BufferTooSmall {
188 needed: BASE_BLOCK_SIZE,
189 available: data.len(),
190 });
191 }
192
193 let mut cursor = Cursor::new(data);
194
195 let mut signature = [0u8; 4];
197 cursor.read_exact(&mut signature)?;
198
199 if &signature != REGF_SIGNATURE {
200 return Err(Error::InvalidSignature {
201 expected: String::from_utf8_lossy(REGF_SIGNATURE).to_string(),
202 found: String::from_utf8_lossy(&signature).to_string(),
203 });
204 }
205
206 let primary_sequence = cursor.read_u32::<LittleEndian>()?;
207 let secondary_sequence = cursor.read_u32::<LittleEndian>()?;
208 let last_written = cursor.read_u64::<LittleEndian>()?;
209 let major_version = cursor.read_u32::<LittleEndian>()?;
210 let minor_version = cursor.read_u32::<LittleEndian>()?;
211 let file_type = cursor.read_u32::<LittleEndian>()?;
212 let file_format = cursor.read_u32::<LittleEndian>()?;
213 let root_cell_offset = cursor.read_u32::<LittleEndian>()?;
214 let hive_bins_data_size = cursor.read_u32::<LittleEndian>()?;
215 let clustering_factor = cursor.read_u32::<LittleEndian>()?;
216
217 let mut file_name = [0u8; 64];
218 cursor.read_exact(&mut file_name)?;
219
220 let mut rm_id = [0u8; 16];
222 cursor.read_exact(&mut rm_id)?;
223
224 let mut log_id = [0u8; 16];
225 cursor.read_exact(&mut log_id)?;
226
227 let flags = cursor.read_u32::<LittleEndian>()?;
228
229 let mut tm_id = [0u8; 16];
230 cursor.read_exact(&mut tm_id)?;
231
232 let mut guid_signature = [0u8; 4];
233 cursor.read_exact(&mut guid_signature)?;
234
235 let last_reorganized = cursor.read_u64::<LittleEndian>()?;
236
237 cursor.set_position(508);
239 let checksum = cursor.read_u32::<LittleEndian>()?;
240
241 let calculated_checksum = calculate_checksum(data);
243 if checksum != calculated_checksum {
244 return Err(Error::ChecksumMismatch {
245 expected: checksum,
246 calculated: calculated_checksum,
247 });
248 }
249
250 cursor.set_position(4040);
252 let mut thaw_tm_id = [0u8; 16];
253 cursor.read_exact(&mut thaw_tm_id)?;
254
255 let mut thaw_rm_id = [0u8; 16];
256 cursor.read_exact(&mut thaw_rm_id)?;
257
258 let mut thaw_log_id = [0u8; 16];
259 cursor.read_exact(&mut thaw_log_id)?;
260
261 cursor.set_position(4088);
263 let boot_type = cursor.read_u32::<LittleEndian>()?;
264 let boot_recover = cursor.read_u32::<LittleEndian>()?;
265
266 let mut offline_registry = OfflineRegistryInfo::default();
269
270 cursor.set_position(176);
272 let mut ofrg_sig = [0u8; 4];
273 cursor.read_exact(&mut ofrg_sig)?;
274
275 if &ofrg_sig == OFRG_SIGNATURE {
276 offline_registry.present = true;
277 offline_registry.flags = cursor.read_u32::<LittleEndian>()?;
278 cursor.set_position(512);
280 offline_registry.serialization_timestamp = cursor.read_u64::<LittleEndian>()?;
281 } else {
282 cursor.set_position(168);
284 cursor.read_exact(&mut ofrg_sig)?;
285 if &ofrg_sig == OFRG_SIGNATURE {
286 offline_registry.present = true;
287 offline_registry.flags = cursor.read_u32::<LittleEndian>()?;
288 cursor.set_position(512);
289 offline_registry.serialization_timestamp = cursor.read_u64::<LittleEndian>()?;
290 }
291 }
292
293 Ok(Self {
294 signature,
295 primary_sequence,
296 secondary_sequence,
297 last_written,
298 major_version,
299 minor_version,
300 file_type,
301 file_format,
302 root_cell_offset,
303 hive_bins_data_size,
304 clustering_factor,
305 file_name,
306 rm_id,
307 log_id,
308 flags,
309 tm_id,
310 guid_signature,
311 last_reorganized,
312 checksum,
313 thaw_tm_id,
314 thaw_rm_id,
315 thaw_log_id,
316 boot_type,
317 boot_recover,
318 offline_registry,
319 })
320 }
321
322 pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
324 let mut buffer = vec![0u8; BASE_BLOCK_SIZE];
325
326 {
327 let mut cursor = Cursor::new(&mut buffer[..]);
328
329 cursor.write_all(&self.signature)?;
330 cursor.write_u32::<LittleEndian>(self.primary_sequence)?;
331 cursor.write_u32::<LittleEndian>(self.secondary_sequence)?;
332 cursor.write_u64::<LittleEndian>(self.last_written)?;
333 cursor.write_u32::<LittleEndian>(self.major_version)?;
334 cursor.write_u32::<LittleEndian>(self.minor_version)?;
335 cursor.write_u32::<LittleEndian>(self.file_type)?;
336 cursor.write_u32::<LittleEndian>(self.file_format)?;
337 cursor.write_u32::<LittleEndian>(self.root_cell_offset)?;
338 cursor.write_u32::<LittleEndian>(self.hive_bins_data_size)?;
339 cursor.write_u32::<LittleEndian>(self.clustering_factor)?;
340 cursor.write_all(&self.file_name)?;
341 cursor.write_all(&self.rm_id)?;
342 cursor.write_all(&self.log_id)?;
343 cursor.write_u32::<LittleEndian>(self.flags)?;
344 cursor.write_all(&self.tm_id)?;
345 cursor.write_all(&self.guid_signature)?;
346 cursor.write_u64::<LittleEndian>(self.last_reorganized)?;
347 }
348
349 let checksum = calculate_checksum(&buffer);
351 buffer[508..512].copy_from_slice(&checksum.to_le_bytes());
352
353 buffer[4040..4056].copy_from_slice(&self.thaw_tm_id);
355 buffer[4056..4072].copy_from_slice(&self.thaw_rm_id);
356 buffer[4072..4088].copy_from_slice(&self.thaw_log_id);
357
358 buffer[4088..4092].copy_from_slice(&self.boot_type.to_le_bytes());
360 buffer[4092..4096].copy_from_slice(&self.boot_recover.to_le_bytes());
361
362 writer.write_all(&buffer)
363 }
364
365 pub fn is_dirty(&self) -> bool {
367 self.primary_sequence != self.secondary_sequence
368 }
369
370 pub fn get_file_type(&self) -> Result<FileType> {
372 FileType::try_from(self.file_type)
373 }
374
375 pub fn get_file_format(&self) -> Result<FileFormat> {
377 FileFormat::try_from(self.file_format)
378 }
379
380 pub fn get_flags(&self) -> BaseBlockFlags {
382 BaseBlockFlags::from_bits_truncate(self.flags)
383 }
384
385 pub fn get_file_name(&self) -> String {
387 let u16_values: Vec<u16> = self.file_name
389 .chunks_exact(2)
390 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
391 .take_while(|&c| c != 0)
392 .collect();
393
394 String::from_utf16_lossy(&u16_values)
395 }
396
397 pub fn set_file_name(&mut self, name: &str) {
399 let mut file_name = [0u8; 64];
400 let u16_values: Vec<u16> = name.encode_utf16().collect();
401
402 for (i, &value) in u16_values.iter().take(31).enumerate() {
403 let bytes = value.to_le_bytes();
404 file_name[i * 2] = bytes[0];
405 file_name[i * 2 + 1] = bytes[1];
406 }
407
408 self.file_name = file_name;
409 }
410
411 pub fn prepare_for_write(&mut self) {
413 use chrono::Utc;
414 use crate::structures::datetime_to_filetime;
415
416 self.primary_sequence = self.primary_sequence.wrapping_add(1);
417 self.last_written = datetime_to_filetime(Utc::now());
418 }
419
420 pub fn was_defragmented(&self) -> bool {
423 (self.last_reorganized & reorganized_bits::DEFRAGMENTED) != 0
424 }
425
426 pub fn was_access_history_cleared(&self) -> bool {
429 (self.last_reorganized & reorganized_bits::ACCESS_HISTORY_CLEARED) != 0
430 }
431
432 pub fn get_last_reorganized_time(&self) -> u64 {
434 self.last_reorganized & !reorganized_bits::SPECIAL_BITS_MASK
435 }
436
437 pub fn is_offline_registry(&self) -> bool {
439 self.offline_registry.present
440 }
441
442 pub fn complete_write(&mut self) {
444 self.secondary_sequence = self.primary_sequence;
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451
452 #[test]
453 fn test_default_base_block() {
454 let block = BaseBlock::default();
455 assert_eq!(&block.signature, REGF_SIGNATURE);
456 assert_eq!(block.major_version, 1);
457 assert!(!block.is_dirty());
458 }
459
460 #[test]
461 fn test_file_name() {
462 let mut block = BaseBlock::default();
463 block.set_file_name("SYSTEM");
464 assert_eq!(block.get_file_name(), "SYSTEM");
465 }
466}
467