1use forensic_rs::{
2 err::ForensicError, notifications::NotificationType, notify_info, prelude::ForensicResult, traits::vfs::VirtualFile
3};
4
5use crate::cell::{read_cell, HiveCell};
6
7pub struct HivePrimaryFile {
8 pub base_block: BaseBlock,
9}
10
11#[derive(Debug)]
12pub struct BaseBlock {
13 pub prim_sequence: u32,
16 pub sec_sequence: u32,
20 pub last_written_timestamp: u64,
22 pub major_version: u32,
24 pub minor_version: u32,
26 pub file_type: u32,
28 pub file_format: u32,
30 pub root_cell_offset: u32,
32 pub hive_bins_data_size: u32,
34 pub clustering_factor: u32,
36 pub file_name: [u8; 64],
38 pub checksum: u32,
40 pub boot_type: u32,
42 pub boot_recovery: u32,
44}
45
46#[repr(C, packed)]
47pub struct BaseBlockWXP {
48 pub signature: u32,
50 pub prim_sequence: u32,
53 pub sec_sequence: u32,
57 pub last_written_timestamp: u64,
59 pub major_version: u32,
61 pub minor_version: u32,
63 pub file_type: u32,
65 pub file_format: u32,
67 pub root_cell_offset: u32,
69 pub hive_bins_data_size: u32,
71 pub clustering_factor: u32,
73 pub file_name: [u8; 64],
75 pub reserved: [u8; 56],
76 pub offreg_signature: u32,
78 pub offreg_flags: u32,
80 pub reserved1: [u8; 332],
81 pub checksum: u32,
83 pub serialization_timestamp: u64,
85 pub reserved2: [u8; 3568],
86 pub boot_type: u32,
88 pub boot_recovery: u32,
90}
91
92#[repr(C, packed)]
93pub struct BaseBlockW10 {
94 pub signature: u32,
96 pub prim_sequence: u32,
99 pub sec_sequence: u32,
103 pub last_written_timestamp: u64,
105 pub major_version: u32,
107 pub minor_version: u32,
109 pub file_type: u32,
111 pub file_format: u32,
113 pub root_cell_offset: u32,
115 pub hive_bins_data_size: u32,
117 pub clustering_factor: u32,
119 pub file_name: [u8; 64],
121 pub rm_id: u128,
123 pub log_id: u128,
125 pub flags: u32,
127 pub tm_id: u128,
129 pub guid_signature: u32,
131 pub last_reorganized_timestamp: u64,
133 pub offreg_signature: u32,
135 pub offreg_flags: u32,
137 pub reserved1: [u8; 324],
138 pub checksum: u32,
140 pub serialization_timestamp: u64,
142 pub reserved2: [u8; 3568],
143 pub boot_type: u32,
145 pub boot_recovery: u32,
147}
148
149#[repr(C, packed)]
150#[derive(Debug)]
151pub struct HiveBinHeader {
152 pub signature: u32,
154 pub offset: u32,
156 pub size: u32,
158 pub reserved: u64,
159 pub timestamp: u64,
161 pub spare: u32,
163}
164
165impl From<BaseBlockW10> for BaseBlock {
166 fn from(value: BaseBlockW10) -> Self {
167 BaseBlock {
168 prim_sequence: value.prim_sequence,
169 sec_sequence: value.sec_sequence,
170 last_written_timestamp: value.last_written_timestamp,
171 major_version: value.major_version,
172 minor_version: value.minor_version,
173 file_type: value.file_type,
174 file_format: value.file_format,
175 root_cell_offset: value.root_cell_offset,
176 hive_bins_data_size: value.hive_bins_data_size,
177 clustering_factor: value.clustering_factor,
178 file_name: value.file_name,
179 checksum: value.checksum,
180 boot_type: value.boot_type,
181 boot_recovery: value.boot_recovery,
182 }
183 }
184}
185impl From<&BaseBlockW10> for BaseBlock {
186 fn from(value: &BaseBlockW10) -> Self {
187 BaseBlock {
188 prim_sequence: value.prim_sequence,
189 sec_sequence: value.sec_sequence,
190 last_written_timestamp: value.last_written_timestamp,
191 major_version: value.major_version,
192 minor_version: value.minor_version,
193 file_type: value.file_type,
194 file_format: value.file_format,
195 root_cell_offset: value.root_cell_offset,
196 hive_bins_data_size: value.hive_bins_data_size,
197 clustering_factor: value.clustering_factor,
198 file_name: value.file_name,
199 checksum: value.checksum,
200 boot_type: value.boot_type,
201 boot_recovery: value.boot_recovery,
202 }
203 }
204}
205
206impl From<BaseBlockWXP> for BaseBlock {
207 fn from(value: BaseBlockWXP) -> Self {
208 BaseBlock {
209 prim_sequence: value.prim_sequence,
210 sec_sequence: value.sec_sequence,
211 last_written_timestamp: value.last_written_timestamp,
212 major_version: value.major_version,
213 minor_version: value.minor_version,
214 file_type: value.file_type,
215 file_format: value.file_format,
216 root_cell_offset: value.root_cell_offset,
217 hive_bins_data_size: value.hive_bins_data_size,
218 clustering_factor: value.clustering_factor,
219 file_name: value.file_name,
220 checksum: value.checksum,
221 boot_type: value.boot_type,
222 boot_recovery: value.boot_recovery,
223 }
224 }
225}
226impl From<&BaseBlockWXP> for BaseBlock {
227 fn from(value: &BaseBlockWXP) -> Self {
228 BaseBlock {
229 prim_sequence: value.prim_sequence,
230 sec_sequence: value.sec_sequence,
231 last_written_timestamp: value.last_written_timestamp,
232 major_version: value.major_version,
233 minor_version: value.minor_version,
234 file_type: value.file_type,
235 file_format: value.file_format,
236 root_cell_offset: value.root_cell_offset,
237 hive_bins_data_size: value.hive_bins_data_size,
238 clustering_factor: value.clustering_factor,
239 file_name: value.file_name,
240 checksum: value.checksum,
241 boot_type: value.boot_type,
242 boot_recovery: value.boot_recovery,
243 }
244 }
245}
246
247pub fn is_win10_format(buff: &[u8]) -> bool {
248 if buff.len() != 4096 {
249 return false;
250 }
251 &buff[164..168] == b"rmtm"
252}
253
254pub fn is_valid_base_block(buff: &[u8]) -> bool {
255 if buff.len() != 4096 {
256 return false;
257 }
258 &buff[0..4] == b"regf"
259}
260
261pub fn is_hive_data(buff: &[u8]) -> bool {
262 if buff.len() != 32 {
263 return false;
264 }
265 &buff[0..4] == b"hbin"
266}
267
268pub fn checksum_is_correct(buff: &[u8]) -> bool {
269 let mut c: i32 = 0;
270 for i in (0..508).step_by(4) {
271 let v = i32::from_ne_bytes(buff[i..i + 4].try_into().unwrap_or_default());
272 c ^= v;
273 }
274 if c == -1 {
275 c = -2;
276 } else if c == 0 {
277 c = -1;
278 }
279 let checksum = i32::from_ne_bytes(buff[508..512].try_into().unwrap_or_default());
280 c == checksum
281}
282
283pub enum KernelTransactionManagerFlags {
284 LockedHive = 0x00000001,
285 DefragmentedHive = 0x00000002,
286}
287
288pub fn read_base_block(file: &mut Box<dyn VirtualFile>) -> ForensicResult<BaseBlock> {
289 let mut buffer = vec![0u8; 4096];
290 file.read_exact(&mut buffer)?;
291 if !is_valid_base_block(&buffer) {
292 return Err(ForensicError::bad_format_str("Invalid base block"));
293 }
294 if !checksum_is_correct(&buffer) {
295 notify_info!(
296 NotificationType::Informational,
297 "Incorrect checksum for Hive"
298 );
299 }
300 if is_win10_format(&buffer) {
301 let (head, body, tail) = unsafe { buffer.align_to::<BaseBlockW10>() };
302 if !head.is_empty() || !tail.is_empty() {
303 return Err(ForensicError::bad_format_str("Invalid base block for Windows 10 format"));
304 }
305 let base_block = &body[0];
306 return Ok(base_block.into());
307 }
308 let (head, body, tail) = unsafe { buffer.align_to::<BaseBlockWXP>() };
309 if !head.is_empty() || !tail.is_empty() {
310 return Err(ForensicError::bad_format_str("Invalid base block for windows XP format"));
311 }
312 let base_block = &body[0];
313 Ok(base_block.into())
314}
315pub fn read_bin_header(file: &mut Box<dyn VirtualFile>) -> ForensicResult<HiveBinHeader> {
316 let mut buffer = vec![0u8; 32];
317 file.read_exact(&mut buffer)?;
318 if !is_hive_data(&buffer) {
319 return Err(ForensicError::bad_format_str("Invalid HiveBin header: signature does not match"));
320 }
321 let (head, body, tail) = unsafe { buffer.align_to::<HiveBinHeader>() };
322 if !head.is_empty() || !tail.is_empty() {
323 return Err(ForensicError::bad_format_str("Invalid HiveBin header: not a header"));
324 }
325 let header = &body[0];
326 Ok(HiveBinHeader {
327 signature: header.signature,
328 offset: header.offset,
329 size: header.size,
330 reserved: header.reserved,
331 timestamp: header.timestamp,
332 spare: header.spare,
333 })
334}
335
336pub fn read_hive_bin_at_file_position(
337 file: &mut Box<dyn VirtualFile>,
338) -> ForensicResult<(HiveBinHeader, Vec<u8>)> {
339 let initial_offset = file.stream_position().unwrap_or_default();
340 let header = match read_bin_header(file) {
341 Ok(v) => v,
342 Err(e) => {
343 file.seek(std::io::SeekFrom::Start(initial_offset))?;
344 return Err(e);
345 }
346 };
347 if header.size < 32 {
348 file.seek(std::io::SeekFrom::Start(initial_offset))?;
349 return Err(ForensicError::bad_format_str("Invalid HiveBin header: too small"));
350 }
351 let mut buff = vec![0u8; header.size as usize - 32];
352 file.read_exact(&mut buff)?;
353 Ok((header, buff))
354}
355
356pub fn read_cells(data: &[u8], bin_offset: u64) -> ForensicResult<Vec<HiveCell>> {
357 let len = data.len();
358 if len < 4 {
359 return Err(ForensicError::bad_format_str("Invalid HiveCell: too small"));
360 }
361 let mut offset = 0;
362 let mut list = Vec::with_capacity(128);
363 loop {
364 if offset > len {
365 return Err(ForensicError::bad_format_str("Invalid HiveCell: offset at position larger than the buffer"));
366 } else if offset == len {
367 break;
368 }
369 let cell_len_i =
371 i32::from_ne_bytes(data[offset..offset + 4].try_into().unwrap_or_default());
372 let cell_len = cell_len_i.unsigned_abs() as usize;
373 if offset + cell_len > len {
374 return Err(ForensicError::bad_format_str("Invalid HiveCell: end position larger than the buffer"));
375 }
376 let cell_data = &data[offset..offset + cell_len];
377 let cell = match read_cell(cell_data, bin_offset + offset as u64) {
378 Ok(v) => v,
379 Err(_e) => {
380 offset += cell_len;
381 continue;
382 }
383 };
384 list.push(cell);
385 offset += cell_len;
387 }
388 Ok(list)
389}
390
391#[cfg(test)]
392mod tst {
393 use super::*;
394 use crate::tst::*;
395 #[test]
396 fn can_read_hive_header_block() {
397 init_tst();
398 let mut fs = init_virtual_fs();
399 let mut sam_file = read_sam_hive(&mut fs);
400 let base_block = read_base_block(&mut sam_file).unwrap();
401 assert_eq!(
402 str_to_unicode_with_ending(b"\\SystemRoot\\System32\\Config\\SAM"),
403 &base_block.file_name[0..64]
404 );
405 let mut sec_file: Box<dyn VirtualFile> = read_sec_hive(&mut fs);
406 let base_block = read_base_block(&mut sec_file).unwrap();
407 assert_eq!(
408 str_to_unicode_with_ending(b"emRoot\\System32\\Config\\SECURITY"),
409 &base_block.file_name[0..64]
410 );
411 assert_no_notifications();
412 }
413
414 #[test]
415 fn can_read_sam_hive_data() {
416 init_tst();
417 let mut fs = init_virtual_fs();
418 let mut sam_file = read_sam_hive(&mut fs);
419 let base_block = read_base_block(&mut sam_file).unwrap();
420 assert_no_notifications();
421 sam_file
423 .seek(std::io::SeekFrom::Start(
424 4096 + base_block.root_cell_offset as u64 - 32,
425 ))
426 .unwrap();
427 let hive_bin = read_hive_bin_at_file_position(&mut sam_file).unwrap();
428 read_cells(&hive_bin.1, 4096 + base_block.root_cell_offset as u64 - 32).unwrap();
429 let mut i = 0;
430 let mut offset = 4096 + base_block.root_cell_offset as u64 - 32;
431 let mut cell_offset = 32;
432 loop {
433 if offset >= base_block.hive_bins_data_size.into() {
434 break;
435 }
436 i = i + 1;
437 sam_file.seek(std::io::SeekFrom::Start(offset)).unwrap();
438 let hive_bin = read_hive_bin_at_file_position(&mut sam_file).unwrap();
439 let cells = read_cells(&hive_bin.1, cell_offset).unwrap();
440 println!("{:?}", cells);
441 offset += hive_bin.0.size as u64;
442 cell_offset += hive_bin.0.size as u64;
443 }
444 }
445 #[test]
446 fn can_read_security_hive_data() {
447 init_tst();
448 let mut fs = init_virtual_fs();
449 let mut sec_file = read_sec_hive(&mut fs);
450 let base_block = read_base_block(&mut sec_file).unwrap();
451 assert_no_notifications();
452 let mut i = 0;
455 let mut offset = 4096 + base_block.root_cell_offset as u64 - 32;
456
457 loop {
458 if offset >= base_block.hive_bins_data_size.into() {
459 break;
460 }
461 i = i + 1;
462 sec_file.seek(std::io::SeekFrom::Start(offset)).unwrap();
463 let hive_bin = read_hive_bin_at_file_position(&mut sec_file).unwrap();
464 let cells = read_cells(
465 &hive_bin.1,
466 offset - 4064 - base_block.root_cell_offset as u64,
467 )
468 .unwrap();
469 println!("{:?}", cells);
470 offset += hive_bin.0.size as u64;
471 }
472 }
473
474 #[test]
475 #[ignore]
476 fn can_read_software_hive_data() {
477 init_tst();
478 let mut fs = init_virtual_fs();
479 let mut sec_file = read_software_hive(&mut fs);
480 let base_block = read_base_block(&mut sec_file).unwrap();
481 assert_no_notifications();
482 let mut i = 0;
485 let mut offset = 4096 + base_block.root_cell_offset as u64 - 32;
486
487 loop {
488 if offset >= base_block.hive_bins_data_size.into() {
489 break;
490 }
491 i = i + 1;
492 sec_file.seek(std::io::SeekFrom::Start(offset)).unwrap();
493 let hive_bin = read_hive_bin_at_file_position(&mut sec_file).unwrap();
494 let cells = read_cells(
495 &hive_bin.1,
496 offset - 4064 - base_block.root_cell_offset as u64,
497 )
498 .unwrap();
499 println!("{:?}", cells);
500 offset += hive_bin.0.size as u64;
501 }
502 }
503
504 #[test]
505 fn can_read_supersecretadmin_hive_data() {
506 init_tst();
507 let mut fs = init_virtual_fs();
508 let mut sec_file = read_supersecretadmin_hive(&mut fs);
509 let base_block = read_base_block(&mut sec_file).unwrap();
510 assert_no_notifications();
511 let mut i = 0;
514 let mut offset = 4096 + base_block.root_cell_offset as u64 - 32;
515
516 loop {
517 if offset >= base_block.hive_bins_data_size.into() {
518 break;
519 }
520 i = i + 1;
521 sec_file.seek(std::io::SeekFrom::Start(offset)).unwrap();
522 let hive_bin = read_hive_bin_at_file_position(&mut sec_file).unwrap();
523 let cells = read_cells(
524 &hive_bin.1,
525 offset - 4064 - base_block.root_cell_offset as u64,
526 )
527 .unwrap();
528 println!("{:?}", cells);
529 offset += hive_bin.0.size as u64;
530 }
531 }
532}