Skip to main content

winreg_format/
hbin.rs

1//! Hive bin (hbin) header — 32-byte container header within a hive file.
2
3use binrw::BinRead;
4
5/// Hive bin header (32 bytes). Hive bins immediately follow the 4096-byte
6/// base block and contain all cells (keys, values, security descriptors, etc.).
7///
8/// Reference: research/regf-binary-format-specification.md Section 2.1
9#[derive(Debug, Clone, BinRead)]
10#[br(little, magic = b"hbin")]
11pub struct HbinHeader {
12    /// Offset of this hbin from the start of hive bins data (NOT file start).
13    /// First hbin has offset 0.
14    pub offset: u32,
15    /// Size of this hbin in bytes (including 32-byte header). Always multiple of 4096.
16    pub size: u32,
17    /// Reserved (8 bytes). Typically zero.
18    pub reserved: u64,
19    /// FILETIME timestamp. Only meaningful for the first hbin.
20    pub timestamp: u64,
21    /// Runtime spare/memory allocation field. No meaning on disk.
22    pub spare: u32,
23}
24
25impl HbinHeader {
26    /// Size of the hbin header in bytes.
27    pub const SIZE: u32 = 32;
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use std::io::Cursor;
34
35    fn build_test_hbin(offset: u32, size: u32) -> Vec<u8> {
36        let mut buf = vec![0u8; 32];
37        buf[0..4].copy_from_slice(b"hbin");
38        buf[4..8].copy_from_slice(&offset.to_le_bytes());
39        buf[8..12].copy_from_slice(&size.to_le_bytes());
40        buf
41    }
42
43    #[test]
44    fn parse_hbin_header() {
45        let buf = build_test_hbin(0, 4096);
46        let mut cursor = Cursor::new(&buf[..]);
47        let hbin = HbinHeader::read(&mut cursor).unwrap();
48        assert_eq!(hbin.offset, 0);
49        assert_eq!(hbin.size, 4096);
50    }
51
52    #[test]
53    fn parse_second_hbin_with_offset() {
54        let buf = build_test_hbin(4096, 8192);
55        let mut cursor = Cursor::new(&buf[..]);
56        let hbin = HbinHeader::read(&mut cursor).unwrap();
57        assert_eq!(hbin.offset, 4096);
58        assert_eq!(hbin.size, 8192);
59    }
60
61    #[test]
62    fn rejects_invalid_signature() {
63        let mut buf = build_test_hbin(0, 4096);
64        buf[0..4].copy_from_slice(b"nope");
65        let mut cursor = Cursor::new(&buf[..]);
66        assert!(HbinHeader::read(&mut cursor).is_err());
67    }
68}