├── .gitignore ├── testdata ├── offreg-testhive-writer │ ├── .gitignore │ └── offreg-testhive-writer.c └── testhive ├── .github └── workflows │ └── rust.yml ├── Cargo.toml ├── src ├── helpers.rs ├── lib.rs ├── error.rs ├── key_values_list.rs ├── index_root.rs ├── subkeys_list.rs ├── leaf.rs ├── big_data.rs ├── hive.rs ├── key_value.rs ├── key_node.rs └── string.rs ├── CHANGELOG.md ├── img └── nt-hive.svg ├── README.md ├── examples └── readhive.rs └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /target 3 | **/*.rs.bk -------------------------------------------------------------------------------- /testdata/offreg-testhive-writer/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | testhive 4 | -------------------------------------------------------------------------------- /testdata/testhive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColinFinck/nt-hive/HEAD/testdata/testhive -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Rust Version 20 | run: rustc --version 21 | - name: Format 22 | run: cargo fmt --all -- --check 23 | - name: Clippy 24 | run: cargo clippy --workspace --all-targets --all-features -- -D warnings 25 | - name: Build no_std 26 | run: cargo build --workspace --no-default-features 27 | - name: Build std 28 | run: cargo build --workspace --all-features 29 | - name: Tests 30 | run: cargo test --workspace --all-features 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nt-hive" 3 | version = "0.3.0" 4 | authors = ["Colin Finck "] 5 | description = "Access keys, values, and data stored in Windows hive (registry) files" 6 | homepage = "https://github.com/ColinFinck/nt-hive" 7 | repository = "https://github.com/ColinFinck/nt-hive" 8 | documentation = "https://docs.rs/nt-hive" 9 | readme = "README.md" 10 | edition = "2021" 11 | rust-version = "1.81" 12 | license = "GPL-2.0-or-later" 13 | keywords = ["windows", "nt", "registry", "hive", "regf"] 14 | categories = ["no-std", "os::windows-apis", "parser-implementations"] 15 | 16 | [dependencies] 17 | bitflags = "2.8.0" 18 | enumn = "0.1.14" 19 | memoffset = "0.9.1" 20 | thiserror = { version = "2.0.11", default-features = false } 21 | zerocopy = { version = "0.8.14", features = ["derive"] } 22 | 23 | [features] 24 | default = ["std"] 25 | alloc = [] 26 | std = ["alloc", "thiserror/std"] 27 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::ops::Range; 5 | 6 | macro_rules! iter_try { 7 | ($e:expr) => { 8 | match $e { 9 | Ok(x) => x, 10 | Err(e) => return Some(Err(e)), 11 | } 12 | }; 13 | } 14 | 15 | /// Return a subrange of the given `Range` encompassing `byte_count` 16 | /// bytes and starting at the beginning of `range`. 17 | /// 18 | /// This function performs all necessary sanity checks to guarantee that `byte_count` 19 | /// bytes are actually available within the boundaries of the given `range`. 20 | /// If that is not the case, `None` is returned. 21 | pub(crate) fn byte_subrange(range: &Range, byte_count: usize) -> Option> { 22 | // Guard against integer overflows. 23 | let subrange_end = range.start.checked_add(byte_count)?; 24 | 25 | // Guard against exceeding the boundaries of the given range. 26 | if subrange_end > range.end { 27 | return None; 28 | } 29 | 30 | Some(range.start..subrange_end) 31 | } 32 | 33 | #[cfg(test)] 34 | pub mod tests { 35 | use std::fs::File; 36 | use std::io::Read; 37 | 38 | pub fn testhive_vec() -> Vec { 39 | let mut buffer = Vec::new(); 40 | File::open("testdata/testhive") 41 | .unwrap() 42 | .read_to_end(&mut buffer) 43 | .unwrap(); 44 | buffer 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [0.3.0] - 2025-01-21 9 | 10 | ### Added 11 | - Added `KeyNode::class_name` ([#4]). 12 | - Added printing a key's class name to the `readhive` example. 13 | - Added support for `core::error::Error`. 14 | 15 | ### Changed 16 | - Changed `KeyValue::multi_string_data` to return an iterator instead of a `Vec`. 17 | - Changed project to Rust 2021 edition and MSRV to Rust 1.81. 18 | - Replaced displaydoc dependency by thiserror. 19 | - Upgraded to bitflags 2.8.0, enumn 0.1.14, memoffset 0.9.1, zerocopy 0.8.14. 20 | 21 | ### Fixed 22 | - Fixed validating the cell size against the remaining data length. 23 | - Fixed `KeyNode::subkey` and `KeyNode::subpath` nesting the lifetimes instead of using the single lifetime of `Hive`. 24 | 25 | [#4]: https://github.com/ColinFinck/nt-hive/pull/4 26 | 27 | 28 | ## [0.2.0] - 2021-11-10 29 | 30 | ### Added 31 | - Added `Hive::without_validation` to accept hives failing header validation ([#1]). 32 | 33 | ### Changed 34 | - Updated to bitflags 1.3.2, byteorder 1.4.3, displaydoc 0.2.3, memoffset 0.6.4, zerocopy 0.6.1. 35 | 36 | ### Fixed 37 | - Fixed example in `README.md` ([#1]). 38 | - Fixed `PartialOrd` implementations for comparing `NtHiveString` and `str`. 39 | 40 | [#1]: https://github.com/ColinFinck/nt-hive/issues/1 41 | 42 | 43 | ## [0.1.0] - 2021-02-21 44 | - Initial release 45 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | //! # nt-hive 5 | //! The *nt-hive* Rust crate provides a comfortable and safe interface for accessing keys, values, and data stored in *hive* files. 6 | //! Hive files can be found in `C:\Windows\system32\config` and store what is commonly called the *Windows registry*. 7 | //! This crate supports the hive format that is used from Windows NT 4.0 up to the current Windows 10. 8 | //! 9 | //! # Getting started 10 | //! 1. Create a [`Hive`] structure from hive data by calling [`Hive::new`]. 11 | //! 2. Retrieve the root [`KeyNode`] via [`Hive::root_key_node`]. 12 | //! 3. Move to a subkey via [`KeyNode::subkey`], [`KeyNode::subkeys`] or [`KeyNode::subpath`]. 13 | //! 4. Get an interesting value using [`KeyNode::value`] or [`KeyNode::values`]. 14 | 15 | #![cfg_attr(not(feature = "std"), no_std)] 16 | #![doc(html_logo_url = "https://colinfinck.de/img/software/nt-hive.svg")] 17 | #![forbid(unsafe_code)] 18 | 19 | #[macro_use] 20 | mod helpers; 21 | 22 | mod big_data; 23 | mod error; 24 | mod hive; 25 | mod index_root; 26 | mod key_node; 27 | mod key_value; 28 | mod key_values_list; 29 | mod leaf; 30 | mod string; 31 | mod subkeys_list; 32 | 33 | pub use crate::big_data::*; 34 | pub use crate::error::*; 35 | pub use crate::hive::*; 36 | pub use crate::index_root::*; 37 | pub use crate::key_node::*; 38 | pub use crate::key_value::*; 39 | pub use crate::key_values_list::*; 40 | pub use crate::leaf::*; 41 | pub use crate::string::*; 42 | pub use crate::subkeys_list::*; 43 | 44 | #[cfg(feature = "alloc")] 45 | extern crate alloc; 46 | -------------------------------------------------------------------------------- /img/nt-hive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use thiserror::Error; 5 | 6 | use crate::key_value::KeyValueDataType; 7 | 8 | /// Central result type of nt-hive. 9 | pub type Result = core::result::Result; 10 | 11 | /// Central error type of nt-hive. 12 | #[derive(Clone, Debug, Error, Eq, PartialEq)] 13 | pub enum NtHiveError { 14 | #[error("The checksum in the base block should be {expected}, but it is {actual}")] 15 | InvalidChecksum { expected: u32, actual: u32 }, 16 | #[error("The data at offset {offset:#010x} should have a size of {expected} bytes, but it only has {actual} bytes")] 17 | InvalidDataSize { 18 | offset: usize, 19 | expected: usize, 20 | actual: usize, 21 | }, 22 | #[error("The 4-byte signature field at offset {offset:#010x} should contain {expected:?}, but it contains {actual:?}")] 23 | InvalidFourByteSignature { 24 | offset: usize, 25 | expected: &'static [u8], 26 | actual: [u8; 4], 27 | }, 28 | #[error("The struct at offset {offset:#010x} should have a size of {expected} bytes, but only {actual} bytes are left in the slice")] 29 | InvalidHeaderSize { 30 | offset: usize, 31 | expected: usize, 32 | actual: usize, 33 | }, 34 | #[error("Expected one of the key value data types {expected:?}, but found {actual:?}")] 35 | InvalidKeyValueDataType { 36 | expected: &'static [KeyValueDataType], 37 | actual: KeyValueDataType, 38 | }, 39 | #[error("The size field at offset {offset:#010x} specifies {expected} bytes, but only {actual} bytes are left in the slice")] 40 | InvalidSizeField { 41 | offset: usize, 42 | expected: usize, 43 | actual: usize, 44 | }, 45 | #[error("The size field at offset {offset:#010x} specifies {size} bytes, but they are not aligned to the expected {expected_alignment} bytes")] 46 | InvalidSizeFieldAlignment { 47 | offset: usize, 48 | size: usize, 49 | expected_alignment: usize, 50 | }, 51 | #[error("The 2-byte signature field at offset {offset:#010x} should contain {expected:?}, but it contains {actual:?}")] 52 | InvalidTwoByteSignature { 53 | offset: usize, 54 | expected: &'static [u8], 55 | actual: [u8; 2], 56 | }, 57 | #[error("The sequence numbers in the base block do not match ({primary} != {secondary})")] 58 | SequenceNumberMismatch { primary: u32, secondary: u32 }, 59 | #[error("The cell at offset {offset:#010x} with a size of {size} bytes is unallocated")] 60 | UnallocatedCell { offset: usize, size: i32 }, 61 | #[error( 62 | "The clustering factor in the base block is expected to be {expected}, but it is {actual}" 63 | )] 64 | UnsupportedClusteringFactor { expected: u32, actual: u32 }, 65 | #[error("The file format in the base block is expected to be {expected}, but it is {actual}")] 66 | UnsupportedFileFormat { expected: u32, actual: u32 }, 67 | #[error("The file type in the base block is expected to be {expected}, but it is {actual}")] 68 | UnsupportedFileType { expected: u32, actual: u32 }, 69 | #[error("The key value data type at offset {offset:#010x} is {actual:#010x}, which is not supported")] 70 | UnsupportedKeyValueDataType { offset: usize, actual: u32 }, 71 | #[error("The version in the base block ({major}.{minor}) is unsupported")] 72 | UnsupportedVersion { major: u32, minor: u32 }, 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # nt-hive 4 | 5 | [![crates.io](https://img.shields.io/crates/v/nt-hive)](https://crates.io/crates/nt-hive) 6 | [![docs.rs](https://img.shields.io/docsrs/nt-hive)](https://docs.rs/nt-hive) 7 | [![license: GPL-2.0-or-later](https://img.shields.io/crates/l/nt-hive)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) 8 | 9 | *by Colin Finck <>* 10 | 11 | The *nt-hive* Rust crate provides a comfortable and safe interface for accessing keys, values, and data stored in *hive* files. 12 | Hive files can be found in `C:\Windows\system32\config` and store what is commonly called the *Windows registry*. 13 | This crate supports the hive format that is used from Windows NT 4.0 up to the current Windows 10. 14 | 15 | nt-hive has been developed as part of a Rust bootloader project. 16 | Its current feature set is therefore aligned to the needs of a ReactOS/Windows bootloader. 17 | 18 | ## Features 19 | * Support for reading keys, values, and data from any byte slice containing hive data (i.e. anything that implements [`zerocopy::SplitByteSlice`](https://docs.rs/zerocopy/0.3.0/zerocopy/trait.ByteSlice.html)). 20 | * Basic in-memory modifications of hive data (as [required for a bootloader](https://github.com/reactos/reactos/pull/1883)). 21 | * Iterators for keys and values to enable writing idiomatic Rust code. 22 | * Functions to find a specific subkey, subkey path, or value as efficient as possible (taking advantage of binary search for keys). 23 | * Error propagation through a custom `NtHiveError` type that implements `Display`. 24 | As a bootloader may hit corrupted hive files at some point, nt-hive outputs precise errors everywhere that refer to the faulty data byte. 25 | * Full functionality even in a `no_std` environment (with `alloc`, some limitations without `alloc`). 26 | * Static borrow checking everywhere. No mutexes or runtime borrowing. 27 | * Zero-copy data representations wherever possible. 28 | * No usage of `unsafe` anywhere. Checked arithmetic where needed. 29 | * Platform and endian independence. 30 | 31 | ## Non-Goals 32 | Full write support is currently not a goal for nt-hive. 33 | This would require a wholly different architecture, where nt-hive loads a hive into linked in-memory data structures, keeps track of changes, and can write changes back to disk (possibly extending the on-disk file). 34 | The current focus on read-only access allows for a simpler architecture. 35 | 36 | ## Examples 37 | The following example reads the *List* value from the *ControlSet001\Control\ServiceGroupOrder* subkey of the *SYSTEM* hive, which is a real thing that happens during boot: 38 | 39 | ```rust,no_run 40 | let mut buffer = Vec::new(); 41 | File::open("SYSTEM").unwrap().read_to_end(&mut buffer).unwrap(); 42 | 43 | let hive = Hive::new(buffer.as_ref()).unwrap(); 44 | let root_key_node = hive.root_key_node().unwrap(); 45 | let key_node = root_key_node.subpath("ControlSet001\\Control\\ServiceGroupOrder").unwrap().unwrap(); 46 | let key_value = key_node.value("List").unwrap().unwrap(); 47 | 48 | let multi_sz_data = key_value.multi_string_data(); 49 | if let Ok(vec) = multi_sz_data { 50 | println!("Vector of REG_MULTI_SZ lines: {vec:?}"); 51 | } 52 | ``` 53 | 54 | Check out the [docs](https://docs.rs/nt-hive), the tests, and the supplied *readhive* example application for more ideas how to use nt-hive. 55 | 56 | ## Contributing and License 57 | Contributions are currently preferred in the form of bug reports. 58 | If you encounter a bug, an unexpected panic, or a potentially unsafe calculation, please [file a bug report](https://github.com/ColinFinck/nt-hive/issues). 59 | 60 | nt-hive is available under *GNU General Public License 2.0 or (at your option) any later version*. 61 | This license fits well with the projects I'm planning to use nt-hive for, and should allow integration into any open-source project. 62 | I may however put nt-hive under a more permissive license later if you [give me a good reason](mailto:colin@reactos.org). 63 | 64 | As relicensing requires permission from every contributor, I only accept code contributions that are explicitly put under [Creative Commons Zero (CC0)](https://creativecommons.org/publicdomain/zero/1.0/). 65 | If that is not an option for you, you are still very welcome to suggest your change in a bug report. 66 | 67 | ## Further Resources 68 | * [Windows registry file format specification](https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md) by [Maxim Suhanov](https://dfir.ru/) 69 | * [cmlib library](https://github.com/reactos/reactos/tree/master/sdk/lib/cmlib) by the [ReactOS Project](https://reactos.org) 70 | -------------------------------------------------------------------------------- /src/key_values_list.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::iter::FusedIterator; 5 | use core::mem; 6 | use core::ops::{Deref, Range}; 7 | 8 | use zerocopy::byteorder::LittleEndian; 9 | use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned, U32}; 10 | 11 | use crate::error::{NtHiveError, Result}; 12 | use crate::helpers::byte_subrange; 13 | use crate::hive::Hive; 14 | use crate::key_value::KeyValue; 15 | 16 | /// On-Disk Structure of a Key Values List item. 17 | #[allow(dead_code)] 18 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 19 | #[repr(packed)] 20 | struct KeyValuesListItem { 21 | key_value_offset: U32, 22 | } 23 | 24 | /// Byte range of a single Key Values list item returned by [`KeyValuesListItemRanges`]. 25 | struct KeyValuesListItemRange(Range); 26 | 27 | impl KeyValuesListItemRange { 28 | fn key_value_offset(&self, hive: &Hive) -> u32 29 | where 30 | B: SplitByteSlice, 31 | { 32 | let item = Ref::<&[u8], KeyValuesListItem>::from_bytes(&hive.data[self.0.clone()]).unwrap(); 33 | item.key_value_offset.get() 34 | } 35 | } 36 | 37 | impl Deref for KeyValuesListItemRange { 38 | type Target = Range; 39 | 40 | fn deref(&self) -> &Self::Target { 41 | &self.0 42 | } 43 | } 44 | 45 | /// Iterator over 46 | /// a contiguous range of data bytes containing Key Value items, 47 | /// returning a [`KeyValuesListItemRange`] for each item. 48 | /// 49 | /// On-Disk Signature: `vk` 50 | #[derive(Clone)] 51 | struct KeyValuesListItemRanges { 52 | items_range: Range, 53 | } 54 | 55 | impl KeyValuesListItemRanges { 56 | pub(crate) fn new( 57 | count: u32, 58 | count_field_offset: usize, 59 | cell_range: Range, 60 | ) -> Result { 61 | let byte_count = count as usize * mem::size_of::(); 62 | 63 | let items_range = byte_subrange(&cell_range, byte_count).ok_or_else(|| { 64 | NtHiveError::InvalidSizeField { 65 | offset: count_field_offset, 66 | expected: byte_count, 67 | actual: cell_range.len(), 68 | } 69 | })?; 70 | 71 | Ok(Self { items_range }) 72 | } 73 | } 74 | 75 | impl Iterator for KeyValuesListItemRanges { 76 | type Item = KeyValuesListItemRange; 77 | 78 | fn next(&mut self) -> Option { 79 | let item_range = byte_subrange(&self.items_range, mem::size_of::())?; 80 | self.items_range.start += mem::size_of::(); 81 | 82 | Some(KeyValuesListItemRange(item_range)) 83 | } 84 | 85 | fn count(self) -> usize { 86 | let (size, _) = self.size_hint(); 87 | size 88 | } 89 | 90 | fn last(mut self) -> Option { 91 | let (size, _) = self.size_hint(); 92 | if size == 0 { 93 | return None; 94 | } 95 | 96 | self.nth(size - 1) 97 | } 98 | 99 | fn nth(&mut self, n: usize) -> Option { 100 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 101 | let bytes_to_skip = n.checked_mul(mem::size_of::())?; 102 | self.items_range.start = self.items_range.start.checked_add(bytes_to_skip)?; 103 | self.next() 104 | } 105 | 106 | fn size_hint(&self) -> (usize, Option) { 107 | let size = self.items_range.len() / mem::size_of::(); 108 | (size, Some(size)) 109 | } 110 | } 111 | 112 | impl ExactSizeIterator for KeyValuesListItemRanges {} 113 | impl FusedIterator for KeyValuesListItemRanges {} 114 | 115 | /// Iterator over 116 | /// a contiguous range of data bytes containing Key Value items, 117 | /// returning a constant [`KeyValue`] for each item. 118 | /// 119 | /// On-Disk Signature: `vk` 120 | #[derive(Clone)] 121 | pub struct KeyValues<'h, B: SplitByteSlice> { 122 | hive: &'h Hive, 123 | key_values_list_item_ranges: KeyValuesListItemRanges, 124 | } 125 | 126 | impl<'h, B> KeyValues<'h, B> 127 | where 128 | B: SplitByteSlice, 129 | { 130 | pub(crate) fn new( 131 | hive: &'h Hive, 132 | count: u32, 133 | count_field_offset: usize, 134 | cell_range: Range, 135 | ) -> Result { 136 | let key_values_list_item_ranges = 137 | KeyValuesListItemRanges::new(count, count_field_offset, cell_range)?; 138 | 139 | Ok(Self { 140 | hive, 141 | key_values_list_item_ranges, 142 | }) 143 | } 144 | } 145 | 146 | impl<'h, B> Iterator for KeyValues<'h, B> 147 | where 148 | B: SplitByteSlice, 149 | { 150 | type Item = Result>; 151 | 152 | fn next(&mut self) -> Option { 153 | let key_values_list_item_range = self.key_values_list_item_ranges.next()?; 154 | let key_value_offset = key_values_list_item_range.key_value_offset(self.hive); 155 | let cell_range = iter_try!(self.hive.cell_range_from_data_offset(key_value_offset)); 156 | let key_value = iter_try!(KeyValue::new(self.hive, cell_range)); 157 | Some(Ok(key_value)) 158 | } 159 | 160 | fn count(self) -> usize { 161 | self.key_values_list_item_ranges.count() 162 | } 163 | 164 | fn last(mut self) -> Option { 165 | let (size, _) = self.size_hint(); 166 | if size == 0 { 167 | return None; 168 | } 169 | 170 | self.nth(size - 1) 171 | } 172 | 173 | fn nth(&mut self, n: usize) -> Option { 174 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 175 | let bytes_to_skip = n.checked_mul(mem::size_of::())?; 176 | self.key_values_list_item_ranges.items_range.start = self 177 | .key_values_list_item_ranges 178 | .items_range 179 | .start 180 | .checked_add(bytes_to_skip)?; 181 | self.next() 182 | } 183 | 184 | fn size_hint(&self) -> (usize, Option) { 185 | self.key_values_list_item_ranges.size_hint() 186 | } 187 | } 188 | 189 | impl ExactSizeIterator for KeyValues<'_, B> where B: SplitByteSlice {} 190 | impl FusedIterator for KeyValues<'_, B> where B: SplitByteSlice {} 191 | -------------------------------------------------------------------------------- /examples/readhive.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use std::env; 5 | use std::fs::File; 6 | use std::io::Read; 7 | 8 | use nt_hive::{Hive, KeyNode, KeyValueData, KeyValueDataType, Result}; 9 | use zerocopy::SplitByteSlice; 10 | 11 | fn main() -> Result<(), String> { 12 | // Parse arguments. 13 | let args: Vec = env::args().collect(); 14 | if args.len() < 2 { 15 | println!("Usage: readhive "); 16 | return Ok(()); 17 | } 18 | 19 | // Read the hive file. 20 | let filename = &args[1]; 21 | let mut f = File::open(filename).map_err(|e| format!("Error opening hive file: {e}"))?; 22 | let mut buffer = Vec::::new(); 23 | f.read_to_end(&mut buffer) 24 | .map_err(|e| format!("Error reading hive file: {e}"))?; 25 | 26 | // Parse the hive. 27 | let hive = Hive::new(buffer.as_ref()).map_err(|e| format!("Error parsing hive file: {e}"))?; 28 | 29 | // Print the name of the root key node. 30 | let root_key = hive 31 | .root_key_node() 32 | .map_err(|e| format!("Error getting root key: {e}"))?; 33 | println!("{}", root_key.name().unwrap().to_string_lossy()); 34 | 35 | process_subkey(root_key, 0)?; 36 | 37 | Ok(()) 38 | } 39 | 40 | fn process_subkey(key_node: KeyNode, level: usize) -> Result<(), String> 41 | where 42 | B: SplitByteSlice, 43 | { 44 | // Print the names of subkeys of this node. 45 | if let Some(subkeys) = key_node.subkeys() { 46 | let subkeys = subkeys.map_err(|e| format!("Error getting subkeys: {e}"))?; 47 | 48 | for key_node in subkeys { 49 | let key_node = key_node.map_err(|e| format!("Error enumerating key: {e}"))?; 50 | let key_name = key_node 51 | .name() 52 | .map_err(|e| format!("Error getting key name: {e}"))?; 53 | 54 | print_indentation(level); 55 | println!("● {key_name}"); 56 | 57 | if let Some(class_name) = key_node.class_name() { 58 | let class_name = 59 | class_name.map_err(|e| format!("Error getting class name: {e}"))?; 60 | print_indentation(level); 61 | println!(" Class Name: {class_name}"); 62 | } 63 | 64 | // Print the names of the values of this node. 65 | if let Some(value_iter) = key_node.values() { 66 | let value_iter = 67 | value_iter.map_err(|e| format!("Error creating value iterator: {e}"))?; 68 | 69 | for value in value_iter { 70 | let value = value.map_err(|e| format!("Error enumerating value: {e}"))?; 71 | 72 | let mut value_name = value 73 | .name() 74 | .map_err(|e| format!("Error getting value name: {e}"))? 75 | .to_string_lossy(); 76 | if value_name.is_empty() { 77 | value_name.push_str("(Default)"); 78 | } 79 | 80 | let value_type = value 81 | .data_type() 82 | .map_err(|e| format!("Error getting value type: {e}"))?; 83 | 84 | let data_size = value.data_size(); 85 | 86 | // First line: Value Name, Data Type, and Data Size 87 | print_indentation(level); 88 | println!(" ○ {value_name} - {value_type:?} - {data_size}"); 89 | 90 | // Second line: The actual Value Data 91 | print_indentation(level); 92 | print!(" "); 93 | 94 | match value_type { 95 | KeyValueDataType::RegSZ | KeyValueDataType::RegExpandSZ => { 96 | let string_data = value 97 | .string_data() 98 | .map_err(|e| format!("Error getting string data: {e}"))?; 99 | println!("{string_data}") 100 | } 101 | KeyValueDataType::RegBinary => { 102 | let binary_data = value 103 | .data() 104 | .map_err(|e| format!("Error getting binary data: {e}"))?; 105 | match binary_data { 106 | KeyValueData::Small(data) => println!("{data:?}"), 107 | KeyValueData::Big(_iter) => println!("BIG DATA"), 108 | } 109 | } 110 | KeyValueDataType::RegDWord | KeyValueDataType::RegDWordBigEndian => { 111 | let dword_data = value 112 | .dword_data() 113 | .map_err(|e| format!("Error getting DWORD data: {e}"))?; 114 | println!("{dword_data}") 115 | } 116 | KeyValueDataType::RegMultiSZ => { 117 | let multi_string_data = value 118 | .multi_string_data() 119 | .map_err(|e| format!("Error getting multi string data: {e}"))? 120 | .collect::>>() 121 | .map_err(|e| { 122 | format!("Error getting multi string element data: {e}") 123 | })?; 124 | println!("{multi_string_data:?}") 125 | } 126 | KeyValueDataType::RegQWord => { 127 | let qword_data = value 128 | .qword_data() 129 | .map_err(|e| format!("Error getting QWORD data: {e}"))?; 130 | println!("{qword_data}") 131 | } 132 | _ => println!(), 133 | } 134 | } 135 | } 136 | 137 | // Process subkeys. 138 | process_subkey(key_node, level + 1)?; 139 | } 140 | } 141 | 142 | Ok(()) 143 | } 144 | 145 | fn print_indentation(level: usize) { 146 | for _i in 0..level { 147 | print!(" "); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/index_root.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::iter::FusedIterator; 5 | use core::mem; 6 | use core::ops::{Deref, Range}; 7 | 8 | use zerocopy::byteorder::LittleEndian; 9 | use zerocopy::{ 10 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, 11 | Unaligned, U32, 12 | }; 13 | 14 | use crate::error::{NtHiveError, Result}; 15 | use crate::helpers::byte_subrange; 16 | use crate::hive::Hive; 17 | use crate::key_node::{KeyNode, KeyNodeMut}; 18 | use crate::leaf::LeafItemRanges; 19 | 20 | /// On-Disk Structure of a single Index Root item. 21 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 22 | #[repr(packed)] 23 | struct IndexRootItem { 24 | subkeys_list_offset: U32, 25 | } 26 | 27 | /// Byte range of a single Index Root item returned by [`IndexRootItemRanges`]. 28 | pub(crate) struct IndexRootItemRange(Range); 29 | 30 | impl IndexRootItemRange { 31 | pub fn subkeys_list_offset(&self, hive: &Hive) -> u32 32 | where 33 | B: SplitByteSlice, 34 | { 35 | let item = Ref::<&[u8], IndexRootItem>::from_bytes(&hive.data[self.0.clone()]).unwrap(); 36 | item.subkeys_list_offset.get() 37 | } 38 | } 39 | 40 | impl Deref for IndexRootItemRange { 41 | type Target = Range; 42 | 43 | fn deref(&self) -> &Self::Target { 44 | &self.0 45 | } 46 | } 47 | 48 | /// Iterator over 49 | /// a contiguous range of data bytes containing Index Root items, 50 | /// returning an [`IndexRootItemRange`] for each Index Root item. 51 | /// 52 | /// On-Disk Signature: `ri` 53 | #[derive(Clone)] 54 | pub(crate) struct IndexRootItemRanges { 55 | items_range: Range, 56 | } 57 | 58 | impl IndexRootItemRanges { 59 | fn new(count: u16, count_field_offset: usize, data_range: Range) -> Result { 60 | let byte_count = count as usize * mem::size_of::(); 61 | 62 | let items_range = byte_subrange(&data_range, byte_count).ok_or_else(|| { 63 | NtHiveError::InvalidSizeField { 64 | offset: count_field_offset, 65 | expected: byte_count, 66 | actual: data_range.len(), 67 | } 68 | })?; 69 | 70 | Ok(Self { items_range }) 71 | } 72 | } 73 | 74 | impl Iterator for IndexRootItemRanges { 75 | type Item = IndexRootItemRange; 76 | 77 | fn next(&mut self) -> Option { 78 | let item_range = byte_subrange(&self.items_range, mem::size_of::())?; 79 | self.items_range.start += mem::size_of::(); 80 | 81 | Some(IndexRootItemRange(item_range)) 82 | } 83 | 84 | fn count(self) -> usize { 85 | let (size, _) = self.size_hint(); 86 | size 87 | } 88 | 89 | fn last(mut self) -> Option { 90 | let (size, _) = self.size_hint(); 91 | if size == 0 { 92 | return None; 93 | } 94 | 95 | self.nth(size - 1) 96 | } 97 | 98 | fn nth(&mut self, n: usize) -> Option { 99 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 100 | let bytes_to_skip = n.checked_mul(mem::size_of::())?; 101 | self.items_range.start = self.items_range.start.checked_add(bytes_to_skip)?; 102 | self.next() 103 | } 104 | 105 | fn size_hint(&self) -> (usize, Option) { 106 | let size = self.items_range.len() / mem::size_of::(); 107 | (size, Some(size)) 108 | } 109 | } 110 | 111 | impl ExactSizeIterator for IndexRootItemRanges {} 112 | impl FusedIterator for IndexRootItemRanges {} 113 | 114 | impl From> for IndexRootItemRanges { 115 | fn from(index_root_key_nodes: IndexRootKeyNodes<'_, B>) -> IndexRootItemRanges { 116 | index_root_key_nodes.index_root_item_ranges 117 | } 118 | } 119 | 120 | /// Iterator over 121 | /// a contiguous range of data bytes containing Index Root items, 122 | /// returning a constant [`KeyNode`] for each Leaf item of each Index Root item, 123 | /// used by [`SubKeyNodes`] 124 | /// 125 | /// On-Disk Signature: `ri` 126 | /// 127 | /// [`SubKeyNodes`]: crate::subkeys_list::SubKeyNodes 128 | #[derive(Clone)] 129 | pub struct IndexRootKeyNodes<'h, B: SplitByteSlice> { 130 | hive: &'h Hive, 131 | index_root_item_ranges: IndexRootItemRanges, 132 | leaf_item_ranges: Option, 133 | } 134 | 135 | impl<'h, B> IndexRootKeyNodes<'h, B> 136 | where 137 | B: SplitByteSlice, 138 | { 139 | pub(crate) fn new( 140 | hive: &'h Hive, 141 | count: u16, 142 | count_field_offset: usize, 143 | data_range: Range, 144 | ) -> Result { 145 | let index_root_item_ranges = 146 | IndexRootItemRanges::new(count, count_field_offset, data_range)?; 147 | 148 | Ok(Self { 149 | hive, 150 | index_root_item_ranges, 151 | leaf_item_ranges: None, 152 | }) 153 | } 154 | } 155 | 156 | impl<'h, B> Iterator for IndexRootKeyNodes<'h, B> 157 | where 158 | B: SplitByteSlice, 159 | { 160 | type Item = Result>; 161 | 162 | fn next(&mut self) -> Option { 163 | loop { 164 | if let Some(leaf_item_ranges) = self.leaf_item_ranges.as_mut() { 165 | if let Some(leaf_item_range) = leaf_item_ranges.next() { 166 | let key_node = 167 | iter_try!(KeyNode::from_leaf_item_range(self.hive, leaf_item_range)); 168 | return Some(Ok(key_node)); 169 | } 170 | } 171 | 172 | // No leaf_item_ranges or the last one has been fully iterated. 173 | // So get the next Index Root item and build leaf_item_ranges out of that. 174 | let index_root_item_range = self.index_root_item_ranges.next()?; 175 | let leaf_item_ranges = iter_try!(LeafItemRanges::from_index_root_item_range( 176 | self.hive, 177 | index_root_item_range 178 | )); 179 | self.leaf_item_ranges = Some(leaf_item_ranges); 180 | } 181 | } 182 | } 183 | 184 | impl FusedIterator for IndexRootKeyNodes<'_, B> where B: SplitByteSlice {} 185 | 186 | /// Iterator over 187 | /// a contiguous range of data bytes containing Index Root items, 188 | /// returning a mutable [`KeyNode`] for each Leaf item of each Index Root item, 189 | /// used by [`SubKeyNodesMut`]. 190 | /// 191 | /// On-Disk Signature: `ri` 192 | /// 193 | /// [`SubKeyNodesMut`]: crate::subkeys_list::SubKeyNodesMut 194 | pub(crate) struct IndexRootKeyNodesMut<'h, B: SplitByteSliceMut> { 195 | hive: &'h mut Hive, 196 | index_root_item_ranges: IndexRootItemRanges, 197 | leaf_item_ranges: Option, 198 | } 199 | 200 | impl<'h, B> IndexRootKeyNodesMut<'h, B> 201 | where 202 | B: SplitByteSliceMut, 203 | { 204 | pub(crate) fn new( 205 | hive: &'h mut Hive, 206 | count: u16, 207 | count_field_offset: usize, 208 | data_range: Range, 209 | ) -> Result { 210 | let index_root_item_ranges = 211 | IndexRootItemRanges::new(count, count_field_offset, data_range)?; 212 | 213 | Ok(Self { 214 | hive, 215 | index_root_item_ranges, 216 | leaf_item_ranges: None, 217 | }) 218 | } 219 | 220 | pub(crate) fn next<'a>(&'a mut self) -> Option>> 221 | where 222 | 'h: 'a, 223 | { 224 | loop { 225 | if let Some(leaf_item_ranges) = self.leaf_item_ranges.as_mut() { 226 | if let Some(leaf_item_range) = leaf_item_ranges.next() { 227 | let key_node = 228 | iter_try!(KeyNodeMut::from_leaf_item_range(self.hive, leaf_item_range)); 229 | return Some(Ok(key_node)); 230 | } 231 | } 232 | 233 | // No leaf_item_ranges or the last one has been fully iterated. 234 | // So get the next Index Root item and build leaf_item_ranges out of that. 235 | let index_root_item_range = self.index_root_item_ranges.next()?; 236 | let leaf_item_ranges = iter_try!(LeafItemRanges::from_index_root_item_range( 237 | self.hive, 238 | index_root_item_range 239 | )); 240 | self.leaf_item_ranges = Some(leaf_item_ranges); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/subkeys_list.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::iter::FusedIterator; 5 | use core::mem; 6 | use core::ops::Range; 7 | 8 | use zerocopy::byteorder::LittleEndian; 9 | use zerocopy::{ 10 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, 11 | Unaligned, U16, 12 | }; 13 | 14 | use crate::error::{NtHiveError, Result}; 15 | use crate::helpers::byte_subrange; 16 | use crate::hive::Hive; 17 | use crate::index_root::{IndexRootKeyNodes, IndexRootKeyNodesMut}; 18 | use crate::key_node::{KeyNode, KeyNodeMut}; 19 | use crate::leaf::{LeafKeyNodes, LeafKeyNodesMut, LeafType}; 20 | 21 | /// On-Disk Structure of a Subkeys List header. 22 | /// This is common for all subkey types (Fast Leaf, Hash Leaf, Index Leaf, Index Root). 23 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 24 | #[repr(packed)] 25 | pub(crate) struct SubkeysListHeader { 26 | pub(crate) signature: [u8; 2], 27 | pub(crate) count: U16, 28 | } 29 | 30 | /// Subkeys of a single [`KeyNode`]. 31 | /// 32 | /// A Subkeys List generalizes over all structures used to manage subkeys. 33 | /// These are: Fast Leaf (`lf`), Hash Leaf (`lh`), Index Leaf (`li`), Index Root (`ri`). 34 | pub(crate) struct SubkeysList<'h, B: SplitByteSlice> { 35 | hive: &'h Hive, 36 | header_range: Range, 37 | pub(crate) data_range: Range, 38 | } 39 | 40 | impl<'h, B> SubkeysList<'h, B> 41 | where 42 | B: SplitByteSlice, 43 | { 44 | pub(crate) fn new(hive: &'h Hive, cell_range: Range) -> Result { 45 | Self::new_internal(hive, cell_range, true) 46 | } 47 | 48 | pub(crate) fn new_without_index_root( 49 | hive: &'h Hive, 50 | cell_range: Range, 51 | ) -> Result { 52 | // This function only exists to share validation code with `LeafItemRanges`. 53 | Self::new_internal(hive, cell_range, false) 54 | } 55 | 56 | fn new_internal( 57 | hive: &'h Hive, 58 | cell_range: Range, 59 | index_root_supported: bool, 60 | ) -> Result { 61 | let header_range = byte_subrange(&cell_range, mem::size_of::()) 62 | .ok_or_else(|| NtHiveError::InvalidHeaderSize { 63 | offset: hive.offset_of_data_offset(cell_range.start), 64 | expected: mem::size_of::(), 65 | actual: cell_range.len(), 66 | })?; 67 | let data_range = header_range.end..cell_range.end; 68 | 69 | let subkeys_list = Self { 70 | hive, 71 | header_range, 72 | data_range, 73 | }; 74 | subkeys_list.validate_signature(index_root_supported)?; 75 | 76 | Ok(subkeys_list) 77 | } 78 | 79 | pub(crate) fn header(&self) -> Ref<&[u8], SubkeysListHeader> { 80 | Ref::from_bytes(&self.hive.data[self.header_range.clone()]).unwrap() 81 | } 82 | 83 | fn validate_signature(&self, index_root_supported: bool) -> Result<()> { 84 | let header = self.header(); 85 | 86 | match &header.signature { 87 | // Index Leaf / Fast Leaf / Hash Leaf 88 | b"lf" | b"lh" | b"li" => return Ok(()), 89 | 90 | // Index Root 91 | b"ri" => { 92 | if index_root_supported { 93 | return Ok(()); 94 | } 95 | } 96 | 97 | // Anything else 98 | _ => (), 99 | } 100 | 101 | let expected_signature: &[u8] = if index_root_supported { 102 | b"lf|lh|li|ri" 103 | } else { 104 | b"lf|lh|li" 105 | }; 106 | 107 | Err(NtHiveError::InvalidTwoByteSignature { 108 | offset: self.hive.offset_of_field(&header.signature), 109 | expected: expected_signature, 110 | actual: header.signature, 111 | }) 112 | } 113 | } 114 | 115 | /// Iterator over 116 | /// all subkeys of a [`KeyNode`], 117 | /// returning a constant [`KeyNode`] for each subkey. 118 | /// 119 | /// This iterator combines [`IndexRootKeyNodes`] and [`LeafKeyNodes`]. 120 | /// Refer to them for a more technical documentation. 121 | /// 122 | /// On-Disk Signatures: `lf`, `lh`, `li`, `ri` 123 | #[derive(Clone)] 124 | pub enum SubKeyNodes<'h, B: SplitByteSlice> { 125 | IndexRoot(IndexRootKeyNodes<'h, B>), 126 | Leaf(LeafKeyNodes<'h, B>), 127 | } 128 | 129 | impl<'h, B> SubKeyNodes<'h, B> 130 | where 131 | B: SplitByteSlice, 132 | { 133 | pub(crate) fn new(hive: &'h Hive, cell_range: Range) -> Result { 134 | let subkeys_list = SubkeysList::new(hive, cell_range)?; 135 | let header = subkeys_list.header(); 136 | let signature = header.signature; 137 | let count = header.count.get(); 138 | let count_field_offset = subkeys_list.hive.offset_of_field(&header.count); 139 | let data_range = subkeys_list.data_range; 140 | 141 | match &signature { 142 | b"lf" | b"lh" | b"li" => { 143 | // Fast Leaf, Hash Leaf or Index Leaf 144 | let leaf_type = LeafType::from_signature(&signature).unwrap(); 145 | let iter = 146 | LeafKeyNodes::new(hive, count, count_field_offset, data_range, leaf_type)?; 147 | Ok(Self::Leaf(iter)) 148 | } 149 | b"ri" => { 150 | // Index Root 151 | let iter = IndexRootKeyNodes::new(hive, count, count_field_offset, data_range)?; 152 | Ok(Self::IndexRoot(iter)) 153 | } 154 | _ => unreachable!(), 155 | } 156 | } 157 | } 158 | 159 | impl<'h, B> Iterator for SubKeyNodes<'h, B> 160 | where 161 | B: SplitByteSlice, 162 | { 163 | type Item = Result>; 164 | 165 | fn next(&mut self) -> Option { 166 | match self { 167 | Self::IndexRoot(iter) => iter.next(), 168 | Self::Leaf(iter) => iter.next(), 169 | } 170 | } 171 | 172 | fn count(self) -> usize { 173 | match self { 174 | Self::IndexRoot(iter) => iter.count(), 175 | Self::Leaf(iter) => iter.count(), 176 | } 177 | } 178 | 179 | fn last(self) -> Option { 180 | match self { 181 | Self::IndexRoot(iter) => iter.last(), 182 | Self::Leaf(iter) => iter.last(), 183 | } 184 | } 185 | 186 | fn nth(&mut self, n: usize) -> Option { 187 | match self { 188 | Self::IndexRoot(iter) => iter.nth(n), 189 | Self::Leaf(iter) => iter.nth(n), 190 | } 191 | } 192 | 193 | fn size_hint(&self) -> (usize, Option) { 194 | match self { 195 | Self::IndexRoot(iter) => iter.size_hint(), 196 | Self::Leaf(iter) => iter.size_hint(), 197 | } 198 | } 199 | } 200 | 201 | impl FusedIterator for SubKeyNodes<'_, B> where B: SplitByteSlice {} 202 | 203 | /// Iterator over 204 | /// all subkeys of a [`KeyNode`], 205 | /// returning a mutable [`KeyNode`] for each subkey. 206 | /// 207 | /// This iterator combines [`IndexRootKeyNodesMut`] and [`LeafKeyNodesMut`]. 208 | /// Refer to them for a more technical documentation. 209 | /// 210 | /// On-Disk Signatures: `lf`, `lh`, `li`, `ri` 211 | pub(crate) enum SubKeyNodesMut<'h, B: SplitByteSliceMut> { 212 | IndexRoot(IndexRootKeyNodesMut<'h, B>), 213 | Leaf(LeafKeyNodesMut<'h, B>), 214 | } 215 | 216 | impl<'h, B> SubKeyNodesMut<'h, B> 217 | where 218 | B: SplitByteSliceMut, 219 | { 220 | pub(crate) fn new(hive: &'h mut Hive, cell_range: Range) -> Result { 221 | let subkeys_list = SubkeysList::new(&*hive, cell_range)?; 222 | let header = subkeys_list.header(); 223 | let signature = header.signature; 224 | let count = header.count.get(); 225 | let count_field_offset = subkeys_list.hive.offset_of_field(&header.count); 226 | let data_range = subkeys_list.data_range; 227 | 228 | match &signature { 229 | b"lf" | b"lh" | b"li" => { 230 | // Fast Leaf, Hash Leaf or Index Leaf 231 | let leaf_type = LeafType::from_signature(&signature).unwrap(); 232 | let iter = 233 | LeafKeyNodesMut::new(hive, count, count_field_offset, data_range, leaf_type)?; 234 | Ok(Self::Leaf(iter)) 235 | } 236 | b"ri" => { 237 | // Index Root 238 | let iter = IndexRootKeyNodesMut::new(hive, count, count_field_offset, data_range)?; 239 | Ok(Self::IndexRoot(iter)) 240 | } 241 | _ => unreachable!(), 242 | } 243 | } 244 | 245 | pub fn next(&mut self) -> Option>> { 246 | match self { 247 | Self::IndexRoot(iter) => iter.next(), 248 | Self::Leaf(iter) => iter.next(), 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /testdata/offreg-testhive-writer/offreg-testhive-writer.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2023 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | // 4 | // Windows tool to generate "testhive" using the Offline Registry Library shipped with Windows Vista and newer. 5 | // Build with `cl offreg-testhive-writer.c` in a Visual Studio Command Prompt. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | static const WCHAR wszOutputFileName[] = L"testhive"; 12 | 13 | // Not everyone has "offreg.h"... 14 | typedef PVOID ORHKEY, *PORHKEY; 15 | typedef DWORD (WINAPI* ORCLOSEKEY)(ORHKEY Handle); 16 | typedef DWORD (WINAPI* ORCREATEHIVE)(PORHKEY phkResult); 17 | typedef DWORD (WINAPI* ORCREATEKEY)(ORHKEY Handle, PCWSTR lpSubKey, PWSTR lpClass, DWORD dwOptions, PSECURITY_DESCRIPTOR pSecurityDescriptor, PORHKEY phkResult, PDWORD pdwDisposition); 18 | typedef DWORD (WINAPI* ORSAVEHIVE)(ORHKEY Handle, PCWSTR lpHivePath, DWORD dwOsMajorVersion, DWORD dwOsMinorVersion); 19 | typedef DWORD (WINAPI* ORSETVALUE)(ORHKEY Handle, PCWSTR lpValueName, DWORD dwType, const BYTE* lpData, DWORD cbData); 20 | 21 | static ORCLOSEKEY pfnORCloseKey; 22 | static ORCREATEHIVE pfnORCreateHive; 23 | static ORCREATEKEY pfnORCreateKey; 24 | static ORSAVEHIVE pfnORSaveHive; 25 | static ORSETVALUE pfnORSetValue; 26 | 27 | 28 | static bool _GetOffregFunctions(void) 29 | { 30 | HANDLE hOffreg = LoadLibraryW(L"offreg"); 31 | if (!hOffreg) 32 | { 33 | fprintf(stderr, "Could not load offreg.dll.\n"); 34 | return false; 35 | } 36 | 37 | pfnORCloseKey = (ORCLOSEKEY)GetProcAddress(hOffreg, "ORCloseKey"); 38 | pfnORCreateHive = (ORCREATEHIVE)GetProcAddress(hOffreg, "ORCreateHive"); 39 | pfnORCreateKey = (ORCREATEKEY)GetProcAddress(hOffreg, "ORCreateKey"); 40 | pfnORSaveHive = (ORSAVEHIVE)GetProcAddress(hOffreg, "ORSaveHive"); 41 | pfnORSetValue = (ORSETVALUE)GetProcAddress(hOffreg, "ORSetValue"); 42 | 43 | return true; 44 | } 45 | 46 | static void _WriteBigDataTest(ORHKEY hKey) 47 | { 48 | BYTE TestData[16345]; 49 | 50 | // This value should still fit into a single cell and not require Big Data. 51 | memset(TestData, 'A', 16343); 52 | pfnORSetValue(hKey, L"A", REG_BINARY, TestData, 16343); 53 | 54 | // Same for this one, but we're touching the threshold here. 55 | memset(TestData, 'B', 16344); 56 | pfnORSetValue(hKey, L"B", REG_BINARY, TestData, 16344); 57 | 58 | // This one must finally generate a Big Data structure. 59 | memset(TestData, 'C', 16345); 60 | pfnORSetValue(hKey, L"C", REG_BINARY, TestData, 16345); 61 | } 62 | 63 | static void _WriteCharacterEncodingTest(ORHKEY hKey) 64 | { 65 | ORHKEY hSubKey; 66 | 67 | // Prove that Latin1 characters are always stored with 1 byte per character by adding some German umlauts. 68 | pfnORCreateKey(hKey, L"\u00e4\u00f6\u00fc", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 69 | pfnORCloseKey(hSubKey); 70 | 71 | // Prove that all characters of the Unicode Basic Multilingual Plane are compared case-insensitively 72 | // by trying to add both "Full-Width Uppercase A" and "Full-Width Lowercase A", 73 | // and ending up with just one of them. 74 | pfnORCreateKey(hKey, L"\uff21", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 75 | pfnORCloseKey(hSubKey); 76 | pfnORCreateKey(hKey, L"\uff41", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 77 | pfnORCloseKey(hSubKey); 78 | 79 | // Prove that this isn't the case outside the Unicode Basic Multilingual Plane 80 | // by adding "Deseret Uppercase H" and "Deseret Lowercase H". 81 | pfnORCreateKey(hKey, L"\U00010410", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 82 | pfnORCloseKey(hSubKey); 83 | pfnORCreateKey(hKey, L"\U00010438", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 84 | pfnORCloseKey(hSubKey); 85 | } 86 | 87 | static void _WriteDataTest(ORHKEY hKey) 88 | { 89 | // REG_SZ and REG_EXPAND_SZ 90 | const WCHAR StringTestData[] = L"sz-test"; 91 | pfnORSetValue(hKey, L"reg-sz", REG_SZ, (const BYTE*)StringTestData, wcslen(StringTestData) * sizeof(WCHAR)); 92 | pfnORSetValue(hKey, L"reg-sz-with-terminating-nul", REG_SZ, (const BYTE*)StringTestData, sizeof(StringTestData)); 93 | pfnORSetValue(hKey, L"reg-expand-sz", REG_EXPAND_SZ, (const BYTE*)StringTestData, wcslen(StringTestData) * sizeof(WCHAR)); 94 | 95 | // REG_MULTI_SZ with small value data. 96 | const WCHAR MultiStringTestData[] = L"multi-sz-test\0line2\0"; 97 | pfnORSetValue(hKey, L"reg-multi-sz", REG_MULTI_SZ, (const BYTE*)MultiStringTestData, sizeof(MultiStringTestData)); 98 | 99 | // REG_MULTI_SZ with big value data. 100 | // First line is 820 times the "0123456789" sequence. 101 | // Second line is a single "0123456789" string. 102 | // Tests whether nt-hive can concatenate a string that is split between big data slice boundaries. 103 | const WCHAR Sequence[] = L"0123456789"; 104 | const size_t SequenceLength = 10; 105 | const size_t SequenceRepeats = 820; 106 | const size_t ElementCount = SequenceRepeats * SequenceLength + 1 + SequenceLength + 1 + 1; 107 | PWSTR pMultiStringBigTestData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ElementCount * sizeof(WCHAR)); 108 | 109 | for (size_t i = 0; i < SequenceRepeats; i++) 110 | { 111 | CopyMemory(&pMultiStringBigTestData[i * SequenceLength], Sequence, SequenceLength * sizeof(WCHAR)); 112 | } 113 | 114 | CopyMemory(&pMultiStringBigTestData[SequenceRepeats * SequenceLength + 1], Sequence, SequenceLength * sizeof(WCHAR)); 115 | pfnORSetValue(hKey, L"reg-multi-sz-big", REG_MULTI_SZ, (const BYTE*)pMultiStringBigTestData, ElementCount * sizeof(WCHAR)); 116 | HeapFree(GetProcessHeap(), 0, pMultiStringBigTestData); 117 | 118 | // REG_DWORD and REG_DWORD_BIG_ENDIAN 119 | const DWORD DwordTestData = 42; 120 | pfnORSetValue(hKey, L"dword", REG_DWORD, (const BYTE*)&DwordTestData, sizeof(DwordTestData)); 121 | pfnORSetValue(hKey, L"dword-big-endian", REG_DWORD_BIG_ENDIAN, (const BYTE*)&DwordTestData, sizeof(DwordTestData)); 122 | 123 | // REG_QWORD 124 | const ULONGLONG QwordTestData = (ULONGLONG)-1; 125 | pfnORSetValue(hKey, L"qword", REG_QWORD, (const BYTE*)&QwordTestData, sizeof(QwordTestData)); 126 | 127 | // REG_BINARY 128 | const BYTE BinaryTestData[] = {1, 2, 3, 4, 5}; 129 | pfnORSetValue(hKey, L"binary", REG_BINARY, BinaryTestData, sizeof(BinaryTestData)); 130 | } 131 | 132 | static void _WriteSubkeyTest(ORHKEY hKey) 133 | { 134 | ORHKEY hSubKey; 135 | WCHAR wszKeyName[16]; 136 | 137 | // Create enough subkeys for the Offline Registry Library to generate an Index Root. 138 | for (int i = 0; i < 512; i++) 139 | { 140 | // Prove that we can find all subkeys no matter the letter case. 141 | const char FirstLetter = (i % 2 == 0) ? 'K' : 'k'; 142 | 143 | swprintf_s(wszKeyName, _countof(wszKeyName), L"%cey%d", FirstLetter, i); 144 | pfnORCreateKey(hKey, wszKeyName, NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 145 | pfnORCloseKey(hSubKey); 146 | } 147 | } 148 | 149 | static void _WriteSubpathTest(ORHKEY hKey) 150 | { 151 | ORHKEY hSubKey1, hSubKey2, hSubKey3; 152 | 153 | pfnORCreateKey(hKey, L"no-subkeys", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey1, NULL); 154 | pfnORCloseKey(hSubKey1); 155 | 156 | pfnORCreateKey(hKey, L"with-single-level-subkey", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey1, NULL); 157 | pfnORCreateKey(hSubKey1, L"subkey", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey2, NULL); 158 | pfnORCloseKey(hSubKey2); 159 | pfnORCloseKey(hSubKey1); 160 | 161 | pfnORCreateKey(hKey, L"with-two-levels-of-subkeys", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey1, NULL); 162 | pfnORCreateKey(hSubKey1, L"subkey1", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey2, NULL); 163 | pfnORCreateKey(hSubKey2, L"subkey2", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey3, NULL); 164 | pfnORCloseKey(hSubKey3); 165 | pfnORCloseKey(hSubKey2); 166 | pfnORCloseKey(hSubKey1); 167 | } 168 | 169 | static bool _WriteTestHive(void) 170 | { 171 | ORHKEY hRoot, hSubKey; 172 | pfnORCreateHive(&hRoot); 173 | 174 | pfnORCreateKey(hRoot, L"big-data-test", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 175 | _WriteBigDataTest(hSubKey); 176 | pfnORCloseKey(hSubKey); 177 | 178 | pfnORCreateKey(hRoot, L"character-encoding-test", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 179 | _WriteCharacterEncodingTest(hSubKey); 180 | pfnORCloseKey(hSubKey); 181 | 182 | pfnORCreateKey(hRoot, L"data-test", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 183 | _WriteDataTest(hSubKey); 184 | pfnORCloseKey(hSubKey); 185 | 186 | pfnORCreateKey(hRoot, L"subkey-test", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 187 | _WriteSubkeyTest(hSubKey); 188 | pfnORCloseKey(hSubKey); 189 | 190 | pfnORCreateKey(hRoot, L"subpath-test", NULL, REG_OPTION_NON_VOLATILE, NULL, &hSubKey, NULL); 191 | _WriteSubpathTest(hSubKey); 192 | pfnORCloseKey(hSubKey); 193 | 194 | // Rewrite the hive file. 195 | DeleteFileW(wszOutputFileName); 196 | DWORD dwErrorCode = pfnORSaveHive(hRoot, wszOutputFileName, 6, 1); 197 | pfnORCloseKey(hRoot); 198 | 199 | if (dwErrorCode != ERROR_SUCCESS) 200 | { 201 | fprintf(stderr, "ORSaveHive failed with error %lu.\n", dwErrorCode); 202 | return false; 203 | } 204 | 205 | return true; 206 | } 207 | 208 | int main() 209 | { 210 | if (!_GetOffregFunctions()) 211 | { 212 | return 1; 213 | } 214 | 215 | if (!_WriteTestHive()) 216 | { 217 | return 1; 218 | } 219 | 220 | return 0; 221 | } 222 | -------------------------------------------------------------------------------- /src/leaf.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::iter::FusedIterator; 5 | use core::mem; 6 | use core::ops::{Deref, Range}; 7 | 8 | use zerocopy::byteorder::LittleEndian; 9 | use zerocopy::{ 10 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, 11 | Unaligned, U32, 12 | }; 13 | 14 | use crate::error::{NtHiveError, Result}; 15 | use crate::helpers::byte_subrange; 16 | use crate::hive::Hive; 17 | use crate::index_root::IndexRootItemRange; 18 | use crate::key_node::{KeyNode, KeyNodeMut}; 19 | use crate::subkeys_list::SubkeysList; 20 | 21 | /// On-Disk Structure of a Fast Leaf item (On-Disk Signature: `lf`). 22 | /// They are supported since Windows NT 4. 23 | #[allow(dead_code)] 24 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 25 | #[repr(packed)] 26 | struct FastLeafItem { 27 | key_node_offset: U32, 28 | name_hint: [u8; 4], 29 | } 30 | 31 | /// On-Disk Structure of a Hash Leaf item (On-Disk Signature: `lh`). 32 | /// They are supported since Windows XP. 33 | #[allow(dead_code)] 34 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 35 | #[repr(packed)] 36 | struct HashLeafItem { 37 | key_node_offset: U32, 38 | name_hash: [u8; 4], 39 | } 40 | 41 | /// On-Disk Structure of an Index Leaf item (On-Disk Signature: `li`). 42 | /// They are supported in all Windows versions. 43 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 44 | #[repr(packed)] 45 | struct IndexLeafItem { 46 | key_node_offset: U32, 47 | } 48 | 49 | /// All known and supported Leaf types. 50 | /// 51 | /// We first had only Index Leafs, then got Fast Leafs with Windows NT 4 which add a 52 | /// `name_hint` (first 4 characters of the key name), and finally got Hash Leafs with 53 | /// Windows XP which come with a `name_hash` (simple hash of the entire key name) 54 | /// instead. 55 | /// Both Fast Leafs and Hash Leafs were introduced to speed up key lookups. 56 | /// However, their performance benefits are marginal to non-existing in 2020 57 | /// when we assume that the entire registry hive is randomly accessible. 58 | /// Therefore, the nt-hive crate treats all types equally by only accessing the 59 | /// `key_node_offset` field and ignoring all other fields. 60 | #[derive(Clone, Copy)] 61 | pub(crate) enum LeafType { 62 | Fast, 63 | Hash, 64 | Index, 65 | } 66 | 67 | impl LeafType { 68 | pub(crate) fn from_signature(signature: &[u8]) -> Option { 69 | match signature { 70 | b"lf" => Some(Self::Fast), 71 | b"lh" => Some(Self::Hash), 72 | b"li" => Some(Self::Index), 73 | _ => None, 74 | } 75 | } 76 | 77 | fn item_size(&self) -> usize { 78 | match self { 79 | Self::Fast => mem::size_of::(), 80 | Self::Hash => mem::size_of::(), 81 | Self::Index => mem::size_of::(), 82 | } 83 | } 84 | } 85 | 86 | /// Byte range of a single Leaf item returned by [`LeafItemRanges`]. 87 | pub(crate) struct LeafItemRange(Range); 88 | 89 | impl LeafItemRange { 90 | pub fn key_node_offset(&self, hive: &Hive) -> u32 91 | where 92 | B: SplitByteSlice, 93 | { 94 | // We make use of the fact that a `FastLeafItem` or `HashLeafItem` is just an 95 | // `IndexLeafItem` with additional fields. 96 | // As they all have the `key_node_offset` as their first field, treat them equally. 97 | let (index_leaf_item, _) = 98 | Ref::<&[u8], IndexLeafItem>::from_prefix(&hive.data[self.0.clone()]).unwrap(); 99 | index_leaf_item.key_node_offset.get() 100 | } 101 | } 102 | 103 | impl Deref for LeafItemRange { 104 | type Target = Range; 105 | 106 | fn deref(&self) -> &Self::Target { 107 | &self.0 108 | } 109 | } 110 | 111 | /// Iterator over 112 | /// a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index), 113 | /// returning a [`LeafItemRange`] for each Leaf item. 114 | /// 115 | /// On-Disk Signatures: `lf`, `lh`, `li` 116 | #[derive(Clone)] 117 | pub(crate) struct LeafItemRanges { 118 | items_range: Range, 119 | leaf_type: LeafType, 120 | } 121 | 122 | impl LeafItemRanges { 123 | pub fn new( 124 | count: u16, 125 | count_field_offset: usize, 126 | data_range: Range, 127 | leaf_type: LeafType, 128 | ) -> Result { 129 | let byte_count = count as usize * leaf_type.item_size(); 130 | 131 | let items_range = byte_subrange(&data_range, byte_count).ok_or_else(|| { 132 | NtHiveError::InvalidSizeField { 133 | offset: count_field_offset, 134 | expected: byte_count, 135 | actual: data_range.len(), 136 | } 137 | })?; 138 | 139 | Ok(Self { 140 | items_range, 141 | leaf_type, 142 | }) 143 | } 144 | 145 | pub fn from_index_root_item_range( 146 | hive: &Hive, 147 | index_root_item_range: IndexRootItemRange, 148 | ) -> Result 149 | where 150 | B: SplitByteSlice, 151 | { 152 | let subkeys_list_offset = index_root_item_range.subkeys_list_offset(hive); 153 | let cell_range = hive.cell_range_from_data_offset(subkeys_list_offset)?; 154 | let subkeys_list = SubkeysList::new_without_index_root(hive, cell_range)?; 155 | 156 | let header = subkeys_list.header(); 157 | let count = header.count.get(); 158 | let count_field_offset = hive.offset_of_field(&header.count); 159 | 160 | // Subkeys Lists belonging to Index Root items need to contain at least 1 item. 161 | // Otherwise, we can't perform efficient binary search on them, which is the sole reason 162 | // Index Roots exist. 163 | if count == 0 { 164 | return Err(NtHiveError::InvalidSizeField { 165 | offset: count_field_offset, 166 | expected: 1, 167 | actual: 0, 168 | }); 169 | } 170 | 171 | let leaf_type = LeafType::from_signature(&header.signature).unwrap(); 172 | LeafItemRanges::new( 173 | count, 174 | count_field_offset, 175 | subkeys_list.data_range, 176 | leaf_type, 177 | ) 178 | } 179 | } 180 | 181 | impl Iterator for LeafItemRanges { 182 | type Item = LeafItemRange; 183 | 184 | fn next(&mut self) -> Option { 185 | let item_size = self.leaf_type.item_size(); 186 | let item_range = byte_subrange(&self.items_range, item_size)?; 187 | self.items_range.start += item_size; 188 | 189 | Some(LeafItemRange(item_range)) 190 | } 191 | 192 | fn count(self) -> usize { 193 | let (size, _) = self.size_hint(); 194 | size 195 | } 196 | 197 | fn last(mut self) -> Option { 198 | let (size, _) = self.size_hint(); 199 | if size == 0 { 200 | return None; 201 | } 202 | 203 | self.nth(size - 1) 204 | } 205 | 206 | fn nth(&mut self, n: usize) -> Option { 207 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 208 | let bytes_to_skip = n.checked_mul(self.leaf_type.item_size())?; 209 | self.items_range.start = self.items_range.start.checked_add(bytes_to_skip)?; 210 | self.next() 211 | } 212 | 213 | fn size_hint(&self) -> (usize, Option) { 214 | let size = self.items_range.len() / self.leaf_type.item_size(); 215 | (size, Some(size)) 216 | } 217 | } 218 | 219 | impl From> for LeafItemRanges { 220 | fn from(leaf_key_nodes: LeafKeyNodes<'_, B>) -> LeafItemRanges { 221 | leaf_key_nodes.leaf_item_ranges 222 | } 223 | } 224 | 225 | impl ExactSizeIterator for LeafItemRanges {} 226 | impl FusedIterator for LeafItemRanges {} 227 | 228 | /// Iterator over 229 | /// a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index), 230 | /// returning a constant [`KeyNode`] for each Leaf item, 231 | /// used by [`SubKeyNodes`]. 232 | /// 233 | /// On-Disk Signatures: `lf`, `lh`, `li` 234 | /// 235 | /// [`SubKeyNodes`]: crate::subkeys_list::SubKeyNodes 236 | #[derive(Clone)] 237 | pub struct LeafKeyNodes<'h, B: SplitByteSlice> { 238 | hive: &'h Hive, 239 | leaf_item_ranges: LeafItemRanges, 240 | } 241 | 242 | impl<'h, B> LeafKeyNodes<'h, B> 243 | where 244 | B: SplitByteSlice, 245 | { 246 | pub(crate) fn new( 247 | hive: &'h Hive, 248 | count: u16, 249 | count_field_offset: usize, 250 | data_range: Range, 251 | leaf_type: LeafType, 252 | ) -> Result { 253 | let leaf_item_ranges = 254 | LeafItemRanges::new(count, count_field_offset, data_range, leaf_type)?; 255 | 256 | Ok(Self { 257 | hive, 258 | leaf_item_ranges, 259 | }) 260 | } 261 | } 262 | 263 | impl<'h, B> Iterator for LeafKeyNodes<'h, B> 264 | where 265 | B: SplitByteSlice, 266 | { 267 | type Item = Result>; 268 | 269 | fn next(&mut self) -> Option { 270 | let leaf_item_range = self.leaf_item_ranges.next()?; 271 | let key_node = iter_try!(KeyNode::from_leaf_item_range(self.hive, leaf_item_range)); 272 | Some(Ok(key_node)) 273 | } 274 | 275 | fn count(self) -> usize { 276 | self.leaf_item_ranges.count() 277 | } 278 | 279 | fn last(mut self) -> Option { 280 | let (size, _) = self.size_hint(); 281 | if size == 0 { 282 | return None; 283 | } 284 | 285 | self.nth(size - 1) 286 | } 287 | 288 | fn nth(&mut self, n: usize) -> Option { 289 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 290 | let bytes_to_skip = n.checked_mul(self.leaf_item_ranges.leaf_type.item_size())?; 291 | self.leaf_item_ranges.items_range.start = self 292 | .leaf_item_ranges 293 | .items_range 294 | .start 295 | .checked_add(bytes_to_skip)?; 296 | self.next() 297 | } 298 | 299 | fn size_hint(&self) -> (usize, Option) { 300 | self.leaf_item_ranges.size_hint() 301 | } 302 | } 303 | 304 | impl ExactSizeIterator for LeafKeyNodes<'_, B> where B: SplitByteSlice {} 305 | impl FusedIterator for LeafKeyNodes<'_, B> where B: SplitByteSlice {} 306 | 307 | /// Iterator over 308 | /// a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index), 309 | /// returning a mutable [`KeyNode`] for each Leaf item, 310 | /// used by [`SubKeyNodesMut`]. 311 | /// 312 | /// On-Disk Signatures: `lf`, `lh`, `li` 313 | /// 314 | /// [`SubKeyNodesMut`]: crate::subkeys_list::SubKeyNodesMut 315 | pub(crate) struct LeafKeyNodesMut<'h, B: SplitByteSliceMut> { 316 | hive: &'h mut Hive, 317 | leaf_item_ranges: LeafItemRanges, 318 | } 319 | 320 | impl<'h, B> LeafKeyNodesMut<'h, B> 321 | where 322 | B: SplitByteSliceMut, 323 | { 324 | pub(crate) fn new( 325 | hive: &'h mut Hive, 326 | count: u16, 327 | count_field_offset: usize, 328 | data_range: Range, 329 | leaf_type: LeafType, 330 | ) -> Result { 331 | let leaf_item_ranges = 332 | LeafItemRanges::new(count, count_field_offset, data_range, leaf_type)?; 333 | 334 | Ok(Self { 335 | hive, 336 | leaf_item_ranges, 337 | }) 338 | } 339 | 340 | pub(crate) fn next<'a>(&'a mut self) -> Option>> 341 | where 342 | 'h: 'a, 343 | { 344 | let leaf_item_range = self.leaf_item_ranges.next()?; 345 | let key_node = iter_try!(KeyNodeMut::from_leaf_item_range(self.hive, leaf_item_range)); 346 | Some(Ok(key_node)) 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /src/big_data.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::cmp; 5 | use core::iter::FusedIterator; 6 | use core::mem; 7 | use core::ops::{Deref, Range}; 8 | 9 | use zerocopy::byteorder::LittleEndian; 10 | use zerocopy::{ 11 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned, U16, U32, 12 | }; 13 | 14 | use crate::error::{NtHiveError, Result}; 15 | use crate::helpers::byte_subrange; 16 | use crate::hive::Hive; 17 | 18 | /// Number of bytes that a single Big Data segment can hold. 19 | /// Every Big Data segment contains that many data bytes except for the last one. 20 | /// 21 | /// This is also the threshold to decide whether Key Value Data is considered Big Data or not. 22 | /// Up to this size, data fits into a single cell and is handled via KeyValueData::Small. 23 | /// Everything above needs a Big Data structure and is handled through KeyValueData::Big. 24 | pub(crate) const BIG_DATA_SEGMENT_SIZE: usize = 16344; 25 | 26 | /// On-Disk Structure of a Big Data header. 27 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 28 | #[repr(packed)] 29 | struct BigDataHeader { 30 | signature: [u8; 2], 31 | segment_count: U16, 32 | segment_list_offset: U32, 33 | } 34 | 35 | /// On-Disk Structure of a Big Data list item. 36 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 37 | #[repr(packed)] 38 | struct BigDataListItem { 39 | segment_offset: U32, 40 | } 41 | 42 | /// Byte range of a single Big Data list item returned by [`BigDataListItemRanges`]. 43 | struct BigDataListItemRange(Range); 44 | 45 | impl BigDataListItemRange { 46 | fn segment_offset(&self, hive: &Hive) -> u32 47 | where 48 | B: SplitByteSlice, 49 | { 50 | let item = Ref::<&[u8], BigDataListItem>::from_bytes(&hive.data[self.0.clone()]).unwrap(); 51 | item.segment_offset.get() 52 | } 53 | } 54 | 55 | impl Deref for BigDataListItemRange { 56 | type Target = Range; 57 | 58 | fn deref(&self) -> &Self::Target { 59 | &self.0 60 | } 61 | } 62 | 63 | /// Iterator over 64 | /// a contiguous range of data bytes containing Big Data list items, 65 | /// returning a [`BigDataListItemRange`] for each item. 66 | /// 67 | /// On-Disk Signature: `db` 68 | #[derive(Clone)] 69 | struct BigDataListItemRanges { 70 | items_range: Range, 71 | } 72 | 73 | impl BigDataListItemRanges { 74 | fn new( 75 | hive: &Hive, 76 | data_size: u32, 77 | data_size_field_offset: usize, 78 | header_cell_range: Range, 79 | ) -> Result 80 | where 81 | B: SplitByteSlice, 82 | { 83 | let data_size = data_size as usize; 84 | 85 | // The passed `header_cell_range` contains just the `BigDataHeader`. 86 | // Verify this header. 87 | let header_range = byte_subrange(&header_cell_range, mem::size_of::()) 88 | .ok_or_else(|| NtHiveError::InvalidHeaderSize { 89 | offset: hive.offset_of_data_offset(header_cell_range.start), 90 | expected: mem::size_of::(), 91 | actual: header_cell_range.len(), 92 | })?; 93 | 94 | let header = Ref::from_bytes(&hive.data[header_range]).unwrap(); 95 | Self::validate_signature(hive, &header)?; 96 | 97 | // Check the `segment_count` of the `BigDataHeader`. 98 | // Verify that we have enough segments to contain the entire data. 99 | let segment_count = header.segment_count.get(); 100 | let max_data_size = segment_count as usize * BIG_DATA_SEGMENT_SIZE; 101 | if data_size > max_data_size { 102 | return Err(NtHiveError::InvalidSizeField { 103 | offset: data_size_field_offset, 104 | expected: max_data_size, 105 | actual: data_size, 106 | }); 107 | } 108 | 109 | // Get the Big Data segment list referenced by the `segment_list_offset`. 110 | let segment_list_offset = header.segment_list_offset.get(); 111 | let segment_list_cell_range = hive.cell_range_from_data_offset(segment_list_offset)?; 112 | 113 | // Finally calculate the range of Big Data list items we want to iterate over. 114 | let byte_count = segment_count as usize * mem::size_of::(); 115 | 116 | let items_range = byte_subrange(&segment_list_cell_range, byte_count).ok_or_else(|| { 117 | NtHiveError::InvalidSizeField { 118 | offset: hive.offset_of_field(&header.segment_count), 119 | expected: byte_count, 120 | actual: segment_list_cell_range.len(), 121 | } 122 | })?; 123 | 124 | Ok(Self { items_range }) 125 | } 126 | 127 | fn validate_signature(hive: &Hive, header: &Ref<&[u8], BigDataHeader>) -> Result<()> 128 | where 129 | B: SplitByteSlice, 130 | { 131 | let signature = &header.signature; 132 | let expected_signature = b"db"; 133 | 134 | if signature == expected_signature { 135 | Ok(()) 136 | } else { 137 | Err(NtHiveError::InvalidTwoByteSignature { 138 | offset: hive.offset_of_field(signature), 139 | expected: expected_signature, 140 | actual: *signature, 141 | }) 142 | } 143 | } 144 | } 145 | 146 | impl Iterator for BigDataListItemRanges { 147 | type Item = BigDataListItemRange; 148 | 149 | fn next(&mut self) -> Option { 150 | let item_range = byte_subrange(&self.items_range, mem::size_of::())?; 151 | self.items_range.start += mem::size_of::(); 152 | 153 | Some(BigDataListItemRange(item_range)) 154 | } 155 | 156 | fn count(self) -> usize { 157 | let (size, _) = self.size_hint(); 158 | size 159 | } 160 | 161 | fn last(mut self) -> Option { 162 | let (size, _) = self.size_hint(); 163 | if size == 0 { 164 | return None; 165 | } 166 | 167 | self.nth(size - 1) 168 | } 169 | 170 | fn nth(&mut self, n: usize) -> Option { 171 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 172 | let bytes_to_skip = n.checked_mul(mem::size_of::())?; 173 | self.items_range.start = self.items_range.start.checked_add(bytes_to_skip)?; 174 | self.next() 175 | } 176 | 177 | fn size_hint(&self) -> (usize, Option) { 178 | let size = self.items_range.len() / mem::size_of::(); 179 | (size, Some(size)) 180 | } 181 | } 182 | 183 | impl ExactSizeIterator for BigDataListItemRanges {} 184 | impl FusedIterator for BigDataListItemRanges {} 185 | 186 | /// Iterator over 187 | /// a contiguous range of data bytes containing Big Data list items, 188 | /// returning a constant byte slice for each item, 189 | /// used by [`KeyValueData`]. 190 | /// 191 | /// On-Disk Signature: `db` 192 | /// 193 | /// [`KeyValueData`]: crate::key_value::KeyValueData 194 | #[derive(Clone)] 195 | pub struct BigDataSlices<'h, B: SplitByteSlice> { 196 | hive: &'h Hive, 197 | big_data_list_item_ranges: BigDataListItemRanges, 198 | bytes_left: usize, 199 | } 200 | 201 | impl<'h, B> BigDataSlices<'h, B> 202 | where 203 | B: SplitByteSlice, 204 | { 205 | pub(crate) fn new( 206 | hive: &'h Hive, 207 | data_size: u32, 208 | data_size_field_offset: usize, 209 | header_cell_range: Range, 210 | ) -> Result { 211 | let big_data_list_item_ranges = 212 | BigDataListItemRanges::new(hive, data_size, data_size_field_offset, header_cell_range)?; 213 | 214 | Ok(Self { 215 | hive, 216 | big_data_list_item_ranges, 217 | bytes_left: data_size as usize, 218 | }) 219 | } 220 | } 221 | 222 | impl<'h, B> Iterator for BigDataSlices<'h, B> 223 | where 224 | B: SplitByteSlice, 225 | { 226 | type Item = Result<&'h [u8]>; 227 | 228 | fn next(&mut self) -> Option { 229 | // Every segment contains BIG_DATA_SEGMENT_SIZE bytes of data except for the last one. 230 | let bytes_to_return = cmp::min(self.bytes_left, BIG_DATA_SEGMENT_SIZE); 231 | if bytes_to_return == 0 { 232 | return None; 233 | } 234 | 235 | // Get the next segment offset and adjust `bytes_left` accordingly. 236 | let big_data_list_item_range = self.big_data_list_item_ranges.next()?; 237 | let segment_offset = big_data_list_item_range.segment_offset(self.hive); 238 | self.bytes_left -= bytes_to_return; 239 | 240 | // Get the cell belonging to that offset and check if it contains as many bytes 241 | // as we expect. 242 | let cell_range = iter_try!(self.hive.cell_range_from_data_offset(segment_offset)); 243 | let data_range = iter_try!(byte_subrange(&cell_range, bytes_to_return).ok_or_else(|| { 244 | NtHiveError::InvalidDataSize { 245 | offset: self.hive.offset_of_data_offset(cell_range.start), 246 | expected: bytes_to_return, 247 | actual: cell_range.len(), 248 | } 249 | })); 250 | 251 | // Return a byte slice containing this segment's data. 252 | Some(Ok(&self.hive.data[data_range])) 253 | } 254 | 255 | fn count(self) -> usize { 256 | self.big_data_list_item_ranges.count() 257 | } 258 | 259 | fn last(mut self) -> Option { 260 | let (size, _) = self.size_hint(); 261 | if size == 0 { 262 | return None; 263 | } 264 | 265 | self.nth(size - 1) 266 | } 267 | 268 | fn nth(&mut self, n: usize) -> Option { 269 | // `n` is arbitrary and usize, so we may hit boundaries here. Check that! 270 | let bytes_to_skip = n.checked_mul(BIG_DATA_SEGMENT_SIZE)?; 271 | self.bytes_left = self.bytes_left.saturating_sub(bytes_to_skip); 272 | if self.bytes_left == 0 { 273 | return None; 274 | } 275 | 276 | // This calculation is safe considering that we have checked the 277 | // multiplication and subtraction above. 278 | self.big_data_list_item_ranges.items_range.start += n * mem::size_of::(); 279 | 280 | self.next() 281 | } 282 | 283 | fn size_hint(&self) -> (usize, Option) { 284 | self.big_data_list_item_ranges.size_hint() 285 | } 286 | } 287 | 288 | impl ExactSizeIterator for BigDataSlices<'_, B> where B: SplitByteSlice {} 289 | impl FusedIterator for BigDataSlices<'_, B> where B: SplitByteSlice {} 290 | 291 | #[cfg(test)] 292 | mod tests { 293 | use crate::*; 294 | 295 | #[test] 296 | fn test_big_data() { 297 | let testhive = crate::helpers::tests::testhive_vec(); 298 | let hive = Hive::new(testhive.as_ref()).unwrap(); 299 | let root_key_node = hive.root_key_node().unwrap(); 300 | let key_node = root_key_node.subkey("big-data-test").unwrap().unwrap(); 301 | 302 | // Key Value "A" should be filled with 16343 'A' bytes and still fit into a cell. 303 | let key_value = key_node.value("A").unwrap().unwrap(); 304 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegBinary); 305 | assert_eq!(key_value.data_size(), 16343); 306 | 307 | let expected_data = vec![b'A'; 16343]; 308 | let key_value_data = key_value.data().unwrap(); 309 | assert!(matches!(key_value_data, KeyValueData::Small(_))); 310 | assert_eq!(key_value_data.into_vec().unwrap(), expected_data); 311 | 312 | // Key Value "B" should be filled with 16344 'B' bytes and still fit into a cell. 313 | let key_value = key_node.value("B").unwrap().unwrap(); 314 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegBinary); 315 | assert_eq!(key_value.data_size(), 16344); 316 | 317 | let expected_data = vec![b'B'; 16344]; 318 | let key_value_data = key_value.data().unwrap(); 319 | assert!(matches!(key_value_data, KeyValueData::Small(_))); 320 | assert_eq!(key_value_data.into_vec().unwrap(), expected_data); 321 | 322 | // Key Value "C" should be filled with 16345 'C' bytes and require a Big Data structure. 323 | let key_value = key_node.value("C").unwrap().unwrap(); 324 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegBinary); 325 | assert_eq!(key_value.data_size(), 16345); 326 | 327 | let expected_data = vec![b'C'; 16345]; 328 | let key_value_data = key_value.data().unwrap(); 329 | assert!(matches!(key_value_data, KeyValueData::Big(_))); 330 | assert_eq!(key_value_data.into_vec().unwrap(), expected_data); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/hive.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::mem; 5 | use core::ops::Range; 6 | 7 | use enumn::N; 8 | use memoffset::offset_of; 9 | use zerocopy::byteorder::LittleEndian; 10 | use zerocopy::{ 11 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, 12 | Unaligned, I32, U16, U32, U64, 13 | }; 14 | 15 | use crate::error::{NtHiveError, Result}; 16 | use crate::helpers::byte_subrange; 17 | use crate::key_node::{KeyNode, KeyNodeMut}; 18 | 19 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 20 | #[repr(packed)] 21 | struct CellHeader { 22 | size: I32, 23 | } 24 | 25 | /// Known hive minor versions. 26 | /// 27 | /// You can use [`HiveMinorVersion::n`] on the value returned by [`Hive::minor_version`] 28 | /// to find out whether a hive has a known version. 29 | #[derive(Clone, Copy, Debug, Eq, N, Ord, PartialEq, PartialOrd)] 30 | #[repr(u32)] 31 | pub enum HiveMinorVersion { 32 | WindowsNT3_1Beta = 0, 33 | WindowsNT3_1 = 1, 34 | WindowsNT3_5 = 2, 35 | WindowsNT4 = 3, 36 | WindowsXPBeta = 4, 37 | WindowsXP = 5, 38 | WindowsVista = 6, 39 | } 40 | 41 | #[allow(dead_code)] 42 | #[repr(u32)] 43 | enum HiveFileTypes { 44 | Primary = 0, 45 | Log = 1, 46 | External = 2, 47 | } 48 | 49 | #[repr(u32)] 50 | enum HiveFileFormats { 51 | Memory = 1, 52 | } 53 | 54 | #[allow(dead_code)] 55 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 56 | #[repr(packed)] 57 | struct HiveBaseBlock { 58 | signature: [u8; 4], 59 | primary_sequence_number: U32, 60 | secondary_sequence_number: U32, 61 | timestamp: U64, 62 | major_version: U32, 63 | minor_version: U32, 64 | file_type: U32, 65 | file_format: U32, 66 | root_cell_offset: U32, 67 | data_size: U32, 68 | clustering_factor: U32, 69 | file_name: [U16; 32], 70 | padding_1: [u8; 396], 71 | checksum: U32, 72 | padding_2: [u8; 3576], 73 | boot_type: U32, 74 | boot_recover: U32, 75 | } 76 | 77 | /// Root structure describing a registry hive. 78 | pub struct Hive { 79 | base_block: Ref, 80 | pub(crate) data: B, 81 | } 82 | 83 | impl Hive 84 | where 85 | B: SplitByteSlice, 86 | { 87 | /// Creates a new `Hive` from any byte slice. 88 | /// Performs basic validation and rejects any invalid hive. 89 | /// 90 | /// You may use [`Hive::without_validation`] if you want to accept hives that fail validation. 91 | pub fn new(bytes: B) -> Result { 92 | let hive = Self::without_validation(bytes)?; 93 | hive.validate()?; 94 | Ok(hive) 95 | } 96 | 97 | /// Creates a new `Hive` from any byte slice, without validating the header. 98 | /// 99 | /// You may later validate the header via [`Hive::validate`]. 100 | /// This is a solution for accessing parts of hives that have not been fully flushed to disk 101 | /// (e.g. due to hibernation and mismatching sequence numbers). 102 | pub fn without_validation(bytes: B) -> Result { 103 | let length = bytes.len(); 104 | let (base_block, data) = 105 | Ref::from_prefix(bytes).map_err(|_| NtHiveError::InvalidHeaderSize { 106 | offset: 0, 107 | expected: mem::size_of::(), 108 | actual: length, 109 | })?; 110 | 111 | let hive = Self { base_block, data }; 112 | Ok(hive) 113 | } 114 | 115 | pub(crate) fn cell_range_from_data_offset(&self, data_offset: u32) -> Result> { 116 | // Only valid data offsets are accepted here. 117 | assert!(data_offset != u32::MAX); 118 | 119 | // Accept only u32 data offsets, but convert them into usize right away for 120 | // slice range operations and fearless calculations. 121 | let data_offset = data_offset as usize; 122 | 123 | // Get the cell header. 124 | let remaining_range = data_offset..self.data.len(); 125 | let header_range = byte_subrange(&remaining_range, mem::size_of::()) 126 | .ok_or_else(|| NtHiveError::InvalidHeaderSize { 127 | offset: self.offset_of_data_offset(data_offset), 128 | expected: mem::size_of::(), 129 | actual: remaining_range.len(), 130 | })?; 131 | let cell_data_offset = header_range.end; 132 | 133 | // After the check above, the following operation must succeed, so we can just `unwrap`. 134 | let header = Ref::<&[u8], CellHeader>::from_bytes(&self.data[header_range]).unwrap(); 135 | let cell_size = header.size.get(); 136 | 137 | // A cell with size > 0 is unallocated and shouldn't be processed any further by us. 138 | if cell_size > 0 { 139 | return Err(NtHiveError::UnallocatedCell { 140 | offset: self.offset_of_data_offset(data_offset), 141 | size: cell_size, 142 | }); 143 | } 144 | let cell_size = cell_size.unsigned_abs() as usize; 145 | 146 | // The cell size must be a multiple of 8 bytes 147 | let expected_alignment = 8; 148 | if cell_size % expected_alignment != 0 { 149 | return Err(NtHiveError::InvalidSizeFieldAlignment { 150 | offset: self.offset_of_field(&header.size), 151 | size: cell_size, 152 | expected_alignment, 153 | }); 154 | } 155 | 156 | // Get the actual data range and verify that it's inside our hive data. 157 | let remaining_range = cell_data_offset..self.data.len(); 158 | let cell_data_size = cell_size - mem::size_of::(); 159 | let cell_data_range = byte_subrange(&remaining_range, cell_data_size).ok_or_else(|| { 160 | NtHiveError::InvalidSizeField { 161 | offset: self.offset_of_field(&header.size), 162 | expected: cell_data_size, 163 | actual: remaining_range.len(), 164 | } 165 | })?; 166 | 167 | Ok(cell_data_range) 168 | } 169 | 170 | /// Calculate a field's offset from the very beginning of the hive bytes. 171 | /// 172 | /// Note that this function primarily exists to provide absolute hive file offsets when reporting errors. 173 | /// It cannot be used to index into the hive bytes, because they are initially split into `base_block` 174 | /// and `data`. 175 | pub(crate) fn offset_of_field(&self, field: &T) -> usize { 176 | let field_address = field as *const T as usize; 177 | let base_address = Ref::bytes(&self.base_block).as_ptr() as usize; 178 | 179 | assert!(field_address > base_address); 180 | field_address - base_address 181 | } 182 | 183 | /// Calculate a data offset's offset from the very beginning of the hive bytes. 184 | pub(crate) fn offset_of_data_offset(&self, data_offset: usize) -> usize { 185 | data_offset + mem::size_of::() 186 | } 187 | 188 | /// Returns the major version of this hive. 189 | /// 190 | /// The only known value is `1`. 191 | pub fn major_version(&self) -> u32 { 192 | self.base_block.major_version.get() 193 | } 194 | 195 | /// Returns the minor version of this hive. 196 | /// 197 | /// You can feed this value to [`HiveMinorVersion::n`] to find out whether this is a known version. 198 | pub fn minor_version(&self) -> u32 { 199 | self.base_block.minor_version.get() 200 | } 201 | 202 | /// Returns the root [`KeyNode`] of this hive. 203 | pub fn root_key_node(&self) -> Result> { 204 | let root_cell_offset = self.base_block.root_cell_offset.get(); 205 | let cell_range = self.cell_range_from_data_offset(root_cell_offset)?; 206 | KeyNode::from_cell_range(self, cell_range) 207 | } 208 | 209 | /// Performs basic validations on the header of this hive. 210 | /// 211 | /// If you read the hive via [`Hive::new`], these validations have already been performed. 212 | /// This function is only relevant for hives opened via [`Hive::without_validation`]. 213 | pub fn validate(&self) -> Result<()> { 214 | self.validate_signature()?; 215 | self.validate_sequence_numbers()?; 216 | self.validate_version()?; 217 | self.validate_file_type()?; 218 | self.validate_file_format()?; 219 | self.validate_data_size()?; 220 | self.validate_clustering_factor()?; 221 | self.validate_checksum()?; 222 | Ok(()) 223 | } 224 | 225 | fn validate_checksum(&self) -> Result<()> { 226 | let checksum_offset = offset_of!(HiveBaseBlock, checksum); 227 | 228 | // Calculate the XOR-32 checksum of all bytes preceding the checksum field. 229 | let mut calculated_checksum = 0; 230 | for dword_bytes in 231 | Ref::bytes(&self.base_block)[..checksum_offset].chunks(mem::size_of::()) 232 | { 233 | let dword = u32::from_le_bytes(dword_bytes.try_into().unwrap()); 234 | calculated_checksum ^= dword; 235 | } 236 | 237 | if calculated_checksum == 0 { 238 | calculated_checksum += 1; 239 | } else if calculated_checksum == u32::MAX { 240 | calculated_checksum -= 1; 241 | } 242 | 243 | // Compare the calculated checksum with the stored one. 244 | let checksum = self.base_block.checksum.get(); 245 | if checksum == calculated_checksum { 246 | Ok(()) 247 | } else { 248 | Err(NtHiveError::InvalidChecksum { 249 | expected: checksum, 250 | actual: calculated_checksum, 251 | }) 252 | } 253 | } 254 | 255 | fn validate_clustering_factor(&self) -> Result<()> { 256 | let clustering_factor = self.base_block.clustering_factor.get(); 257 | let expected_clustering_factor = 1; 258 | 259 | if clustering_factor == expected_clustering_factor { 260 | Ok(()) 261 | } else { 262 | Err(NtHiveError::UnsupportedClusteringFactor { 263 | expected: expected_clustering_factor, 264 | actual: clustering_factor, 265 | }) 266 | } 267 | } 268 | 269 | fn validate_data_size(&self) -> Result<()> { 270 | let data_size = self.base_block.data_size.get() as usize; 271 | let expected_alignment = 4096; 272 | 273 | // The data size must be a multiple of 4096 bytes 274 | if data_size % expected_alignment != 0 { 275 | return Err(NtHiveError::InvalidSizeFieldAlignment { 276 | offset: self.offset_of_field(&self.base_block.data_size), 277 | size: data_size, 278 | expected_alignment, 279 | }); 280 | } 281 | 282 | // Does the size go beyond our hive data? 283 | if data_size > self.data.len() { 284 | return Err(NtHiveError::InvalidSizeField { 285 | offset: self.offset_of_field(&self.base_block.data_size), 286 | expected: data_size, 287 | actual: self.data.len(), 288 | }); 289 | } 290 | 291 | Ok(()) 292 | } 293 | 294 | fn validate_file_format(&self) -> Result<()> { 295 | let file_format = self.base_block.file_format.get(); 296 | let expected_file_format = HiveFileFormats::Memory as u32; 297 | 298 | if file_format == expected_file_format { 299 | Ok(()) 300 | } else { 301 | Err(NtHiveError::UnsupportedFileFormat { 302 | expected: expected_file_format, 303 | actual: file_format, 304 | }) 305 | } 306 | } 307 | 308 | fn validate_file_type(&self) -> Result<()> { 309 | let file_type = self.base_block.file_type.get(); 310 | let expected_file_type = HiveFileTypes::Primary as u32; 311 | 312 | if file_type == expected_file_type { 313 | Ok(()) 314 | } else { 315 | Err(NtHiveError::UnsupportedFileType { 316 | expected: expected_file_type, 317 | actual: file_type, 318 | }) 319 | } 320 | } 321 | 322 | fn validate_sequence_numbers(&self) -> Result<()> { 323 | let primary_sequence_number = self.base_block.primary_sequence_number.get(); 324 | let secondary_sequence_number = self.base_block.secondary_sequence_number.get(); 325 | 326 | if primary_sequence_number == secondary_sequence_number { 327 | Ok(()) 328 | } else { 329 | Err(NtHiveError::SequenceNumberMismatch { 330 | primary: primary_sequence_number, 331 | secondary: secondary_sequence_number, 332 | }) 333 | } 334 | } 335 | 336 | fn validate_signature(&self) -> Result<()> { 337 | let signature = &self.base_block.signature; 338 | let expected_signature = b"regf"; 339 | 340 | if signature == expected_signature { 341 | Ok(()) 342 | } else { 343 | Err(NtHiveError::InvalidFourByteSignature { 344 | offset: self.offset_of_field(signature), 345 | expected: expected_signature, 346 | actual: *signature, 347 | }) 348 | } 349 | } 350 | 351 | fn validate_version(&self) -> Result<()> { 352 | let major = self.major_version(); 353 | let minor = self.minor_version(); 354 | 355 | if major == 1 && minor >= HiveMinorVersion::WindowsNT4 as u32 { 356 | Ok(()) 357 | } else { 358 | Err(NtHiveError::UnsupportedVersion { major, minor }) 359 | } 360 | } 361 | } 362 | 363 | impl Hive 364 | where 365 | B: SplitByteSliceMut, 366 | { 367 | /// Clears the `volatile_subkey_count` field of all key nodes recursively. 368 | /// 369 | /// This needs to be done before passing the hive to an NT kernel during boot. 370 | /// See for more information. 371 | pub fn clear_volatile_subkeys(&mut self) -> Result<()> { 372 | let mut root_key_node = self.root_key_node_mut()?; 373 | root_key_node.clear_volatile_subkeys() 374 | } 375 | 376 | pub(crate) fn root_key_node_mut(&mut self) -> Result> { 377 | let root_cell_offset = self.base_block.root_cell_offset.get(); 378 | let cell_range = self.cell_range_from_data_offset(root_cell_offset)?; 379 | KeyNodeMut::from_cell_range(self, cell_range) 380 | } 381 | } 382 | 383 | #[cfg(test)] 384 | mod tests { 385 | use crate::*; 386 | 387 | #[test] 388 | fn test_clear_volatile_subkeys() { 389 | // clear_volatile_subkeys traverses all subkeys, so this test just checks 390 | // that it doesn't crash during that process. 391 | let mut testhive = crate::helpers::tests::testhive_vec(); 392 | let mut hive = Hive::new(testhive.as_mut()).unwrap(); 393 | assert!(hive.clear_volatile_subkeys().is_ok()); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/key_value.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::mem; 5 | use core::ops::Range; 6 | use core::ptr; 7 | 8 | use bitflags::bitflags; 9 | use enumn::N; 10 | use memoffset::offset_of; 11 | use zerocopy::byteorder::LittleEndian; 12 | use zerocopy::{ 13 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned, U16, U32, 14 | }; 15 | 16 | use crate::big_data::{BigDataSlices, BIG_DATA_SEGMENT_SIZE}; 17 | use crate::error::{NtHiveError, Result}; 18 | use crate::helpers::byte_subrange; 19 | use crate::hive::Hive; 20 | use crate::string::NtHiveNameString; 21 | 22 | #[cfg(feature = "alloc")] 23 | use { 24 | alloc::{string::String, vec::Vec}, 25 | core::{ 26 | char::{self, DecodeUtf16, DecodeUtf16Error}, 27 | iter::{self, FusedIterator, Map}, 28 | slice::ChunksExact, 29 | }, 30 | }; 31 | 32 | /// This bit in `data_size` indicates that the data is small enough to be stored in `data_offset`. 33 | const DATA_STORED_IN_DATA_OFFSET: u32 = 0x8000_0000; 34 | 35 | bitflags! { 36 | struct KeyValueFlags: u16 { 37 | /// The name is in (extended) ASCII instead of UTF-16LE. 38 | const VALUE_COMP_NAME = 0x0001; 39 | } 40 | } 41 | 42 | /// Zero-copy representation of raw Key Value data, returned by [`KeyValue::data`]. 43 | #[derive(Clone)] 44 | pub enum KeyValueData<'h, B: SplitByteSlice> { 45 | /// The data fits into a single cell. 46 | /// Contains the contiguous range of data bytes. 47 | Small(&'h [u8]), 48 | /// The data is big enough to require more than one cell. 49 | /// Contains an iterator that returns the data byte slice for each cell. 50 | Big(BigDataSlices<'h, B>), 51 | } 52 | 53 | impl KeyValueData<'_, B> 54 | where 55 | B: SplitByteSlice, 56 | { 57 | #[cfg(feature = "alloc")] 58 | pub fn into_vec(self) -> Result> { 59 | match self { 60 | KeyValueData::Small(data) => Ok(data.to_vec()), 61 | KeyValueData::Big(iter) => { 62 | let mut data = Vec::new(); 63 | 64 | for slice_data in iter { 65 | let slice_data = slice_data?; 66 | data.extend_from_slice(slice_data); 67 | } 68 | 69 | Ok(data) 70 | } 71 | } 72 | } 73 | } 74 | 75 | /// Possible data types of the data belonging to a [`KeyValue`]. 76 | #[derive(Clone, Copy, Debug, Eq, N, PartialEq)] 77 | #[repr(u32)] 78 | pub enum KeyValueDataType { 79 | RegNone = 0x0000_0000, 80 | RegSZ = 0x0000_0001, 81 | RegExpandSZ = 0x0000_0002, 82 | RegBinary = 0x0000_0003, 83 | RegDWord = 0x0000_0004, 84 | RegDWordBigEndian = 0x0000_0005, 85 | RegLink = 0x0000_0006, 86 | RegMultiSZ = 0x0000_0007, 87 | RegResourceList = 0x0000_0008, 88 | RegFullResourceDescriptor = 0x0000_0009, 89 | RegResourceRequirementsList = 0x0000_000a, 90 | RegQWord = 0x0000_000b, 91 | } 92 | 93 | /// On-Disk Structure of a Key Value header. 94 | #[allow(dead_code)] 95 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 96 | #[repr(packed)] 97 | struct KeyValueHeader { 98 | signature: [u8; 2], 99 | name_length: U16, 100 | data_size: U32, 101 | data_offset: U32, 102 | data_type: U32, 103 | flags: U16, 104 | spare: U16, 105 | } 106 | 107 | /// A single value that belongs to a [`KeyNode`]. 108 | /// It has a name and attached data. 109 | /// 110 | /// On-Disk Signature: `vk` 111 | /// 112 | /// [`KeyNode`]: crate::key_node::KeyNode 113 | #[derive(Clone)] 114 | pub struct KeyValue<'h, B: SplitByteSlice> { 115 | hive: &'h Hive, 116 | header_range: Range, 117 | data_range: Range, 118 | } 119 | 120 | impl<'h, B> KeyValue<'h, B> 121 | where 122 | B: SplitByteSlice, 123 | { 124 | pub(crate) fn new(hive: &'h Hive, cell_range: Range) -> Result { 125 | let header_range = byte_subrange(&cell_range, mem::size_of::()) 126 | .ok_or_else(|| NtHiveError::InvalidHeaderSize { 127 | offset: hive.offset_of_data_offset(cell_range.start), 128 | expected: mem::size_of::(), 129 | actual: cell_range.len(), 130 | })?; 131 | let data_range = header_range.end..cell_range.end; 132 | 133 | let key_value = Self { 134 | hive, 135 | header_range, 136 | data_range, 137 | }; 138 | key_value.validate_signature()?; 139 | 140 | Ok(key_value) 141 | } 142 | 143 | fn header(&self) -> Ref<&[u8], KeyValueHeader> { 144 | Ref::from_bytes(&self.hive.data[self.header_range.clone()]).unwrap() 145 | } 146 | 147 | /// Returns the raw data bytes as [`KeyValueData`]. 148 | pub fn data(&self) -> Result> { 149 | let header = self.header(); 150 | 151 | let data_size = header.data_size.get(); 152 | let data_stored_in_data_offset = data_size & DATA_STORED_IN_DATA_OFFSET > 0; 153 | let data_size = (data_size & !DATA_STORED_IN_DATA_OFFSET) as usize; 154 | 155 | if data_stored_in_data_offset { 156 | // If the entire data is stored in the `data_offset` field, its size mustn't 157 | // exceed the 4 bytes we have. 158 | if data_size > mem::size_of::() { 159 | return Err(NtHiveError::InvalidSizeField { 160 | offset: self.hive.offset_of_field(&header.data_size), 161 | expected: mem::size_of::(), 162 | actual: data_size, 163 | }); 164 | } 165 | 166 | let data_start = self.header_range.start + offset_of!(KeyValueHeader, data_offset); 167 | let data_end = data_start + data_size; 168 | 169 | Ok(KeyValueData::Small(&self.hive.data[data_start..data_end])) 170 | } else if data_size <= BIG_DATA_SEGMENT_SIZE { 171 | // The entire data is stored in a single cell referenced by `data_offset`. 172 | let cell_range = self 173 | .hive 174 | .cell_range_from_data_offset(header.data_offset.get())?; 175 | if cell_range.len() < data_size { 176 | return Err(NtHiveError::InvalidDataSize { 177 | offset: self.hive.offset_of_data_offset(cell_range.start), 178 | expected: data_size, 179 | actual: cell_range.len(), 180 | }); 181 | } 182 | 183 | let data_start = cell_range.start; 184 | let data_end = data_start + data_size; 185 | 186 | Ok(KeyValueData::Small(&self.hive.data[data_start..data_end])) 187 | } else { 188 | // The data size exceeds what can be stored in a single cell. 189 | // It's therefore stored in a Big Data structure referencing multiple cells. 190 | let cell_range = self 191 | .hive 192 | .cell_range_from_data_offset(header.data_offset.get())?; 193 | let iter = BigDataSlices::new( 194 | self.hive, 195 | data_size as u32, 196 | self.hive.offset_of_field(&header.data_size), 197 | cell_range, 198 | )?; 199 | 200 | Ok(KeyValueData::Big(iter)) 201 | } 202 | } 203 | 204 | #[cfg(feature = "alloc")] 205 | fn utf16le_to_string_lossy(iter: I) -> Result 206 | where 207 | I: Iterator>, 208 | { 209 | let mut string = String::new(); 210 | 211 | // A very long REG_SZ / REG_EXPAND_SZ value may be split over several Big Data segments. 212 | // Transparently concatenate them in the output string. 213 | for slice_data in iter { 214 | let slice_data = slice_data?; 215 | 216 | let u16_iter = slice_data 217 | .chunks_exact(2) 218 | .map(|two_bytes| u16::from_le_bytes(two_bytes.try_into().unwrap())); 219 | 220 | // You hardly find emojis or other characters outside the UTF-16 Basic Multilingual Plane in registry data. 221 | // Hence, the count of UTF-16 code points is a good estimate for the final string length. 222 | string.reserve(u16_iter.len()); 223 | 224 | // Interpret the u16 chunks as UTF-16 code points for characters. Replace undecodable ones silently. 225 | let char_iter = 226 | char::decode_utf16(u16_iter).map(|x| x.unwrap_or(char::REPLACEMENT_CHARACTER)); 227 | 228 | for c in char_iter { 229 | // Some applications erroneously store NUL-terminated strings in the registry. 230 | // To cope with that, we either stop at the first NUL character or when no more characters are left, whatever comes first. 231 | if c == '\0' { 232 | return Ok(string); 233 | } else { 234 | string.push(c); 235 | } 236 | } 237 | } 238 | 239 | Ok(string) 240 | } 241 | 242 | /// Checks if this is a `REG_SZ` or `REG_EXPAND_SZ` Key Value 243 | /// and returns the data as a [`String`] in that case. 244 | #[cfg(feature = "alloc")] 245 | pub fn string_data(&'h self) -> Result { 246 | match self.data_type()? { 247 | KeyValueDataType::RegSZ | KeyValueDataType::RegExpandSZ => (), 248 | data_type => { 249 | return Err(NtHiveError::InvalidKeyValueDataType { 250 | expected: &[KeyValueDataType::RegSZ, KeyValueDataType::RegExpandSZ], 251 | actual: data_type, 252 | }); 253 | } 254 | } 255 | 256 | match self.data()? { 257 | KeyValueData::Small(data) => Self::utf16le_to_string_lossy(iter::once(Ok(data))), 258 | KeyValueData::Big(iter) => Self::utf16le_to_string_lossy(iter), 259 | } 260 | } 261 | 262 | /// Checks if this is a `REG_DWORD` or `REG_DWORD_BIG_ENDIAN` Key Value 263 | /// and returns the data as a [`u32`] in that case. 264 | pub fn dword_data(&self) -> Result { 265 | // DWORD data never needs a Big Data structure. 266 | if let KeyValueData::Small(data) = self.data()? { 267 | // DWORD data must be exactly 4 bytes long. 268 | if data.len() != mem::size_of::() { 269 | return Err(NtHiveError::InvalidDataSize { 270 | offset: self.hive.offset_of_field(&data), 271 | expected: mem::size_of::(), 272 | actual: data.len(), 273 | }); 274 | } 275 | 276 | // Ensure that this is a REG_DWORD or REG_DWORD_BIG_ENDIAN data type. 277 | match self.data_type()? { 278 | KeyValueDataType::RegDWord => Ok(u32::from_le_bytes(data.try_into().unwrap())), 279 | KeyValueDataType::RegDWordBigEndian => { 280 | Ok(u32::from_be_bytes(data.try_into().unwrap())) 281 | } 282 | data_type => Err(NtHiveError::InvalidKeyValueDataType { 283 | expected: &[ 284 | KeyValueDataType::RegDWord, 285 | KeyValueDataType::RegDWordBigEndian, 286 | ], 287 | actual: data_type, 288 | }), 289 | } 290 | } else { 291 | // We got a Big Data structure and this can only happen if the data 292 | // is much longer than a single DWORD. 293 | Err(NtHiveError::InvalidDataSize { 294 | offset: self 295 | .hive 296 | .offset_of_data_offset(self.header().data_offset.get() as usize), 297 | expected: mem::size_of::(), 298 | actual: self.data_size() as usize, 299 | }) 300 | } 301 | } 302 | 303 | /// Checks if this is a `REG_MULTI_SZ` Key Value 304 | /// and returns an iterator over [`String`]s for each line in that case. 305 | #[cfg(feature = "alloc")] 306 | pub fn multi_string_data(&self) -> Result> { 307 | // Ensure that this is a REG_MULTI_SZ data type. 308 | match self.data_type()? { 309 | KeyValueDataType::RegMultiSZ => (), 310 | data_type => { 311 | return Err(NtHiveError::InvalidKeyValueDataType { 312 | expected: &[KeyValueDataType::RegMultiSZ], 313 | actual: data_type, 314 | }); 315 | } 316 | } 317 | 318 | match self.data()? { 319 | KeyValueData::Small(data) => Ok(RegMultiSZStrings::small(data)), 320 | KeyValueData::Big(iter) => Ok(RegMultiSZStrings::big(iter)), 321 | } 322 | } 323 | 324 | /// Checks if this is a `REG_QWORD` Key Value 325 | /// and returns the data as a [`u64`] in that case. 326 | pub fn qword_data(&self) -> Result { 327 | // QWORD data never needs a Big Data structure. 328 | if let KeyValueData::Small(data) = self.data()? { 329 | // QWORD data must be exactly 8 bytes long. 330 | if data.len() != mem::size_of::() { 331 | return Err(NtHiveError::InvalidDataSize { 332 | offset: self.hive.offset_of_field(&data), 333 | expected: mem::size_of::(), 334 | actual: data.len(), 335 | }); 336 | } 337 | 338 | // Ensure that this is a REG_QWORD data type. 339 | match self.data_type()? { 340 | KeyValueDataType::RegQWord => Ok(u64::from_le_bytes(data.try_into().unwrap())), 341 | data_type => Err(NtHiveError::InvalidKeyValueDataType { 342 | expected: &[KeyValueDataType::RegQWord], 343 | actual: data_type, 344 | }), 345 | } 346 | } else { 347 | // We got a Big Data structure and this can only happen if the data 348 | // is much longer than a single QWORD. 349 | Err(NtHiveError::InvalidDataSize { 350 | offset: self 351 | .hive 352 | .offset_of_data_offset(self.header().data_offset.get() as usize), 353 | expected: mem::size_of::(), 354 | actual: self.data_size() as usize, 355 | }) 356 | } 357 | } 358 | 359 | /// Returns the size of the raw data. 360 | pub fn data_size(&self) -> u32 { 361 | let header = self.header(); 362 | header.data_size.get() & !DATA_STORED_IN_DATA_OFFSET 363 | } 364 | 365 | /// Returns the data type of this Key Value. 366 | pub fn data_type(&self) -> Result { 367 | let header = self.header(); 368 | let data_type_code = header.data_type.get(); 369 | 370 | KeyValueDataType::n(data_type_code).ok_or_else(|| { 371 | NtHiveError::UnsupportedKeyValueDataType { 372 | offset: self.hive.offset_of_field(&header.data_type), 373 | actual: data_type_code, 374 | } 375 | }) 376 | } 377 | 378 | /// Returns the name of this Key Value. 379 | pub fn name(&self) -> Result> { 380 | let header = self.header(); 381 | let flags = KeyValueFlags::from_bits_truncate(header.flags.get()); 382 | let name_length = header.name_length.get() as usize; 383 | 384 | let name_range = byte_subrange(&self.data_range, name_length).ok_or_else(|| { 385 | NtHiveError::InvalidSizeField { 386 | offset: self.hive.offset_of_field(&header.name_length), 387 | expected: name_length, 388 | actual: self.data_range.len(), 389 | } 390 | })?; 391 | let name_bytes = &self.hive.data[name_range]; 392 | 393 | if flags.contains(KeyValueFlags::VALUE_COMP_NAME) { 394 | Ok(NtHiveNameString::Latin1(name_bytes)) 395 | } else { 396 | Ok(NtHiveNameString::Utf16LE(name_bytes)) 397 | } 398 | } 399 | 400 | fn validate_signature(&self) -> Result<()> { 401 | let header = self.header(); 402 | let signature = &header.signature; 403 | let expected_signature = b"vk"; 404 | 405 | if signature == expected_signature { 406 | Ok(()) 407 | } else { 408 | Err(NtHiveError::InvalidTwoByteSignature { 409 | offset: self.hive.offset_of_field(signature), 410 | expected: expected_signature, 411 | actual: *signature, 412 | }) 413 | } 414 | } 415 | } 416 | 417 | impl PartialEq for KeyValue<'_, B> 418 | where 419 | B: SplitByteSlice, 420 | { 421 | fn eq(&self, other: &Self) -> bool { 422 | ptr::eq(self.hive, other.hive) 423 | && self.header_range == other.header_range 424 | && self.data_range == other.data_range 425 | } 426 | } 427 | 428 | impl Eq for KeyValue<'_, B> where B: SplitByteSlice {} 429 | 430 | #[cfg(feature = "alloc")] 431 | type RegMultiSZCharIter<'h> = Map< 432 | DecodeUtf16, fn(&'h [u8]) -> u16>>, 433 | fn(Result) -> char, 434 | >; 435 | 436 | #[cfg(feature = "alloc")] 437 | #[derive(Clone)] 438 | pub struct RegMultiSZStrings<'h, B> 439 | where 440 | B: SplitByteSlice + 'h, 441 | { 442 | char_iter: Option>, 443 | big_iter: Option>, 444 | } 445 | 446 | #[cfg(feature = "alloc")] 447 | impl<'h, B> RegMultiSZStrings<'h, B> 448 | where 449 | B: SplitByteSlice + 'h, 450 | { 451 | fn small(data: &'h [u8]) -> Self { 452 | Self { 453 | char_iter: Some(Self::make_char_iter(data)), 454 | big_iter: None, 455 | } 456 | } 457 | 458 | fn big(iter: BigDataSlices<'h, B>) -> Self { 459 | Self { 460 | char_iter: None, 461 | big_iter: Some(iter), 462 | } 463 | } 464 | 465 | fn make_char_iter(slice_data: &'h [u8]) -> RegMultiSZCharIter<'h> { 466 | let u16_iter = slice_data 467 | .chunks_exact(2) 468 | .map(Self::u16_from_le_bytes as fn(&[u8]) -> u16); 469 | char::decode_utf16(u16_iter).map( 470 | Self::unwrap_or_replacement_character as fn(Result) -> char, 471 | ) 472 | } 473 | 474 | fn u16_from_le_bytes(two_bytes: &[u8]) -> u16 { 475 | u16::from_le_bytes(two_bytes.try_into().unwrap()) 476 | } 477 | 478 | fn unwrap_or_replacement_character(input: Result) -> char { 479 | input.unwrap_or(char::REPLACEMENT_CHARACTER) 480 | } 481 | } 482 | 483 | #[cfg(feature = "alloc")] 484 | impl<'h, B> Iterator for RegMultiSZStrings<'h, B> 485 | where 486 | B: SplitByteSlice + 'h, 487 | { 488 | type Item = Result; 489 | 490 | fn next(&mut self) -> Option { 491 | let mut string = String::new(); 492 | 493 | 'outer_loop: loop { 494 | let char_iter = match self.char_iter.as_mut() { 495 | Some(char_iter) => char_iter, 496 | None => { 497 | let big_iter = match self.big_iter.as_mut() { 498 | Some(big_iter) => big_iter, 499 | None => break 'outer_loop, 500 | }; 501 | let slice_data = match big_iter.next() { 502 | Some(Ok(slice_data)) => slice_data, 503 | Some(Err(e)) => return Some(Err(e)), 504 | None => break 'outer_loop, 505 | }; 506 | let char_iter = Self::make_char_iter(slice_data); 507 | self.char_iter = Some(char_iter); 508 | continue 'outer_loop; 509 | } 510 | }; 511 | 512 | for c in char_iter { 513 | // REG_MULTI_SZ data consists of multiple strings each terminated by a NUL character. 514 | // The final string has a double-NUL termination. 515 | // 516 | // However, we will happily accept data without terminating NUL characters as well. 517 | if c == '\0' { 518 | break 'outer_loop; 519 | } else { 520 | string.push(c); 521 | } 522 | } 523 | 524 | // We have fully iterated all characters of this slice. 525 | // Get a new `char_iter` in the next iteration of the outer loop, and concatenate characters 526 | // to our `string` until we find a NUL or no more data. 527 | self.char_iter = None; 528 | } 529 | 530 | if string.is_empty() { 531 | None 532 | } else { 533 | Some(Ok(string)) 534 | } 535 | } 536 | } 537 | 538 | #[cfg(feature = "alloc")] 539 | impl<'h, B> FusedIterator for RegMultiSZStrings<'h, B> where B: SplitByteSlice + 'h {} 540 | 541 | #[cfg(test)] 542 | mod tests { 543 | use crate::*; 544 | 545 | #[test] 546 | fn test_data() { 547 | // Get Key Values of all data types we support and prove that we correctly 548 | // read their data. 549 | let testhive = crate::helpers::tests::testhive_vec(); 550 | let hive = Hive::new(testhive.as_ref()).unwrap(); 551 | let root_key_node = hive.root_key_node().unwrap(); 552 | let key_node = root_key_node.subkey("data-test").unwrap().unwrap(); 553 | 554 | let key_value = key_node.value("reg-sz").unwrap().unwrap(); 555 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegSZ); 556 | assert_eq!(key_value.string_data().unwrap(), "sz-test"); 557 | 558 | let key_value = key_node 559 | .value("reg-sz-with-terminating-nul") 560 | .unwrap() 561 | .unwrap(); 562 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegSZ); 563 | assert_eq!(key_value.string_data().unwrap(), "sz-test"); 564 | 565 | let key_value = key_node.value("reg-expand-sz").unwrap().unwrap(); 566 | assert_eq!( 567 | key_value.data_type().unwrap(), 568 | KeyValueDataType::RegExpandSZ 569 | ); 570 | assert_eq!(key_value.string_data().unwrap(), "sz-test"); 571 | 572 | let key_value = key_node.value("reg-multi-sz").unwrap().unwrap(); 573 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegMultiSZ); 574 | let mut iter = key_value.multi_string_data().unwrap(); 575 | assert_eq!(iter.next(), Some(Ok("multi-sz-test".to_owned()))); 576 | assert_eq!(iter.next(), Some(Ok("line2".to_owned()))); 577 | assert_eq!(iter.next(), None); 578 | assert_eq!(iter.next(), None); 579 | 580 | let key_value = key_node.value("reg-multi-sz-big").unwrap().unwrap(); 581 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegMultiSZ); 582 | let mut iter = key_value.multi_string_data().unwrap(); 583 | assert_eq!(iter.next(), Some(Ok("0123456789".repeat(820)))); 584 | assert_eq!(iter.next(), Some(Ok("0123456789".to_owned()))); 585 | assert_eq!(iter.next(), None); 586 | assert_eq!(iter.next(), None); 587 | 588 | let key_value = key_node.value("dword").unwrap().unwrap(); 589 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegDWord); 590 | assert_eq!(key_value.dword_data().unwrap(), 42); 591 | 592 | // offreg-testhive-writer has stored the same bytes representing '42' in 593 | // little-endian for the big-endian case. 594 | // Thus, we must get a numeric value of 42 << 24 = 704643072 after 595 | // interpreting the same bytes as a big-endian value. 596 | let key_value = key_node.value("dword-big-endian").unwrap().unwrap(); 597 | assert_eq!( 598 | key_value.data_type().unwrap(), 599 | KeyValueDataType::RegDWordBigEndian 600 | ); 601 | assert_eq!(key_value.dword_data().unwrap(), 42 << 24); 602 | 603 | let key_value = key_node.value("qword").unwrap().unwrap(); 604 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegQWord); 605 | assert_eq!(key_value.qword_data().unwrap(), u64::MAX); 606 | 607 | let key_value = key_node.value("binary").unwrap().unwrap(); 608 | let key_value_data = key_value.data().unwrap(); 609 | assert_eq!(key_value.data_type().unwrap(), KeyValueDataType::RegBinary); 610 | assert!(matches!(key_value_data, KeyValueData::Small(_))); 611 | assert_eq!(key_value_data.into_vec().unwrap(), vec![1, 2, 3, 4, 5]); 612 | } 613 | } 614 | -------------------------------------------------------------------------------- /src/key_node.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::cmp::Ordering; 5 | use core::mem; 6 | use core::ops::Range; 7 | use core::ptr; 8 | 9 | use bitflags::bitflags; 10 | use zerocopy::byteorder::LittleEndian; 11 | use zerocopy::{ 12 | FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, 13 | Unaligned, U16, U32, U64, 14 | }; 15 | 16 | use crate::error::{NtHiveError, Result}; 17 | use crate::helpers::byte_subrange; 18 | use crate::hive::Hive; 19 | use crate::index_root::IndexRootItemRanges; 20 | use crate::key_value::KeyValue; 21 | use crate::key_values_list::KeyValues; 22 | use crate::leaf::{LeafItemRange, LeafItemRanges}; 23 | use crate::string::NtHiveNameString; 24 | use crate::subkeys_list::{SubKeyNodes, SubKeyNodesMut}; 25 | 26 | bitflags! { 27 | struct KeyNodeFlags: u16 { 28 | /// This is a volatile key (not stored on disk). 29 | const KEY_IS_VOLATILE = 0x0001; 30 | /// This is the mount point of another hive (not stored on disk). 31 | const KEY_HIVE_EXIT = 0x0002; 32 | /// This is the root key. 33 | const KEY_HIVE_ENTRY = 0x0004; 34 | /// This key cannot be deleted. 35 | const KEY_NO_DELETE = 0x0008; 36 | /// This key is a symbolic link. 37 | const KEY_SYM_LINK = 0x0010; 38 | /// The key name is in (extended) ASCII instead of UTF-16LE. 39 | const KEY_COMP_NAME = 0x0020; 40 | /// This key is a predefined handle. 41 | const KEY_PREDEF_HANDLE = 0x0040; 42 | /// This key was virtualized at least once. 43 | const KEY_VIRT_MIRRORED = 0x0080; 44 | /// This is a virtual key. 45 | const KEY_VIRT_TARGET = 0x0100; 46 | /// This key is part of a virtual store path. 47 | const KEY_VIRTUAL_STORE = 0x0200; 48 | } 49 | } 50 | 51 | /// On-Disk Structure of a Key Node header. 52 | #[allow(dead_code)] 53 | #[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)] 54 | #[repr(packed)] 55 | struct KeyNodeHeader { 56 | signature: [u8; 2], 57 | flags: U16, 58 | timestamp: U64, 59 | spare: U32, 60 | parent: U32, 61 | subkey_count: U32, 62 | volatile_subkey_count: U32, 63 | subkeys_list_offset: U32, 64 | volatile_subkeys_list_offset: U32, 65 | key_values_count: U32, 66 | key_values_list_offset: U32, 67 | key_security_offset: U32, 68 | class_name_offset: U32, 69 | max_subkey_name: U32, 70 | max_subkey_class_name: U32, 71 | max_value_name: U32, 72 | max_value_data: U32, 73 | work_var: U32, 74 | key_name_length: U16, 75 | class_name_length: U16, 76 | } 77 | 78 | /// Byte range of a single Key Node item. 79 | #[derive(Clone, Eq, PartialEq)] 80 | struct KeyNodeItemRange { 81 | header_range: Range, 82 | data_range: Range, 83 | } 84 | 85 | impl KeyNodeItemRange { 86 | fn from_cell_range(hive: &Hive, cell_range: Range) -> Result 87 | where 88 | B: SplitByteSlice, 89 | { 90 | let header_range = 91 | byte_subrange(&cell_range, mem::size_of::()).ok_or_else(|| { 92 | NtHiveError::InvalidHeaderSize { 93 | offset: hive.offset_of_data_offset(cell_range.start), 94 | expected: mem::size_of::(), 95 | actual: cell_range.len(), 96 | } 97 | })?; 98 | let data_range = header_range.end..cell_range.end; 99 | 100 | let key_node_item_range = Self { 101 | header_range, 102 | data_range, 103 | }; 104 | key_node_item_range.validate_signature(hive)?; 105 | 106 | Ok(key_node_item_range) 107 | } 108 | 109 | fn from_leaf_item_range(hive: &Hive, leaf_item_range: LeafItemRange) -> Result 110 | where 111 | B: SplitByteSlice, 112 | { 113 | let key_node_offset = leaf_item_range.key_node_offset(hive); 114 | let cell_range = hive.cell_range_from_data_offset(key_node_offset)?; 115 | let key_node = Self::from_cell_range(hive, cell_range)?; 116 | Ok(key_node) 117 | } 118 | 119 | fn binary_search_subkey_in_index_root( 120 | &self, 121 | hive: &Hive, 122 | name: &str, 123 | index_root_item_ranges: IndexRootItemRanges, 124 | ) -> Option> 125 | where 126 | B: SplitByteSlice, 127 | { 128 | // The following textbook binary search algorithm requires signed math. 129 | // Fortunately, Index Roots have a u16 `count` field, hence we should be able to convert to i32. 130 | assert!(index_root_item_ranges.len() <= u16::MAX as usize); 131 | let mut left = 0i32; 132 | let mut right = index_root_item_ranges.len() as i32 - 1; 133 | 134 | while left <= right { 135 | // Select the middle Index Root item given the current boundaries and get an 136 | // iterator over its Leaf items. 137 | let mid = (left + right) / 2; 138 | 139 | let index_root_item_range = index_root_item_ranges.clone().nth(mid as usize).unwrap(); 140 | let leaf_item_ranges = iter_try!(LeafItemRanges::from_index_root_item_range( 141 | hive, 142 | index_root_item_range 143 | )); 144 | 145 | // Check the name of the FIRST Key Node of the selected Index Root item. 146 | let leaf_item_range = leaf_item_ranges.clone().next().unwrap(); 147 | let key_node_item_range = iter_try!(Self::from_leaf_item_range(hive, leaf_item_range)); 148 | let key_node_name = iter_try!(key_node_item_range.name(hive)); 149 | 150 | match key_node_name.partial_cmp(name).unwrap() { 151 | Ordering::Equal => return Some(Ok(key_node_item_range)), 152 | Ordering::Less => (), 153 | Ordering::Greater => { 154 | // The FIRST Key Node of the selected Index Root item has a name that comes 155 | // AFTER the name we are looking for. 156 | // Hence, the searched Key Node must be in an Index Root item BEFORE the selected one. 157 | right = mid - 1; 158 | continue; 159 | } 160 | } 161 | 162 | // Check the name of the LAST Key Node of the selected Index Root item. 163 | let leaf_item_range = leaf_item_ranges.clone().last().unwrap(); 164 | let key_node_item_range = iter_try!(Self::from_leaf_item_range(hive, leaf_item_range)); 165 | let key_node_name = iter_try!(key_node_item_range.name(hive)); 166 | 167 | match key_node_name.partial_cmp(name).unwrap() { 168 | Ordering::Equal => return Some(Ok(key_node_item_range)), 169 | Ordering::Less => { 170 | // The LAST Key Node of the selected Index Root item has a name that comes 171 | // BEFORE the name we are looking for. 172 | // Hence, the searched Key Node must be in an Index Root item AFTER the selected one. 173 | left = mid + 1; 174 | continue; 175 | } 176 | Ordering::Greater => (), 177 | } 178 | 179 | // If the searched Key Node exists at all, it must be in this Leaf. 180 | return self.binary_search_subkey_in_leaf(hive, name, leaf_item_ranges); 181 | } 182 | 183 | None 184 | } 185 | 186 | fn binary_search_subkey_in_leaf( 187 | &self, 188 | hive: &Hive, 189 | name: &str, 190 | leaf_item_ranges: LeafItemRanges, 191 | ) -> Option> 192 | where 193 | B: SplitByteSlice, 194 | { 195 | // The following textbook binary search algorithm requires signed math. 196 | // Fortunately, Leafs have a u16 `count` field, hence we should be able to convert to i32. 197 | assert!(leaf_item_ranges.len() <= u16::MAX as usize); 198 | let mut left = 0i32; 199 | let mut right = leaf_item_ranges.len() as i32 - 1; 200 | 201 | while left <= right { 202 | // Select the middle Leaf item given the current boundaries and get its name. 203 | let mid = (left + right) / 2; 204 | 205 | let leaf_item_range = leaf_item_ranges.clone().nth(mid as usize).unwrap(); 206 | let key_node_item_range = iter_try!(Self::from_leaf_item_range(hive, leaf_item_range)); 207 | let key_node_name = iter_try!(key_node_item_range.name(hive)); 208 | 209 | // Check if it's the name we are looking for, otherwise adjust the boundaries accordingly. 210 | match key_node_name.partial_cmp(name).unwrap() { 211 | Ordering::Equal => return Some(Ok(key_node_item_range)), 212 | Ordering::Less => left = mid + 1, 213 | Ordering::Greater => right = mid - 1, 214 | } 215 | } 216 | 217 | None 218 | } 219 | 220 | fn class_name<'h, B>(&self, hive: &'h Hive) -> Option>> 221 | where 222 | B: SplitByteSlice, 223 | { 224 | let header = self.header(hive); 225 | let class_name_offset = header.class_name_offset.get(); 226 | if class_name_offset == u32::MAX { 227 | // This Key Node has no Class Name. 228 | return None; 229 | } 230 | 231 | let class_name_length = header.class_name_length.get() as usize; 232 | let class_name_offset_range = 233 | iter_try!(hive.cell_range_from_data_offset(class_name_offset)); 234 | 235 | let class_name_range = iter_try!(byte_subrange( 236 | &class_name_offset_range, 237 | class_name_length 238 | ) 239 | .ok_or_else(|| NtHiveError::InvalidSizeField { 240 | offset: hive.offset_of_field(&header.class_name_length), 241 | expected: class_name_length, 242 | actual: class_name_offset_range.len(), 243 | })); 244 | let class_name_bytes = &hive.data[class_name_range]; 245 | 246 | Some(Ok(NtHiveNameString::Utf16LE(class_name_bytes))) 247 | } 248 | 249 | fn header<'h, B>(&self, hive: &'h Hive) -> Ref<&'h [u8], KeyNodeHeader> 250 | where 251 | B: SplitByteSlice, 252 | { 253 | Ref::from_bytes(&hive.data[self.header_range.clone()]).unwrap() 254 | } 255 | 256 | fn header_mut<'h, B>(&self, hive: &'h mut Hive) -> Ref<&'h mut [u8], KeyNodeHeader> 257 | where 258 | B: SplitByteSliceMut, 259 | { 260 | Ref::from_bytes(&mut hive.data[self.header_range.clone()]).unwrap() 261 | } 262 | 263 | fn name<'h, B>(&self, hive: &'h Hive) -> Result> 264 | where 265 | B: SplitByteSlice, 266 | { 267 | let header = self.header(hive); 268 | let flags = KeyNodeFlags::from_bits_truncate(header.flags.get()); 269 | let key_name_length = header.key_name_length.get() as usize; 270 | 271 | let key_name_range = byte_subrange(&self.data_range, key_name_length).ok_or_else(|| { 272 | NtHiveError::InvalidSizeField { 273 | offset: hive.offset_of_field(&header.key_name_length), 274 | expected: key_name_length, 275 | actual: self.data_range.len(), 276 | } 277 | })?; 278 | let key_name_bytes = &hive.data[key_name_range]; 279 | 280 | if flags.contains(KeyNodeFlags::KEY_COMP_NAME) { 281 | Ok(NtHiveNameString::Latin1(key_name_bytes)) 282 | } else { 283 | Ok(NtHiveNameString::Utf16LE(key_name_bytes)) 284 | } 285 | } 286 | 287 | fn subkey(&self, hive: &Hive, name: &str) -> Option> 288 | where 289 | B: SplitByteSlice, 290 | { 291 | let cell_range = iter_try!(self.subkeys_cell_range(hive)?); 292 | let subkeys = iter_try!(SubKeyNodes::new(hive, cell_range)); 293 | 294 | match subkeys { 295 | SubKeyNodes::IndexRoot(iter) => { 296 | let index_root_item_ranges = IndexRootItemRanges::from(iter); 297 | self.binary_search_subkey_in_index_root(hive, name, index_root_item_ranges) 298 | } 299 | SubKeyNodes::Leaf(iter) => { 300 | let leaf_item_ranges = LeafItemRanges::from(iter); 301 | self.binary_search_subkey_in_leaf(hive, name, leaf_item_ranges) 302 | } 303 | } 304 | } 305 | 306 | fn subkeys_cell_range(&self, hive: &Hive) -> Option>> 307 | where 308 | B: SplitByteSlice, 309 | { 310 | let header = self.header(hive); 311 | let subkeys_list_offset = header.subkeys_list_offset.get(); 312 | if subkeys_list_offset == u32::MAX { 313 | // This Key Node has no subkeys. 314 | return None; 315 | } 316 | 317 | let cell_range = iter_try!(hive.cell_range_from_data_offset(subkeys_list_offset)); 318 | Some(Ok(cell_range)) 319 | } 320 | 321 | fn subpath(&self, hive: &Hive, path: &str) -> Option> 322 | where 323 | B: SplitByteSlice, 324 | { 325 | let mut key_node_item_range = self.clone(); 326 | 327 | for component in path.split('\\') { 328 | // Just skip duplicate, leading, and trailing backslashes. 329 | if !component.is_empty() { 330 | key_node_item_range = iter_try!(key_node_item_range.subkey(hive, component)?); 331 | } 332 | } 333 | 334 | Some(Ok(key_node_item_range)) 335 | } 336 | 337 | fn validate_signature(&self, hive: &Hive) -> Result<()> 338 | where 339 | B: SplitByteSlice, 340 | { 341 | let header = self.header(hive); 342 | let signature = &header.signature; 343 | let expected_signature = b"nk"; 344 | 345 | if signature == expected_signature { 346 | Ok(()) 347 | } else { 348 | Err(NtHiveError::InvalidTwoByteSignature { 349 | offset: hive.offset_of_field(signature), 350 | expected: expected_signature, 351 | actual: *signature, 352 | }) 353 | } 354 | } 355 | 356 | fn value<'h, B>(&self, hive: &'h Hive, name: &str) -> Option>> 357 | where 358 | B: SplitByteSlice, 359 | { 360 | let mut values = iter_try!(self.values(hive)?); 361 | 362 | // Key Values are not sorted, so we can only iterate until we find a match. 363 | values.find(|key_value| { 364 | let key_value = match key_value { 365 | Ok(key_value) => key_value, 366 | Err(_) => return true, 367 | }; 368 | let key_value_name = match key_value.name() { 369 | Ok(name) => name, 370 | Err(_) => return true, 371 | }; 372 | 373 | key_value_name == name 374 | }) 375 | } 376 | 377 | fn values<'h, B>(&self, hive: &'h Hive) -> Option>> 378 | where 379 | B: SplitByteSlice, 380 | { 381 | let header = self.header(hive); 382 | let key_values_list_offset = header.key_values_list_offset.get(); 383 | if key_values_list_offset == u32::MAX { 384 | // This Key Node has no values. 385 | return None; 386 | } 387 | 388 | let cell_range = iter_try!(hive.cell_range_from_data_offset(key_values_list_offset)); 389 | let count = header.key_values_count.get(); 390 | let count_field_offset = hive.offset_of_field(&header.key_values_count); 391 | 392 | Some(KeyValues::new(hive, count, count_field_offset, cell_range)) 393 | } 394 | } 395 | 396 | /// A single key that belongs to a [`Hive`]. 397 | /// It has a name and possibly subkeys ([`KeyNode`]) and values ([`KeyValue`]). 398 | /// 399 | /// On-Disk Signature: `nk` 400 | /// 401 | /// [`KeyValue`]: crate::key_value::KeyValue 402 | #[derive(Clone)] 403 | pub struct KeyNode<'h, B: SplitByteSlice> { 404 | hive: &'h Hive, 405 | item_range: KeyNodeItemRange, 406 | } 407 | 408 | impl<'h, B> KeyNode<'h, B> 409 | where 410 | B: SplitByteSlice, 411 | { 412 | pub(crate) fn from_cell_range(hive: &'h Hive, cell_range: Range) -> Result { 413 | let item_range = KeyNodeItemRange::from_cell_range(hive, cell_range)?; 414 | Ok(Self { hive, item_range }) 415 | } 416 | 417 | pub(crate) fn from_leaf_item_range( 418 | hive: &'h Hive, 419 | leaf_item_range: LeafItemRange, 420 | ) -> Result { 421 | let item_range = KeyNodeItemRange::from_leaf_item_range(hive, leaf_item_range)?; 422 | Ok(Self { hive, item_range }) 423 | } 424 | 425 | /// Returns the class name of this Key Node (if any). 426 | pub fn class_name(&self) -> Option> { 427 | self.item_range.class_name(self.hive) 428 | } 429 | 430 | /// Returns the name of this Key Node. 431 | pub fn name(&self) -> Result { 432 | self.item_range.name(self.hive) 433 | } 434 | 435 | /// Finds a single subkey by name using efficient binary search. 436 | pub fn subkey(&self, name: &str) -> Option>> { 437 | let item_range = iter_try!(self.item_range.subkey(self.hive, name)?); 438 | 439 | Some(Ok(KeyNode { 440 | hive: self.hive, 441 | item_range, 442 | })) 443 | } 444 | 445 | /// Returns an iterator over the subkeys of this Key Node. 446 | pub fn subkeys(&self) -> Option>> { 447 | let cell_range = iter_try!(self.item_range.subkeys_cell_range(self.hive)?); 448 | Some(SubKeyNodes::new(self.hive, cell_range)) 449 | } 450 | 451 | /// Traverses the given subpath and returns the [`KeyNode`] of the last path element. 452 | /// 453 | /// Path elements must be separated by backslashes. 454 | pub fn subpath(&self, path: &str) -> Option>> { 455 | let item_range = iter_try!(self.item_range.subpath(self.hive, path)?); 456 | 457 | Some(Ok(KeyNode { 458 | hive: self.hive, 459 | item_range, 460 | })) 461 | } 462 | 463 | /// Finds a single value by name. 464 | pub fn value(&self, name: &str) -> Option>> { 465 | self.item_range.value(self.hive, name) 466 | } 467 | 468 | /// Returns an iterator over the values of this Key Node. 469 | pub fn values(&self) -> Option>> { 470 | self.item_range.values(self.hive) 471 | } 472 | } 473 | 474 | impl PartialEq for KeyNode<'_, B> 475 | where 476 | B: SplitByteSlice, 477 | { 478 | fn eq(&self, other: &Self) -> bool { 479 | ptr::eq(self.hive, other.hive) && self.item_range == other.item_range 480 | } 481 | } 482 | 483 | impl Eq for KeyNode<'_, B> where B: SplitByteSlice {} 484 | 485 | pub(crate) struct KeyNodeMut<'h, B: SplitByteSliceMut> { 486 | hive: &'h mut Hive, 487 | item_range: KeyNodeItemRange, 488 | } 489 | 490 | impl<'h, B> KeyNodeMut<'h, B> 491 | where 492 | B: SplitByteSliceMut, 493 | { 494 | pub(crate) fn from_cell_range(hive: &'h mut Hive, cell_range: Range) -> Result { 495 | let item_range = KeyNodeItemRange::from_cell_range(hive, cell_range)?; 496 | Ok(Self { hive, item_range }) 497 | } 498 | 499 | pub(crate) fn from_leaf_item_range( 500 | hive: &'h mut Hive, 501 | leaf_item_range: LeafItemRange, 502 | ) -> Result { 503 | let item_range = KeyNodeItemRange::from_leaf_item_range(hive, leaf_item_range)?; 504 | Ok(Self { hive, item_range }) 505 | } 506 | 507 | pub(crate) fn clear_volatile_subkeys(&mut self) -> Result<()> { 508 | let mut header = self.item_range.header_mut(self.hive); 509 | header.volatile_subkey_count.set(0); 510 | 511 | if let Some(subkeys) = self.subkeys_mut() { 512 | let mut subkeys = subkeys?; 513 | while let Some(subkey) = subkeys.next() { 514 | subkey?.clear_volatile_subkeys()?; 515 | } 516 | } 517 | 518 | Ok(()) 519 | } 520 | 521 | pub(crate) fn subkeys_mut(&mut self) -> Option>> { 522 | let cell_range = iter_try!(self.item_range.subkeys_cell_range(self.hive)?); 523 | Some(SubKeyNodesMut::new(self.hive, cell_range)) 524 | } 525 | } 526 | 527 | #[cfg(test)] 528 | mod tests { 529 | use crate::*; 530 | 531 | #[test] 532 | fn test_character_encoding() { 533 | let testhive = crate::helpers::tests::testhive_vec(); 534 | let hive = Hive::new(testhive.as_ref()).unwrap(); 535 | let root_key_node = hive.root_key_node().unwrap(); 536 | let key_node = root_key_node 537 | .subkey("character-encoding-test") 538 | .unwrap() 539 | .unwrap(); 540 | 541 | // Prove that Latin1 characters are always stored with 1 byte per character. 542 | let subkey = key_node.subkey("äöü").unwrap().unwrap(); 543 | assert!(matches!( 544 | subkey.name().unwrap(), 545 | NtHiveNameString::Latin1(&[0xe4, 0xf6, 0xfc]) 546 | )); 547 | 548 | // Prove that all characters of the Unicode Basic Multilingual Plane are compared case-insensitively 549 | // by trying to find both "Full-Width Uppercase A" (U+FF21) and "Full-Width Lowercase A" (U+FF41), 550 | // and ending up with the same subkeys. 551 | let subkey1 = key_node.subkey("A").unwrap().unwrap(); 552 | let subkey2 = key_node.subkey("a").unwrap().unwrap(); 553 | assert!(subkey1 == subkey2); 554 | 555 | // Prove that this isn't the case outside the Unicode Basic Multilingual Plane 556 | // by trying the same for "Deseret Uppercase H" (U+10410) and "Deseret Lowercase H" (U+10438). 557 | let subkey1 = key_node.subkey("𐐐").unwrap().unwrap(); 558 | let subkey2 = key_node.subkey("𐐸").unwrap().unwrap(); 559 | assert!(subkey1 != subkey2); 560 | } 561 | 562 | #[test] 563 | fn test_subkey() { 564 | // Prove that our binary search algorithm finds every subkey of "subkey-test". 565 | let testhive = crate::helpers::tests::testhive_vec(); 566 | let hive = Hive::new(testhive.as_ref()).unwrap(); 567 | let root_key_node = hive.root_key_node().unwrap(); 568 | let key_node = root_key_node.subkey("subkey-test").unwrap().unwrap(); 569 | 570 | for i in 0..512 { 571 | let subkey_name = format!("key{i}"); 572 | assert!( 573 | matches!(key_node.subkey(&subkey_name), Some(Ok(_))), 574 | "Could not find subkey \"{subkey_name}\"" 575 | ); 576 | } 577 | } 578 | 579 | #[test] 580 | fn test_subkeys() { 581 | // Keep in mind that subkeys in the hive are sorted like key0, key1, key10, key11, ... 582 | // We can create the same order by adding them to a vector and sorting that vector. 583 | let mut key_names = Vec::with_capacity(512); 584 | for i in 0..512 { 585 | key_names.push(format!("key{}", i)); 586 | } 587 | 588 | key_names.sort_unstable(); 589 | 590 | // Iterate through subkeys of "subkey-test" and prove that they are sorted just like our vector. 591 | let testhive = crate::helpers::tests::testhive_vec(); 592 | let hive = Hive::new(testhive.as_ref()).unwrap(); 593 | let root_key_node = hive.root_key_node().unwrap(); 594 | let key_node = root_key_node.subkey("subkey-test").unwrap().unwrap(); 595 | 596 | let subkeys = key_node.subkeys().unwrap().unwrap(); 597 | 598 | for (subkey, expected_key_name) in subkeys.zip(key_names.iter()) { 599 | let subkey = subkey.unwrap(); 600 | assert_eq!(subkey.name().unwrap(), expected_key_name.as_str()); 601 | } 602 | } 603 | 604 | #[test] 605 | fn test_subpath() { 606 | let testhive = crate::helpers::tests::testhive_vec(); 607 | let hive = Hive::new(testhive.as_ref()).unwrap(); 608 | let root_key_node = hive.root_key_node().unwrap(); 609 | let key_node = root_key_node.subkey("subpath-test").unwrap().unwrap(); 610 | 611 | assert!(matches!(key_node.subpath("no-subkeys"), Some(Ok(_)))); 612 | assert!(matches!(key_node.subpath("\\no-subkeys"), Some(Ok(_)))); 613 | assert!(matches!(key_node.subpath("no-subkeys\\"), Some(Ok(_)))); 614 | assert!(matches!(key_node.subpath("\\no-subkeys\\"), Some(Ok(_)))); 615 | assert!(key_node.subpath("no-subkeys\\non-existing").is_none()); 616 | 617 | assert!(matches!( 618 | key_node.subpath("with-single-level-subkey"), 619 | Some(Ok(_)) 620 | )); 621 | assert!(matches!( 622 | key_node.subpath("with-single-level-subkey\\subkey"), 623 | Some(Ok(_)) 624 | )); 625 | assert!(matches!( 626 | key_node.subpath("with-single-level-subkey\\\\subkey"), 627 | Some(Ok(_)) 628 | )); 629 | assert!(matches!( 630 | key_node.subpath("with-single-level-subkey\\\\subkey\\"), 631 | Some(Ok(_)) 632 | )); 633 | assert!(key_node 634 | .subpath("with-single-level-subkey\\subkey\\non-existing-too") 635 | .is_none()); 636 | 637 | assert!(matches!( 638 | key_node.subpath("with-two-levels-of-subkeys\\subkey1\\subkey2"), 639 | Some(Ok(_)) 640 | )); 641 | assert!(matches!( 642 | key_node.subpath("with-two-levels-of-subkeys\\subkey1\\\\subkey2"), 643 | Some(Ok(_)) 644 | )); 645 | 646 | assert!(key_node.subpath("non-existing").is_none()); 647 | assert!(key_node.subpath("non-existing\\sub").is_none()); 648 | } 649 | } 650 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2025 Colin Finck 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | use core::char; 5 | use core::cmp::Ordering; 6 | use core::fmt; 7 | 8 | #[cfg(feature = "alloc")] 9 | use alloc::string::String; 10 | 11 | /// Sorted table of lowercase Basic Multilingual Plane (BMP) character code points and their uppercase equivalents. 12 | /// This is what Windows registry hives use to perform case-insensitive comparisons. 13 | /// 14 | /// Generated by a modified version of 15 | /// https://github.com/rust-lang/rust/tree/0d97f7a96877a96015d70ece41ad08bb7af12377/src/tools/unicode-table-generator 16 | /// 17 | /// In contrast to the original UPPERCASE_TABLE in core::unicode and char::to_uppercase, this table 18 | /// - only considers one-to-one mappings from UnicodeData.txt, but none of SpecialCasing.txt 19 | /// - only encompasses the Unicode Basic Multilingual Plane (BMP, first 0xffff characters) 20 | /// - uses `u16` instead of `char` for space efficiency 21 | static BMP_UPPERCASE_TABLE: &[(u16, u16)] = &[ 22 | (0x61, 0x41), 23 | (0x62, 0x42), 24 | (0x63, 0x43), 25 | (0x64, 0x44), 26 | (0x65, 0x45), 27 | (0x66, 0x46), 28 | (0x67, 0x47), 29 | (0x68, 0x48), 30 | (0x69, 0x49), 31 | (0x6a, 0x4a), 32 | (0x6b, 0x4b), 33 | (0x6c, 0x4c), 34 | (0x6d, 0x4d), 35 | (0x6e, 0x4e), 36 | (0x6f, 0x4f), 37 | (0x70, 0x50), 38 | (0x71, 0x51), 39 | (0x72, 0x52), 40 | (0x73, 0x53), 41 | (0x74, 0x54), 42 | (0x75, 0x55), 43 | (0x76, 0x56), 44 | (0x77, 0x57), 45 | (0x78, 0x58), 46 | (0x79, 0x59), 47 | (0x7a, 0x5a), 48 | (0xb5, 0x39c), 49 | (0xe0, 0xc0), 50 | (0xe1, 0xc1), 51 | (0xe2, 0xc2), 52 | (0xe3, 0xc3), 53 | (0xe4, 0xc4), 54 | (0xe5, 0xc5), 55 | (0xe6, 0xc6), 56 | (0xe7, 0xc7), 57 | (0xe8, 0xc8), 58 | (0xe9, 0xc9), 59 | (0xea, 0xca), 60 | (0xeb, 0xcb), 61 | (0xec, 0xcc), 62 | (0xed, 0xcd), 63 | (0xee, 0xce), 64 | (0xef, 0xcf), 65 | (0xf0, 0xd0), 66 | (0xf1, 0xd1), 67 | (0xf2, 0xd2), 68 | (0xf3, 0xd3), 69 | (0xf4, 0xd4), 70 | (0xf5, 0xd5), 71 | (0xf6, 0xd6), 72 | (0xf8, 0xd8), 73 | (0xf9, 0xd9), 74 | (0xfa, 0xda), 75 | (0xfb, 0xdb), 76 | (0xfc, 0xdc), 77 | (0xfd, 0xdd), 78 | (0xfe, 0xde), 79 | (0xff, 0x178), 80 | (0x101, 0x100), 81 | (0x103, 0x102), 82 | (0x105, 0x104), 83 | (0x107, 0x106), 84 | (0x109, 0x108), 85 | (0x10b, 0x10a), 86 | (0x10d, 0x10c), 87 | (0x10f, 0x10e), 88 | (0x111, 0x110), 89 | (0x113, 0x112), 90 | (0x115, 0x114), 91 | (0x117, 0x116), 92 | (0x119, 0x118), 93 | (0x11b, 0x11a), 94 | (0x11d, 0x11c), 95 | (0x11f, 0x11e), 96 | (0x121, 0x120), 97 | (0x123, 0x122), 98 | (0x125, 0x124), 99 | (0x127, 0x126), 100 | (0x129, 0x128), 101 | (0x12b, 0x12a), 102 | (0x12d, 0x12c), 103 | (0x12f, 0x12e), 104 | (0x131, 0x49), 105 | (0x133, 0x132), 106 | (0x135, 0x134), 107 | (0x137, 0x136), 108 | (0x13a, 0x139), 109 | (0x13c, 0x13b), 110 | (0x13e, 0x13d), 111 | (0x140, 0x13f), 112 | (0x142, 0x141), 113 | (0x144, 0x143), 114 | (0x146, 0x145), 115 | (0x148, 0x147), 116 | (0x14b, 0x14a), 117 | (0x14d, 0x14c), 118 | (0x14f, 0x14e), 119 | (0x151, 0x150), 120 | (0x153, 0x152), 121 | (0x155, 0x154), 122 | (0x157, 0x156), 123 | (0x159, 0x158), 124 | (0x15b, 0x15a), 125 | (0x15d, 0x15c), 126 | (0x15f, 0x15e), 127 | (0x161, 0x160), 128 | (0x163, 0x162), 129 | (0x165, 0x164), 130 | (0x167, 0x166), 131 | (0x169, 0x168), 132 | (0x16b, 0x16a), 133 | (0x16d, 0x16c), 134 | (0x16f, 0x16e), 135 | (0x171, 0x170), 136 | (0x173, 0x172), 137 | (0x175, 0x174), 138 | (0x177, 0x176), 139 | (0x17a, 0x179), 140 | (0x17c, 0x17b), 141 | (0x17e, 0x17d), 142 | (0x17f, 0x53), 143 | (0x180, 0x243), 144 | (0x183, 0x182), 145 | (0x185, 0x184), 146 | (0x188, 0x187), 147 | (0x18c, 0x18b), 148 | (0x192, 0x191), 149 | (0x195, 0x1f6), 150 | (0x199, 0x198), 151 | (0x19a, 0x23d), 152 | (0x19e, 0x220), 153 | (0x1a1, 0x1a0), 154 | (0x1a3, 0x1a2), 155 | (0x1a5, 0x1a4), 156 | (0x1a8, 0x1a7), 157 | (0x1ad, 0x1ac), 158 | (0x1b0, 0x1af), 159 | (0x1b4, 0x1b3), 160 | (0x1b6, 0x1b5), 161 | (0x1b9, 0x1b8), 162 | (0x1bd, 0x1bc), 163 | (0x1bf, 0x1f7), 164 | (0x1c5, 0x1c4), 165 | (0x1c6, 0x1c4), 166 | (0x1c8, 0x1c7), 167 | (0x1c9, 0x1c7), 168 | (0x1cb, 0x1ca), 169 | (0x1cc, 0x1ca), 170 | (0x1ce, 0x1cd), 171 | (0x1d0, 0x1cf), 172 | (0x1d2, 0x1d1), 173 | (0x1d4, 0x1d3), 174 | (0x1d6, 0x1d5), 175 | (0x1d8, 0x1d7), 176 | (0x1da, 0x1d9), 177 | (0x1dc, 0x1db), 178 | (0x1dd, 0x18e), 179 | (0x1df, 0x1de), 180 | (0x1e1, 0x1e0), 181 | (0x1e3, 0x1e2), 182 | (0x1e5, 0x1e4), 183 | (0x1e7, 0x1e6), 184 | (0x1e9, 0x1e8), 185 | (0x1eb, 0x1ea), 186 | (0x1ed, 0x1ec), 187 | (0x1ef, 0x1ee), 188 | (0x1f2, 0x1f1), 189 | (0x1f3, 0x1f1), 190 | (0x1f5, 0x1f4), 191 | (0x1f9, 0x1f8), 192 | (0x1fb, 0x1fa), 193 | (0x1fd, 0x1fc), 194 | (0x1ff, 0x1fe), 195 | (0x201, 0x200), 196 | (0x203, 0x202), 197 | (0x205, 0x204), 198 | (0x207, 0x206), 199 | (0x209, 0x208), 200 | (0x20b, 0x20a), 201 | (0x20d, 0x20c), 202 | (0x20f, 0x20e), 203 | (0x211, 0x210), 204 | (0x213, 0x212), 205 | (0x215, 0x214), 206 | (0x217, 0x216), 207 | (0x219, 0x218), 208 | (0x21b, 0x21a), 209 | (0x21d, 0x21c), 210 | (0x21f, 0x21e), 211 | (0x223, 0x222), 212 | (0x225, 0x224), 213 | (0x227, 0x226), 214 | (0x229, 0x228), 215 | (0x22b, 0x22a), 216 | (0x22d, 0x22c), 217 | (0x22f, 0x22e), 218 | (0x231, 0x230), 219 | (0x233, 0x232), 220 | (0x23c, 0x23b), 221 | (0x23f, 0x2c7e), 222 | (0x240, 0x2c7f), 223 | (0x242, 0x241), 224 | (0x247, 0x246), 225 | (0x249, 0x248), 226 | (0x24b, 0x24a), 227 | (0x24d, 0x24c), 228 | (0x24f, 0x24e), 229 | (0x250, 0x2c6f), 230 | (0x251, 0x2c6d), 231 | (0x252, 0x2c70), 232 | (0x253, 0x181), 233 | (0x254, 0x186), 234 | (0x256, 0x189), 235 | (0x257, 0x18a), 236 | (0x259, 0x18f), 237 | (0x25b, 0x190), 238 | (0x25c, 0xa7ab), 239 | (0x260, 0x193), 240 | (0x261, 0xa7ac), 241 | (0x263, 0x194), 242 | (0x265, 0xa78d), 243 | (0x266, 0xa7aa), 244 | (0x268, 0x197), 245 | (0x269, 0x196), 246 | (0x26a, 0xa7ae), 247 | (0x26b, 0x2c62), 248 | (0x26c, 0xa7ad), 249 | (0x26f, 0x19c), 250 | (0x271, 0x2c6e), 251 | (0x272, 0x19d), 252 | (0x275, 0x19f), 253 | (0x27d, 0x2c64), 254 | (0x280, 0x1a6), 255 | (0x282, 0xa7c5), 256 | (0x283, 0x1a9), 257 | (0x287, 0xa7b1), 258 | (0x288, 0x1ae), 259 | (0x289, 0x244), 260 | (0x28a, 0x1b1), 261 | (0x28b, 0x1b2), 262 | (0x28c, 0x245), 263 | (0x292, 0x1b7), 264 | (0x29d, 0xa7b2), 265 | (0x29e, 0xa7b0), 266 | (0x345, 0x399), 267 | (0x371, 0x370), 268 | (0x373, 0x372), 269 | (0x377, 0x376), 270 | (0x37b, 0x3fd), 271 | (0x37c, 0x3fe), 272 | (0x37d, 0x3ff), 273 | (0x3ac, 0x386), 274 | (0x3ad, 0x388), 275 | (0x3ae, 0x389), 276 | (0x3af, 0x38a), 277 | (0x3b1, 0x391), 278 | (0x3b2, 0x392), 279 | (0x3b3, 0x393), 280 | (0x3b4, 0x394), 281 | (0x3b5, 0x395), 282 | (0x3b6, 0x396), 283 | (0x3b7, 0x397), 284 | (0x3b8, 0x398), 285 | (0x3b9, 0x399), 286 | (0x3ba, 0x39a), 287 | (0x3bb, 0x39b), 288 | (0x3bc, 0x39c), 289 | (0x3bd, 0x39d), 290 | (0x3be, 0x39e), 291 | (0x3bf, 0x39f), 292 | (0x3c0, 0x3a0), 293 | (0x3c1, 0x3a1), 294 | (0x3c2, 0x3a3), 295 | (0x3c3, 0x3a3), 296 | (0x3c4, 0x3a4), 297 | (0x3c5, 0x3a5), 298 | (0x3c6, 0x3a6), 299 | (0x3c7, 0x3a7), 300 | (0x3c8, 0x3a8), 301 | (0x3c9, 0x3a9), 302 | (0x3ca, 0x3aa), 303 | (0x3cb, 0x3ab), 304 | (0x3cc, 0x38c), 305 | (0x3cd, 0x38e), 306 | (0x3ce, 0x38f), 307 | (0x3d0, 0x392), 308 | (0x3d1, 0x398), 309 | (0x3d5, 0x3a6), 310 | (0x3d6, 0x3a0), 311 | (0x3d7, 0x3cf), 312 | (0x3d9, 0x3d8), 313 | (0x3db, 0x3da), 314 | (0x3dd, 0x3dc), 315 | (0x3df, 0x3de), 316 | (0x3e1, 0x3e0), 317 | (0x3e3, 0x3e2), 318 | (0x3e5, 0x3e4), 319 | (0x3e7, 0x3e6), 320 | (0x3e9, 0x3e8), 321 | (0x3eb, 0x3ea), 322 | (0x3ed, 0x3ec), 323 | (0x3ef, 0x3ee), 324 | (0x3f0, 0x39a), 325 | (0x3f1, 0x3a1), 326 | (0x3f2, 0x3f9), 327 | (0x3f3, 0x37f), 328 | (0x3f5, 0x395), 329 | (0x3f8, 0x3f7), 330 | (0x3fb, 0x3fa), 331 | (0x430, 0x410), 332 | (0x431, 0x411), 333 | (0x432, 0x412), 334 | (0x433, 0x413), 335 | (0x434, 0x414), 336 | (0x435, 0x415), 337 | (0x436, 0x416), 338 | (0x437, 0x417), 339 | (0x438, 0x418), 340 | (0x439, 0x419), 341 | (0x43a, 0x41a), 342 | (0x43b, 0x41b), 343 | (0x43c, 0x41c), 344 | (0x43d, 0x41d), 345 | (0x43e, 0x41e), 346 | (0x43f, 0x41f), 347 | (0x440, 0x420), 348 | (0x441, 0x421), 349 | (0x442, 0x422), 350 | (0x443, 0x423), 351 | (0x444, 0x424), 352 | (0x445, 0x425), 353 | (0x446, 0x426), 354 | (0x447, 0x427), 355 | (0x448, 0x428), 356 | (0x449, 0x429), 357 | (0x44a, 0x42a), 358 | (0x44b, 0x42b), 359 | (0x44c, 0x42c), 360 | (0x44d, 0x42d), 361 | (0x44e, 0x42e), 362 | (0x44f, 0x42f), 363 | (0x450, 0x400), 364 | (0x451, 0x401), 365 | (0x452, 0x402), 366 | (0x453, 0x403), 367 | (0x454, 0x404), 368 | (0x455, 0x405), 369 | (0x456, 0x406), 370 | (0x457, 0x407), 371 | (0x458, 0x408), 372 | (0x459, 0x409), 373 | (0x45a, 0x40a), 374 | (0x45b, 0x40b), 375 | (0x45c, 0x40c), 376 | (0x45d, 0x40d), 377 | (0x45e, 0x40e), 378 | (0x45f, 0x40f), 379 | (0x461, 0x460), 380 | (0x463, 0x462), 381 | (0x465, 0x464), 382 | (0x467, 0x466), 383 | (0x469, 0x468), 384 | (0x46b, 0x46a), 385 | (0x46d, 0x46c), 386 | (0x46f, 0x46e), 387 | (0x471, 0x470), 388 | (0x473, 0x472), 389 | (0x475, 0x474), 390 | (0x477, 0x476), 391 | (0x479, 0x478), 392 | (0x47b, 0x47a), 393 | (0x47d, 0x47c), 394 | (0x47f, 0x47e), 395 | (0x481, 0x480), 396 | (0x48b, 0x48a), 397 | (0x48d, 0x48c), 398 | (0x48f, 0x48e), 399 | (0x491, 0x490), 400 | (0x493, 0x492), 401 | (0x495, 0x494), 402 | (0x497, 0x496), 403 | (0x499, 0x498), 404 | (0x49b, 0x49a), 405 | (0x49d, 0x49c), 406 | (0x49f, 0x49e), 407 | (0x4a1, 0x4a0), 408 | (0x4a3, 0x4a2), 409 | (0x4a5, 0x4a4), 410 | (0x4a7, 0x4a6), 411 | (0x4a9, 0x4a8), 412 | (0x4ab, 0x4aa), 413 | (0x4ad, 0x4ac), 414 | (0x4af, 0x4ae), 415 | (0x4b1, 0x4b0), 416 | (0x4b3, 0x4b2), 417 | (0x4b5, 0x4b4), 418 | (0x4b7, 0x4b6), 419 | (0x4b9, 0x4b8), 420 | (0x4bb, 0x4ba), 421 | (0x4bd, 0x4bc), 422 | (0x4bf, 0x4be), 423 | (0x4c2, 0x4c1), 424 | (0x4c4, 0x4c3), 425 | (0x4c6, 0x4c5), 426 | (0x4c8, 0x4c7), 427 | (0x4ca, 0x4c9), 428 | (0x4cc, 0x4cb), 429 | (0x4ce, 0x4cd), 430 | (0x4cf, 0x4c0), 431 | (0x4d1, 0x4d0), 432 | (0x4d3, 0x4d2), 433 | (0x4d5, 0x4d4), 434 | (0x4d7, 0x4d6), 435 | (0x4d9, 0x4d8), 436 | (0x4db, 0x4da), 437 | (0x4dd, 0x4dc), 438 | (0x4df, 0x4de), 439 | (0x4e1, 0x4e0), 440 | (0x4e3, 0x4e2), 441 | (0x4e5, 0x4e4), 442 | (0x4e7, 0x4e6), 443 | (0x4e9, 0x4e8), 444 | (0x4eb, 0x4ea), 445 | (0x4ed, 0x4ec), 446 | (0x4ef, 0x4ee), 447 | (0x4f1, 0x4f0), 448 | (0x4f3, 0x4f2), 449 | (0x4f5, 0x4f4), 450 | (0x4f7, 0x4f6), 451 | (0x4f9, 0x4f8), 452 | (0x4fb, 0x4fa), 453 | (0x4fd, 0x4fc), 454 | (0x4ff, 0x4fe), 455 | (0x501, 0x500), 456 | (0x503, 0x502), 457 | (0x505, 0x504), 458 | (0x507, 0x506), 459 | (0x509, 0x508), 460 | (0x50b, 0x50a), 461 | (0x50d, 0x50c), 462 | (0x50f, 0x50e), 463 | (0x511, 0x510), 464 | (0x513, 0x512), 465 | (0x515, 0x514), 466 | (0x517, 0x516), 467 | (0x519, 0x518), 468 | (0x51b, 0x51a), 469 | (0x51d, 0x51c), 470 | (0x51f, 0x51e), 471 | (0x521, 0x520), 472 | (0x523, 0x522), 473 | (0x525, 0x524), 474 | (0x527, 0x526), 475 | (0x529, 0x528), 476 | (0x52b, 0x52a), 477 | (0x52d, 0x52c), 478 | (0x52f, 0x52e), 479 | (0x561, 0x531), 480 | (0x562, 0x532), 481 | (0x563, 0x533), 482 | (0x564, 0x534), 483 | (0x565, 0x535), 484 | (0x566, 0x536), 485 | (0x567, 0x537), 486 | (0x568, 0x538), 487 | (0x569, 0x539), 488 | (0x56a, 0x53a), 489 | (0x56b, 0x53b), 490 | (0x56c, 0x53c), 491 | (0x56d, 0x53d), 492 | (0x56e, 0x53e), 493 | (0x56f, 0x53f), 494 | (0x570, 0x540), 495 | (0x571, 0x541), 496 | (0x572, 0x542), 497 | (0x573, 0x543), 498 | (0x574, 0x544), 499 | (0x575, 0x545), 500 | (0x576, 0x546), 501 | (0x577, 0x547), 502 | (0x578, 0x548), 503 | (0x579, 0x549), 504 | (0x57a, 0x54a), 505 | (0x57b, 0x54b), 506 | (0x57c, 0x54c), 507 | (0x57d, 0x54d), 508 | (0x57e, 0x54e), 509 | (0x57f, 0x54f), 510 | (0x580, 0x550), 511 | (0x581, 0x551), 512 | (0x582, 0x552), 513 | (0x583, 0x553), 514 | (0x584, 0x554), 515 | (0x585, 0x555), 516 | (0x586, 0x556), 517 | (0x10d0, 0x1c90), 518 | (0x10d1, 0x1c91), 519 | (0x10d2, 0x1c92), 520 | (0x10d3, 0x1c93), 521 | (0x10d4, 0x1c94), 522 | (0x10d5, 0x1c95), 523 | (0x10d6, 0x1c96), 524 | (0x10d7, 0x1c97), 525 | (0x10d8, 0x1c98), 526 | (0x10d9, 0x1c99), 527 | (0x10da, 0x1c9a), 528 | (0x10db, 0x1c9b), 529 | (0x10dc, 0x1c9c), 530 | (0x10dd, 0x1c9d), 531 | (0x10de, 0x1c9e), 532 | (0x10df, 0x1c9f), 533 | (0x10e0, 0x1ca0), 534 | (0x10e1, 0x1ca1), 535 | (0x10e2, 0x1ca2), 536 | (0x10e3, 0x1ca3), 537 | (0x10e4, 0x1ca4), 538 | (0x10e5, 0x1ca5), 539 | (0x10e6, 0x1ca6), 540 | (0x10e7, 0x1ca7), 541 | (0x10e8, 0x1ca8), 542 | (0x10e9, 0x1ca9), 543 | (0x10ea, 0x1caa), 544 | (0x10eb, 0x1cab), 545 | (0x10ec, 0x1cac), 546 | (0x10ed, 0x1cad), 547 | (0x10ee, 0x1cae), 548 | (0x10ef, 0x1caf), 549 | (0x10f0, 0x1cb0), 550 | (0x10f1, 0x1cb1), 551 | (0x10f2, 0x1cb2), 552 | (0x10f3, 0x1cb3), 553 | (0x10f4, 0x1cb4), 554 | (0x10f5, 0x1cb5), 555 | (0x10f6, 0x1cb6), 556 | (0x10f7, 0x1cb7), 557 | (0x10f8, 0x1cb8), 558 | (0x10f9, 0x1cb9), 559 | (0x10fa, 0x1cba), 560 | (0x10fd, 0x1cbd), 561 | (0x10fe, 0x1cbe), 562 | (0x10ff, 0x1cbf), 563 | (0x13f8, 0x13f0), 564 | (0x13f9, 0x13f1), 565 | (0x13fa, 0x13f2), 566 | (0x13fb, 0x13f3), 567 | (0x13fc, 0x13f4), 568 | (0x13fd, 0x13f5), 569 | (0x1c80, 0x412), 570 | (0x1c81, 0x414), 571 | (0x1c82, 0x41e), 572 | (0x1c83, 0x421), 573 | (0x1c84, 0x422), 574 | (0x1c85, 0x422), 575 | (0x1c86, 0x42a), 576 | (0x1c87, 0x462), 577 | (0x1c88, 0xa64a), 578 | (0x1d79, 0xa77d), 579 | (0x1d7d, 0x2c63), 580 | (0x1d8e, 0xa7c6), 581 | (0x1e01, 0x1e00), 582 | (0x1e03, 0x1e02), 583 | (0x1e05, 0x1e04), 584 | (0x1e07, 0x1e06), 585 | (0x1e09, 0x1e08), 586 | (0x1e0b, 0x1e0a), 587 | (0x1e0d, 0x1e0c), 588 | (0x1e0f, 0x1e0e), 589 | (0x1e11, 0x1e10), 590 | (0x1e13, 0x1e12), 591 | (0x1e15, 0x1e14), 592 | (0x1e17, 0x1e16), 593 | (0x1e19, 0x1e18), 594 | (0x1e1b, 0x1e1a), 595 | (0x1e1d, 0x1e1c), 596 | (0x1e1f, 0x1e1e), 597 | (0x1e21, 0x1e20), 598 | (0x1e23, 0x1e22), 599 | (0x1e25, 0x1e24), 600 | (0x1e27, 0x1e26), 601 | (0x1e29, 0x1e28), 602 | (0x1e2b, 0x1e2a), 603 | (0x1e2d, 0x1e2c), 604 | (0x1e2f, 0x1e2e), 605 | (0x1e31, 0x1e30), 606 | (0x1e33, 0x1e32), 607 | (0x1e35, 0x1e34), 608 | (0x1e37, 0x1e36), 609 | (0x1e39, 0x1e38), 610 | (0x1e3b, 0x1e3a), 611 | (0x1e3d, 0x1e3c), 612 | (0x1e3f, 0x1e3e), 613 | (0x1e41, 0x1e40), 614 | (0x1e43, 0x1e42), 615 | (0x1e45, 0x1e44), 616 | (0x1e47, 0x1e46), 617 | (0x1e49, 0x1e48), 618 | (0x1e4b, 0x1e4a), 619 | (0x1e4d, 0x1e4c), 620 | (0x1e4f, 0x1e4e), 621 | (0x1e51, 0x1e50), 622 | (0x1e53, 0x1e52), 623 | (0x1e55, 0x1e54), 624 | (0x1e57, 0x1e56), 625 | (0x1e59, 0x1e58), 626 | (0x1e5b, 0x1e5a), 627 | (0x1e5d, 0x1e5c), 628 | (0x1e5f, 0x1e5e), 629 | (0x1e61, 0x1e60), 630 | (0x1e63, 0x1e62), 631 | (0x1e65, 0x1e64), 632 | (0x1e67, 0x1e66), 633 | (0x1e69, 0x1e68), 634 | (0x1e6b, 0x1e6a), 635 | (0x1e6d, 0x1e6c), 636 | (0x1e6f, 0x1e6e), 637 | (0x1e71, 0x1e70), 638 | (0x1e73, 0x1e72), 639 | (0x1e75, 0x1e74), 640 | (0x1e77, 0x1e76), 641 | (0x1e79, 0x1e78), 642 | (0x1e7b, 0x1e7a), 643 | (0x1e7d, 0x1e7c), 644 | (0x1e7f, 0x1e7e), 645 | (0x1e81, 0x1e80), 646 | (0x1e83, 0x1e82), 647 | (0x1e85, 0x1e84), 648 | (0x1e87, 0x1e86), 649 | (0x1e89, 0x1e88), 650 | (0x1e8b, 0x1e8a), 651 | (0x1e8d, 0x1e8c), 652 | (0x1e8f, 0x1e8e), 653 | (0x1e91, 0x1e90), 654 | (0x1e93, 0x1e92), 655 | (0x1e95, 0x1e94), 656 | (0x1e9b, 0x1e60), 657 | (0x1ea1, 0x1ea0), 658 | (0x1ea3, 0x1ea2), 659 | (0x1ea5, 0x1ea4), 660 | (0x1ea7, 0x1ea6), 661 | (0x1ea9, 0x1ea8), 662 | (0x1eab, 0x1eaa), 663 | (0x1ead, 0x1eac), 664 | (0x1eaf, 0x1eae), 665 | (0x1eb1, 0x1eb0), 666 | (0x1eb3, 0x1eb2), 667 | (0x1eb5, 0x1eb4), 668 | (0x1eb7, 0x1eb6), 669 | (0x1eb9, 0x1eb8), 670 | (0x1ebb, 0x1eba), 671 | (0x1ebd, 0x1ebc), 672 | (0x1ebf, 0x1ebe), 673 | (0x1ec1, 0x1ec0), 674 | (0x1ec3, 0x1ec2), 675 | (0x1ec5, 0x1ec4), 676 | (0x1ec7, 0x1ec6), 677 | (0x1ec9, 0x1ec8), 678 | (0x1ecb, 0x1eca), 679 | (0x1ecd, 0x1ecc), 680 | (0x1ecf, 0x1ece), 681 | (0x1ed1, 0x1ed0), 682 | (0x1ed3, 0x1ed2), 683 | (0x1ed5, 0x1ed4), 684 | (0x1ed7, 0x1ed6), 685 | (0x1ed9, 0x1ed8), 686 | (0x1edb, 0x1eda), 687 | (0x1edd, 0x1edc), 688 | (0x1edf, 0x1ede), 689 | (0x1ee1, 0x1ee0), 690 | (0x1ee3, 0x1ee2), 691 | (0x1ee5, 0x1ee4), 692 | (0x1ee7, 0x1ee6), 693 | (0x1ee9, 0x1ee8), 694 | (0x1eeb, 0x1eea), 695 | (0x1eed, 0x1eec), 696 | (0x1eef, 0x1eee), 697 | (0x1ef1, 0x1ef0), 698 | (0x1ef3, 0x1ef2), 699 | (0x1ef5, 0x1ef4), 700 | (0x1ef7, 0x1ef6), 701 | (0x1ef9, 0x1ef8), 702 | (0x1efb, 0x1efa), 703 | (0x1efd, 0x1efc), 704 | (0x1eff, 0x1efe), 705 | (0x1f00, 0x1f08), 706 | (0x1f01, 0x1f09), 707 | (0x1f02, 0x1f0a), 708 | (0x1f03, 0x1f0b), 709 | (0x1f04, 0x1f0c), 710 | (0x1f05, 0x1f0d), 711 | (0x1f06, 0x1f0e), 712 | (0x1f07, 0x1f0f), 713 | (0x1f10, 0x1f18), 714 | (0x1f11, 0x1f19), 715 | (0x1f12, 0x1f1a), 716 | (0x1f13, 0x1f1b), 717 | (0x1f14, 0x1f1c), 718 | (0x1f15, 0x1f1d), 719 | (0x1f20, 0x1f28), 720 | (0x1f21, 0x1f29), 721 | (0x1f22, 0x1f2a), 722 | (0x1f23, 0x1f2b), 723 | (0x1f24, 0x1f2c), 724 | (0x1f25, 0x1f2d), 725 | (0x1f26, 0x1f2e), 726 | (0x1f27, 0x1f2f), 727 | (0x1f30, 0x1f38), 728 | (0x1f31, 0x1f39), 729 | (0x1f32, 0x1f3a), 730 | (0x1f33, 0x1f3b), 731 | (0x1f34, 0x1f3c), 732 | (0x1f35, 0x1f3d), 733 | (0x1f36, 0x1f3e), 734 | (0x1f37, 0x1f3f), 735 | (0x1f40, 0x1f48), 736 | (0x1f41, 0x1f49), 737 | (0x1f42, 0x1f4a), 738 | (0x1f43, 0x1f4b), 739 | (0x1f44, 0x1f4c), 740 | (0x1f45, 0x1f4d), 741 | (0x1f51, 0x1f59), 742 | (0x1f53, 0x1f5b), 743 | (0x1f55, 0x1f5d), 744 | (0x1f57, 0x1f5f), 745 | (0x1f60, 0x1f68), 746 | (0x1f61, 0x1f69), 747 | (0x1f62, 0x1f6a), 748 | (0x1f63, 0x1f6b), 749 | (0x1f64, 0x1f6c), 750 | (0x1f65, 0x1f6d), 751 | (0x1f66, 0x1f6e), 752 | (0x1f67, 0x1f6f), 753 | (0x1f70, 0x1fba), 754 | (0x1f71, 0x1fbb), 755 | (0x1f72, 0x1fc8), 756 | (0x1f73, 0x1fc9), 757 | (0x1f74, 0x1fca), 758 | (0x1f75, 0x1fcb), 759 | (0x1f76, 0x1fda), 760 | (0x1f77, 0x1fdb), 761 | (0x1f78, 0x1ff8), 762 | (0x1f79, 0x1ff9), 763 | (0x1f7a, 0x1fea), 764 | (0x1f7b, 0x1feb), 765 | (0x1f7c, 0x1ffa), 766 | (0x1f7d, 0x1ffb), 767 | (0x1f80, 0x1f88), 768 | (0x1f81, 0x1f89), 769 | (0x1f82, 0x1f8a), 770 | (0x1f83, 0x1f8b), 771 | (0x1f84, 0x1f8c), 772 | (0x1f85, 0x1f8d), 773 | (0x1f86, 0x1f8e), 774 | (0x1f87, 0x1f8f), 775 | (0x1f90, 0x1f98), 776 | (0x1f91, 0x1f99), 777 | (0x1f92, 0x1f9a), 778 | (0x1f93, 0x1f9b), 779 | (0x1f94, 0x1f9c), 780 | (0x1f95, 0x1f9d), 781 | (0x1f96, 0x1f9e), 782 | (0x1f97, 0x1f9f), 783 | (0x1fa0, 0x1fa8), 784 | (0x1fa1, 0x1fa9), 785 | (0x1fa2, 0x1faa), 786 | (0x1fa3, 0x1fab), 787 | (0x1fa4, 0x1fac), 788 | (0x1fa5, 0x1fad), 789 | (0x1fa6, 0x1fae), 790 | (0x1fa7, 0x1faf), 791 | (0x1fb0, 0x1fb8), 792 | (0x1fb1, 0x1fb9), 793 | (0x1fb3, 0x1fbc), 794 | (0x1fbe, 0x399), 795 | (0x1fc3, 0x1fcc), 796 | (0x1fd0, 0x1fd8), 797 | (0x1fd1, 0x1fd9), 798 | (0x1fe0, 0x1fe8), 799 | (0x1fe1, 0x1fe9), 800 | (0x1fe5, 0x1fec), 801 | (0x1ff3, 0x1ffc), 802 | (0x214e, 0x2132), 803 | (0x2170, 0x2160), 804 | (0x2171, 0x2161), 805 | (0x2172, 0x2162), 806 | (0x2173, 0x2163), 807 | (0x2174, 0x2164), 808 | (0x2175, 0x2165), 809 | (0x2176, 0x2166), 810 | (0x2177, 0x2167), 811 | (0x2178, 0x2168), 812 | (0x2179, 0x2169), 813 | (0x217a, 0x216a), 814 | (0x217b, 0x216b), 815 | (0x217c, 0x216c), 816 | (0x217d, 0x216d), 817 | (0x217e, 0x216e), 818 | (0x217f, 0x216f), 819 | (0x2184, 0x2183), 820 | (0x24d0, 0x24b6), 821 | (0x24d1, 0x24b7), 822 | (0x24d2, 0x24b8), 823 | (0x24d3, 0x24b9), 824 | (0x24d4, 0x24ba), 825 | (0x24d5, 0x24bb), 826 | (0x24d6, 0x24bc), 827 | (0x24d7, 0x24bd), 828 | (0x24d8, 0x24be), 829 | (0x24d9, 0x24bf), 830 | (0x24da, 0x24c0), 831 | (0x24db, 0x24c1), 832 | (0x24dc, 0x24c2), 833 | (0x24dd, 0x24c3), 834 | (0x24de, 0x24c4), 835 | (0x24df, 0x24c5), 836 | (0x24e0, 0x24c6), 837 | (0x24e1, 0x24c7), 838 | (0x24e2, 0x24c8), 839 | (0x24e3, 0x24c9), 840 | (0x24e4, 0x24ca), 841 | (0x24e5, 0x24cb), 842 | (0x24e6, 0x24cc), 843 | (0x24e7, 0x24cd), 844 | (0x24e8, 0x24ce), 845 | (0x24e9, 0x24cf), 846 | (0x2c30, 0x2c00), 847 | (0x2c31, 0x2c01), 848 | (0x2c32, 0x2c02), 849 | (0x2c33, 0x2c03), 850 | (0x2c34, 0x2c04), 851 | (0x2c35, 0x2c05), 852 | (0x2c36, 0x2c06), 853 | (0x2c37, 0x2c07), 854 | (0x2c38, 0x2c08), 855 | (0x2c39, 0x2c09), 856 | (0x2c3a, 0x2c0a), 857 | (0x2c3b, 0x2c0b), 858 | (0x2c3c, 0x2c0c), 859 | (0x2c3d, 0x2c0d), 860 | (0x2c3e, 0x2c0e), 861 | (0x2c3f, 0x2c0f), 862 | (0x2c40, 0x2c10), 863 | (0x2c41, 0x2c11), 864 | (0x2c42, 0x2c12), 865 | (0x2c43, 0x2c13), 866 | (0x2c44, 0x2c14), 867 | (0x2c45, 0x2c15), 868 | (0x2c46, 0x2c16), 869 | (0x2c47, 0x2c17), 870 | (0x2c48, 0x2c18), 871 | (0x2c49, 0x2c19), 872 | (0x2c4a, 0x2c1a), 873 | (0x2c4b, 0x2c1b), 874 | (0x2c4c, 0x2c1c), 875 | (0x2c4d, 0x2c1d), 876 | (0x2c4e, 0x2c1e), 877 | (0x2c4f, 0x2c1f), 878 | (0x2c50, 0x2c20), 879 | (0x2c51, 0x2c21), 880 | (0x2c52, 0x2c22), 881 | (0x2c53, 0x2c23), 882 | (0x2c54, 0x2c24), 883 | (0x2c55, 0x2c25), 884 | (0x2c56, 0x2c26), 885 | (0x2c57, 0x2c27), 886 | (0x2c58, 0x2c28), 887 | (0x2c59, 0x2c29), 888 | (0x2c5a, 0x2c2a), 889 | (0x2c5b, 0x2c2b), 890 | (0x2c5c, 0x2c2c), 891 | (0x2c5d, 0x2c2d), 892 | (0x2c5e, 0x2c2e), 893 | (0x2c61, 0x2c60), 894 | (0x2c65, 0x23a), 895 | (0x2c66, 0x23e), 896 | (0x2c68, 0x2c67), 897 | (0x2c6a, 0x2c69), 898 | (0x2c6c, 0x2c6b), 899 | (0x2c73, 0x2c72), 900 | (0x2c76, 0x2c75), 901 | (0x2c81, 0x2c80), 902 | (0x2c83, 0x2c82), 903 | (0x2c85, 0x2c84), 904 | (0x2c87, 0x2c86), 905 | (0x2c89, 0x2c88), 906 | (0x2c8b, 0x2c8a), 907 | (0x2c8d, 0x2c8c), 908 | (0x2c8f, 0x2c8e), 909 | (0x2c91, 0x2c90), 910 | (0x2c93, 0x2c92), 911 | (0x2c95, 0x2c94), 912 | (0x2c97, 0x2c96), 913 | (0x2c99, 0x2c98), 914 | (0x2c9b, 0x2c9a), 915 | (0x2c9d, 0x2c9c), 916 | (0x2c9f, 0x2c9e), 917 | (0x2ca1, 0x2ca0), 918 | (0x2ca3, 0x2ca2), 919 | (0x2ca5, 0x2ca4), 920 | (0x2ca7, 0x2ca6), 921 | (0x2ca9, 0x2ca8), 922 | (0x2cab, 0x2caa), 923 | (0x2cad, 0x2cac), 924 | (0x2caf, 0x2cae), 925 | (0x2cb1, 0x2cb0), 926 | (0x2cb3, 0x2cb2), 927 | (0x2cb5, 0x2cb4), 928 | (0x2cb7, 0x2cb6), 929 | (0x2cb9, 0x2cb8), 930 | (0x2cbb, 0x2cba), 931 | (0x2cbd, 0x2cbc), 932 | (0x2cbf, 0x2cbe), 933 | (0x2cc1, 0x2cc0), 934 | (0x2cc3, 0x2cc2), 935 | (0x2cc5, 0x2cc4), 936 | (0x2cc7, 0x2cc6), 937 | (0x2cc9, 0x2cc8), 938 | (0x2ccb, 0x2cca), 939 | (0x2ccd, 0x2ccc), 940 | (0x2ccf, 0x2cce), 941 | (0x2cd1, 0x2cd0), 942 | (0x2cd3, 0x2cd2), 943 | (0x2cd5, 0x2cd4), 944 | (0x2cd7, 0x2cd6), 945 | (0x2cd9, 0x2cd8), 946 | (0x2cdb, 0x2cda), 947 | (0x2cdd, 0x2cdc), 948 | (0x2cdf, 0x2cde), 949 | (0x2ce1, 0x2ce0), 950 | (0x2ce3, 0x2ce2), 951 | (0x2cec, 0x2ceb), 952 | (0x2cee, 0x2ced), 953 | (0x2cf3, 0x2cf2), 954 | (0x2d00, 0x10a0), 955 | (0x2d01, 0x10a1), 956 | (0x2d02, 0x10a2), 957 | (0x2d03, 0x10a3), 958 | (0x2d04, 0x10a4), 959 | (0x2d05, 0x10a5), 960 | (0x2d06, 0x10a6), 961 | (0x2d07, 0x10a7), 962 | (0x2d08, 0x10a8), 963 | (0x2d09, 0x10a9), 964 | (0x2d0a, 0x10aa), 965 | (0x2d0b, 0x10ab), 966 | (0x2d0c, 0x10ac), 967 | (0x2d0d, 0x10ad), 968 | (0x2d0e, 0x10ae), 969 | (0x2d0f, 0x10af), 970 | (0x2d10, 0x10b0), 971 | (0x2d11, 0x10b1), 972 | (0x2d12, 0x10b2), 973 | (0x2d13, 0x10b3), 974 | (0x2d14, 0x10b4), 975 | (0x2d15, 0x10b5), 976 | (0x2d16, 0x10b6), 977 | (0x2d17, 0x10b7), 978 | (0x2d18, 0x10b8), 979 | (0x2d19, 0x10b9), 980 | (0x2d1a, 0x10ba), 981 | (0x2d1b, 0x10bb), 982 | (0x2d1c, 0x10bc), 983 | (0x2d1d, 0x10bd), 984 | (0x2d1e, 0x10be), 985 | (0x2d1f, 0x10bf), 986 | (0x2d20, 0x10c0), 987 | (0x2d21, 0x10c1), 988 | (0x2d22, 0x10c2), 989 | (0x2d23, 0x10c3), 990 | (0x2d24, 0x10c4), 991 | (0x2d25, 0x10c5), 992 | (0x2d27, 0x10c7), 993 | (0x2d2d, 0x10cd), 994 | (0xa641, 0xa640), 995 | (0xa643, 0xa642), 996 | (0xa645, 0xa644), 997 | (0xa647, 0xa646), 998 | (0xa649, 0xa648), 999 | (0xa64b, 0xa64a), 1000 | (0xa64d, 0xa64c), 1001 | (0xa64f, 0xa64e), 1002 | (0xa651, 0xa650), 1003 | (0xa653, 0xa652), 1004 | (0xa655, 0xa654), 1005 | (0xa657, 0xa656), 1006 | (0xa659, 0xa658), 1007 | (0xa65b, 0xa65a), 1008 | (0xa65d, 0xa65c), 1009 | (0xa65f, 0xa65e), 1010 | (0xa661, 0xa660), 1011 | (0xa663, 0xa662), 1012 | (0xa665, 0xa664), 1013 | (0xa667, 0xa666), 1014 | (0xa669, 0xa668), 1015 | (0xa66b, 0xa66a), 1016 | (0xa66d, 0xa66c), 1017 | (0xa681, 0xa680), 1018 | (0xa683, 0xa682), 1019 | (0xa685, 0xa684), 1020 | (0xa687, 0xa686), 1021 | (0xa689, 0xa688), 1022 | (0xa68b, 0xa68a), 1023 | (0xa68d, 0xa68c), 1024 | (0xa68f, 0xa68e), 1025 | (0xa691, 0xa690), 1026 | (0xa693, 0xa692), 1027 | (0xa695, 0xa694), 1028 | (0xa697, 0xa696), 1029 | (0xa699, 0xa698), 1030 | (0xa69b, 0xa69a), 1031 | (0xa723, 0xa722), 1032 | (0xa725, 0xa724), 1033 | (0xa727, 0xa726), 1034 | (0xa729, 0xa728), 1035 | (0xa72b, 0xa72a), 1036 | (0xa72d, 0xa72c), 1037 | (0xa72f, 0xa72e), 1038 | (0xa733, 0xa732), 1039 | (0xa735, 0xa734), 1040 | (0xa737, 0xa736), 1041 | (0xa739, 0xa738), 1042 | (0xa73b, 0xa73a), 1043 | (0xa73d, 0xa73c), 1044 | (0xa73f, 0xa73e), 1045 | (0xa741, 0xa740), 1046 | (0xa743, 0xa742), 1047 | (0xa745, 0xa744), 1048 | (0xa747, 0xa746), 1049 | (0xa749, 0xa748), 1050 | (0xa74b, 0xa74a), 1051 | (0xa74d, 0xa74c), 1052 | (0xa74f, 0xa74e), 1053 | (0xa751, 0xa750), 1054 | (0xa753, 0xa752), 1055 | (0xa755, 0xa754), 1056 | (0xa757, 0xa756), 1057 | (0xa759, 0xa758), 1058 | (0xa75b, 0xa75a), 1059 | (0xa75d, 0xa75c), 1060 | (0xa75f, 0xa75e), 1061 | (0xa761, 0xa760), 1062 | (0xa763, 0xa762), 1063 | (0xa765, 0xa764), 1064 | (0xa767, 0xa766), 1065 | (0xa769, 0xa768), 1066 | (0xa76b, 0xa76a), 1067 | (0xa76d, 0xa76c), 1068 | (0xa76f, 0xa76e), 1069 | (0xa77a, 0xa779), 1070 | (0xa77c, 0xa77b), 1071 | (0xa77f, 0xa77e), 1072 | (0xa781, 0xa780), 1073 | (0xa783, 0xa782), 1074 | (0xa785, 0xa784), 1075 | (0xa787, 0xa786), 1076 | (0xa78c, 0xa78b), 1077 | (0xa791, 0xa790), 1078 | (0xa793, 0xa792), 1079 | (0xa794, 0xa7c4), 1080 | (0xa797, 0xa796), 1081 | (0xa799, 0xa798), 1082 | (0xa79b, 0xa79a), 1083 | (0xa79d, 0xa79c), 1084 | (0xa79f, 0xa79e), 1085 | (0xa7a1, 0xa7a0), 1086 | (0xa7a3, 0xa7a2), 1087 | (0xa7a5, 0xa7a4), 1088 | (0xa7a7, 0xa7a6), 1089 | (0xa7a9, 0xa7a8), 1090 | (0xa7b5, 0xa7b4), 1091 | (0xa7b7, 0xa7b6), 1092 | (0xa7b9, 0xa7b8), 1093 | (0xa7bb, 0xa7ba), 1094 | (0xa7bd, 0xa7bc), 1095 | (0xa7bf, 0xa7be), 1096 | (0xa7c3, 0xa7c2), 1097 | (0xa7c8, 0xa7c7), 1098 | (0xa7ca, 0xa7c9), 1099 | (0xa7f6, 0xa7f5), 1100 | (0xab53, 0xa7b3), 1101 | (0xab70, 0x13a0), 1102 | (0xab71, 0x13a1), 1103 | (0xab72, 0x13a2), 1104 | (0xab73, 0x13a3), 1105 | (0xab74, 0x13a4), 1106 | (0xab75, 0x13a5), 1107 | (0xab76, 0x13a6), 1108 | (0xab77, 0x13a7), 1109 | (0xab78, 0x13a8), 1110 | (0xab79, 0x13a9), 1111 | (0xab7a, 0x13aa), 1112 | (0xab7b, 0x13ab), 1113 | (0xab7c, 0x13ac), 1114 | (0xab7d, 0x13ad), 1115 | (0xab7e, 0x13ae), 1116 | (0xab7f, 0x13af), 1117 | (0xab80, 0x13b0), 1118 | (0xab81, 0x13b1), 1119 | (0xab82, 0x13b2), 1120 | (0xab83, 0x13b3), 1121 | (0xab84, 0x13b4), 1122 | (0xab85, 0x13b5), 1123 | (0xab86, 0x13b6), 1124 | (0xab87, 0x13b7), 1125 | (0xab88, 0x13b8), 1126 | (0xab89, 0x13b9), 1127 | (0xab8a, 0x13ba), 1128 | (0xab8b, 0x13bb), 1129 | (0xab8c, 0x13bc), 1130 | (0xab8d, 0x13bd), 1131 | (0xab8e, 0x13be), 1132 | (0xab8f, 0x13bf), 1133 | (0xab90, 0x13c0), 1134 | (0xab91, 0x13c1), 1135 | (0xab92, 0x13c2), 1136 | (0xab93, 0x13c3), 1137 | (0xab94, 0x13c4), 1138 | (0xab95, 0x13c5), 1139 | (0xab96, 0x13c6), 1140 | (0xab97, 0x13c7), 1141 | (0xab98, 0x13c8), 1142 | (0xab99, 0x13c9), 1143 | (0xab9a, 0x13ca), 1144 | (0xab9b, 0x13cb), 1145 | (0xab9c, 0x13cc), 1146 | (0xab9d, 0x13cd), 1147 | (0xab9e, 0x13ce), 1148 | (0xab9f, 0x13cf), 1149 | (0xaba0, 0x13d0), 1150 | (0xaba1, 0x13d1), 1151 | (0xaba2, 0x13d2), 1152 | (0xaba3, 0x13d3), 1153 | (0xaba4, 0x13d4), 1154 | (0xaba5, 0x13d5), 1155 | (0xaba6, 0x13d6), 1156 | (0xaba7, 0x13d7), 1157 | (0xaba8, 0x13d8), 1158 | (0xaba9, 0x13d9), 1159 | (0xabaa, 0x13da), 1160 | (0xabab, 0x13db), 1161 | (0xabac, 0x13dc), 1162 | (0xabad, 0x13dd), 1163 | (0xabae, 0x13de), 1164 | (0xabaf, 0x13df), 1165 | (0xabb0, 0x13e0), 1166 | (0xabb1, 0x13e1), 1167 | (0xabb2, 0x13e2), 1168 | (0xabb3, 0x13e3), 1169 | (0xabb4, 0x13e4), 1170 | (0xabb5, 0x13e5), 1171 | (0xabb6, 0x13e6), 1172 | (0xabb7, 0x13e7), 1173 | (0xabb8, 0x13e8), 1174 | (0xabb9, 0x13e9), 1175 | (0xabba, 0x13ea), 1176 | (0xabbb, 0x13eb), 1177 | (0xabbc, 0x13ec), 1178 | (0xabbd, 0x13ed), 1179 | (0xabbe, 0x13ee), 1180 | (0xabbf, 0x13ef), 1181 | (0xff41, 0xff21), 1182 | (0xff42, 0xff22), 1183 | (0xff43, 0xff23), 1184 | (0xff44, 0xff24), 1185 | (0xff45, 0xff25), 1186 | (0xff46, 0xff26), 1187 | (0xff47, 0xff27), 1188 | (0xff48, 0xff28), 1189 | (0xff49, 0xff29), 1190 | (0xff4a, 0xff2a), 1191 | (0xff4b, 0xff2b), 1192 | (0xff4c, 0xff2c), 1193 | (0xff4d, 0xff2d), 1194 | (0xff4e, 0xff2e), 1195 | (0xff4f, 0xff2f), 1196 | (0xff50, 0xff30), 1197 | (0xff51, 0xff31), 1198 | (0xff52, 0xff32), 1199 | (0xff53, 0xff33), 1200 | (0xff54, 0xff34), 1201 | (0xff55, 0xff35), 1202 | (0xff56, 0xff36), 1203 | (0xff57, 0xff37), 1204 | (0xff58, 0xff38), 1205 | (0xff59, 0xff39), 1206 | (0xff5a, 0xff3a), 1207 | ]; 1208 | 1209 | fn utf16_code_unit_to_uppercase(unit: u16) -> u16 { 1210 | match BMP_UPPERCASE_TABLE.binary_search_by(|&(key, _)| key.cmp(&unit)) { 1211 | Ok(index) => BMP_UPPERCASE_TABLE[index].1, 1212 | Err(_) => unit, 1213 | } 1214 | } 1215 | 1216 | /// Zero-copy representation of a key name or value name string stored in hive data. 1217 | /// Can be either in Latin1 (ISO-8859-1) or UTF-16 (Little-Endian). 1218 | /// 1219 | /// This allows to work with the string without performing any allocations or conversions. 1220 | /// If the `alloc` feature is enabled, [`to_string_checked`](NtHiveNameString::to_string_checked) and 1221 | /// [`to_string_lossy`](NtHiveNameString::to_string_lossy) can be used to to retrieve a `String`. 1222 | #[derive(Clone, Debug, Eq)] 1223 | pub enum NtHiveNameString<'h> { 1224 | /// A byte stream where each byte is a single character of the Latin1 (ISO-8859-1) 1225 | /// character set. 1226 | /// Each byte can simply be casted to a [`prim@char`] (as Unicode is ordered the same as Latin1). 1227 | Latin1(&'h [u8]), 1228 | /// A byte stream where every two bytes make up a UTF-16 code point in little-endian order. 1229 | /// Use [`u16::from_le_bytes`] and [`char::decode_utf16`] if you want to get a stream of [`prim@char`]s. 1230 | Utf16LE(&'h [u8]), 1231 | } 1232 | 1233 | impl<'h> NtHiveNameString<'h> { 1234 | fn cmp_iter(mut this_iter: TI, mut other_iter: OI) -> Ordering 1235 | where 1236 | TI: Iterator, 1237 | OI: Iterator, 1238 | { 1239 | loop { 1240 | match (this_iter.next(), other_iter.next()) { 1241 | (Some(this_code_unit), Some(other_code_unit)) => { 1242 | // We have two UTF-16 code units to compare. 1243 | let this_upper = utf16_code_unit_to_uppercase(this_code_unit); 1244 | let other_upper = utf16_code_unit_to_uppercase(other_code_unit); 1245 | 1246 | if this_upper != other_upper { 1247 | return this_upper.cmp(&other_upper); 1248 | } 1249 | } 1250 | (Some(_), None) => { 1251 | // `this_iter` is longer than `other_iter` but otherwise equal. 1252 | return Ordering::Greater; 1253 | } 1254 | (None, Some(_)) => { 1255 | // `other_iter` is longer than `this_iter` but otherwise equal. 1256 | return Ordering::Less; 1257 | } 1258 | (None, None) => { 1259 | // We made it to the end of both strings, so they must be equal. 1260 | return Ordering::Equal; 1261 | } 1262 | } 1263 | } 1264 | } 1265 | 1266 | fn cmp_self_and_str(lhs: &Self, rhs: &str) -> Ordering { 1267 | let rhs_iter = rhs.encode_utf16(); 1268 | 1269 | match lhs { 1270 | Self::Latin1(_) => Self::cmp_iter(lhs.latin1_iter(), rhs_iter), 1271 | Self::Utf16LE(_) => Self::cmp_iter(lhs.utf16le_iter(), rhs_iter), 1272 | } 1273 | } 1274 | 1275 | fn cmp_str_and_self(lhs: &str, rhs: &Self) -> Ordering { 1276 | let lhs_iter = lhs.encode_utf16(); 1277 | 1278 | match rhs { 1279 | Self::Latin1(_) => Self::cmp_iter(lhs_iter, rhs.latin1_iter()), 1280 | Self::Utf16LE(_) => Self::cmp_iter(lhs_iter, rhs.utf16le_iter()), 1281 | } 1282 | } 1283 | 1284 | fn latin1_iter(&'h self) -> impl Iterator + 'h { 1285 | match self { 1286 | Self::Latin1(bytes) => bytes.iter().map(|byte| *byte as u16), 1287 | Self::Utf16LE(_) => panic!("Called latin1_iter for Utf16LE"), 1288 | } 1289 | } 1290 | 1291 | fn utf16le_iter(&'h self) -> impl Iterator + 'h { 1292 | match self { 1293 | Self::Latin1(_) => panic!("Called utf16le_iter for Latin1"), 1294 | Self::Utf16LE(bytes) => bytes 1295 | .chunks_exact(2) 1296 | .map(|two_bytes| u16::from_le_bytes(two_bytes.try_into().unwrap())), 1297 | } 1298 | } 1299 | 1300 | /// Returns `true` if `self` has a length of zero bytes. 1301 | pub const fn is_empty(&self) -> bool { 1302 | self.len() == 0 1303 | } 1304 | 1305 | /// Returns the length of `self`. 1306 | /// 1307 | /// This length is in bytes, not characters! In other words, 1308 | /// it may not be what a human considers the length of the string. 1309 | pub const fn len(&self) -> usize { 1310 | match self { 1311 | Self::Latin1(bytes) => bytes.len(), 1312 | Self::Utf16LE(bytes) => bytes.len(), 1313 | } 1314 | } 1315 | 1316 | /// Attempts to convert `self` to an owned `String`. 1317 | /// Returns `Some(String)` if all characters could be converted successfully or `None` if a decoding error occurred. 1318 | #[cfg(feature = "alloc")] 1319 | pub fn to_string_checked(&self) -> Option { 1320 | match self { 1321 | Self::Latin1(bytes) => { 1322 | let string = bytes.iter().map(|byte| *byte as char).collect(); 1323 | Some(string) 1324 | } 1325 | Self::Utf16LE(_) => char::decode_utf16(self.utf16le_iter()) 1326 | .map(|x| x.ok()) 1327 | .collect::>(), 1328 | } 1329 | } 1330 | 1331 | /// Converts `self` to an owned `String`, replacing invalid data with the replacement character (U+FFFD). 1332 | #[cfg(feature = "alloc")] 1333 | pub fn to_string_lossy(&self) -> String { 1334 | match self { 1335 | Self::Latin1(bytes) => bytes.iter().map(|byte| *byte as char).collect(), 1336 | Self::Utf16LE(_) => char::decode_utf16(self.utf16le_iter()) 1337 | .map(|x| x.unwrap_or(char::REPLACEMENT_CHARACTER)) 1338 | .collect(), 1339 | } 1340 | } 1341 | } 1342 | 1343 | impl fmt::Display for NtHiveNameString<'_> { 1344 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1345 | match self { 1346 | Self::Latin1(bytes) => { 1347 | for byte in bytes.iter() { 1348 | let single_char = *byte as char; 1349 | single_char.fmt(f)?; 1350 | } 1351 | } 1352 | Self::Utf16LE(_) => { 1353 | let utf16_iter = char::decode_utf16(self.utf16le_iter()) 1354 | .map(|x| x.unwrap_or(char::REPLACEMENT_CHARACTER)); 1355 | 1356 | for single_char in utf16_iter { 1357 | single_char.fmt(f)?; 1358 | } 1359 | } 1360 | } 1361 | 1362 | Ok(()) 1363 | } 1364 | } 1365 | 1366 | impl Ord for NtHiveNameString<'_> { 1367 | fn cmp(&self, other: &Self) -> Ordering { 1368 | match (self, other) { 1369 | (Self::Latin1(_), Self::Latin1(_)) => { 1370 | Self::cmp_iter(self.latin1_iter(), other.latin1_iter()) 1371 | } 1372 | (Self::Latin1(_), Self::Utf16LE(_)) => { 1373 | Self::cmp_iter(self.latin1_iter(), other.utf16le_iter()) 1374 | } 1375 | (Self::Utf16LE(_), Self::Latin1(_)) => { 1376 | Self::cmp_iter(self.utf16le_iter(), other.latin1_iter()) 1377 | } 1378 | (Self::Utf16LE(_), Self::Utf16LE(_)) => { 1379 | Self::cmp_iter(self.utf16le_iter(), other.utf16le_iter()) 1380 | } 1381 | } 1382 | } 1383 | } 1384 | 1385 | impl PartialEq for NtHiveNameString<'_> { 1386 | /// Checks that two strings are a case-insensitive match 1387 | /// (according to Windows' definition of case-insensitivity, which only considers the 1388 | /// Unicode Basic Multilingual Plane). 1389 | fn eq(&self, other: &Self) -> bool { 1390 | self.cmp(other) == Ordering::Equal 1391 | } 1392 | } 1393 | 1394 | impl PartialEq for NtHiveNameString<'_> { 1395 | fn eq(&self, other: &str) -> bool { 1396 | NtHiveNameString::cmp_self_and_str(self, other) == Ordering::Equal 1397 | } 1398 | } 1399 | 1400 | impl<'h> PartialEq> for str { 1401 | fn eq(&self, other: &NtHiveNameString<'h>) -> bool { 1402 | NtHiveNameString::cmp_str_and_self(self, other) == Ordering::Equal 1403 | } 1404 | } 1405 | 1406 | impl PartialEq<&str> for NtHiveNameString<'_> { 1407 | fn eq(&self, other: &&str) -> bool { 1408 | NtHiveNameString::cmp_self_and_str(self, other) == Ordering::Equal 1409 | } 1410 | } 1411 | 1412 | impl<'h> PartialEq> for &str { 1413 | fn eq(&self, other: &NtHiveNameString<'h>) -> bool { 1414 | NtHiveNameString::cmp_str_and_self(self, other) == Ordering::Equal 1415 | } 1416 | } 1417 | 1418 | impl PartialOrd for NtHiveNameString<'_> { 1419 | fn partial_cmp(&self, other: &Self) -> Option { 1420 | Some(self.cmp(other)) 1421 | } 1422 | } 1423 | 1424 | impl PartialOrd for NtHiveNameString<'_> { 1425 | fn partial_cmp(&self, other: &str) -> Option { 1426 | Some(NtHiveNameString::cmp_self_and_str(self, other)) 1427 | } 1428 | } 1429 | 1430 | impl<'h> PartialOrd> for str { 1431 | fn partial_cmp(&self, other: &NtHiveNameString<'h>) -> Option { 1432 | Some(NtHiveNameString::cmp_str_and_self(self, other)) 1433 | } 1434 | } 1435 | 1436 | impl PartialOrd<&str> for NtHiveNameString<'_> { 1437 | fn partial_cmp(&self, other: &&str) -> Option { 1438 | Some(NtHiveNameString::cmp_self_and_str(self, other)) 1439 | } 1440 | } 1441 | 1442 | impl<'h> PartialOrd> for &str { 1443 | fn partial_cmp(&self, other: &NtHiveNameString<'h>) -> Option { 1444 | Some(NtHiveNameString::cmp_str_and_self(self, other)) 1445 | } 1446 | } 1447 | 1448 | #[cfg(test)] 1449 | mod tests { 1450 | use super::*; 1451 | 1452 | #[test] 1453 | fn test_eq() { 1454 | assert_eq!(NtHiveNameString::Latin1(b"Hello"), "Hello"); 1455 | assert_eq!( 1456 | NtHiveNameString::Utf16LE(&[b'H', 0, b'e', 0, b'l', 0, b'l', 0, b'o', 0]), 1457 | "Hello" 1458 | ); 1459 | assert_eq!(NtHiveNameString::Latin1(b"Hello"), "hello"); 1460 | assert_eq!( 1461 | NtHiveNameString::Utf16LE(&[b'H', 0, b'e', 0, b'l', 0, b'l', 0, b'o', 0]), 1462 | "hello" 1463 | ); 1464 | assert_eq!(NtHiveNameString::Latin1(b"Hell\xD6"), "hellö"); 1465 | assert_ne!(NtHiveNameString::Latin1(b"Hello"), "Hell"); 1466 | assert_ne!( 1467 | NtHiveNameString::Utf16LE(&[b'H', 0, b'e', 0, b'l', 0, b'l', 0, b'o', 0]), 1468 | "Hell" 1469 | ); 1470 | 1471 | // Characters in the Basic Multilingual Plane are compared case-insensitively, 1472 | // others are not. 1473 | let full_width_upper_a = "\u{FF21}" 1474 | .encode_utf16() 1475 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1476 | .collect::>(); 1477 | let full_width_lower_a = "\u{FF41}" 1478 | .encode_utf16() 1479 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1480 | .collect::>(); 1481 | assert_eq!( 1482 | NtHiveNameString::Utf16LE(&full_width_upper_a), 1483 | NtHiveNameString::Utf16LE(&full_width_lower_a) 1484 | ); 1485 | 1486 | let deseret_upper_h = "\u{10410}" 1487 | .encode_utf16() 1488 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1489 | .collect::>(); 1490 | let deseret_lower_h = "\u{10438}" 1491 | .encode_utf16() 1492 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1493 | .collect::>(); 1494 | assert_ne!( 1495 | NtHiveNameString::Utf16LE(&deseret_upper_h), 1496 | NtHiveNameString::Utf16LE(&deseret_lower_h) 1497 | ); 1498 | } 1499 | 1500 | #[test] 1501 | fn test_is_empty() { 1502 | assert!(NtHiveNameString::Latin1(b"").is_empty()); 1503 | assert!(NtHiveNameString::Utf16LE(&[]).is_empty()); 1504 | assert!(!NtHiveNameString::Latin1(b"Hello").is_empty()); 1505 | assert!( 1506 | !NtHiveNameString::Utf16LE(&[b'H', 0, b'e', 0, b'l', 0, b'l', 0, b'o', 0]).is_empty() 1507 | ); 1508 | } 1509 | 1510 | #[test] 1511 | fn test_len() { 1512 | assert_eq!(NtHiveNameString::Latin1(b"Hello").len(), 5); 1513 | assert_eq!( 1514 | NtHiveNameString::Utf16LE(&[b'H', 0, b'e', 0, b'l', 0, b'l', 0, b'o', 0]).len(), 1515 | 10 1516 | ); 1517 | } 1518 | 1519 | #[test] 1520 | fn test_ord() { 1521 | assert!(NtHiveNameString::Latin1(b"a") < "b"); 1522 | assert!("b" > NtHiveNameString::Latin1(b"a")); 1523 | 1524 | assert!(NtHiveNameString::Latin1(b"a") == NtHiveNameString::Latin1(b"a")); 1525 | assert!(NtHiveNameString::Latin1(b"a") < NtHiveNameString::Latin1(b"aa")); 1526 | assert!(NtHiveNameString::Latin1(b"aa") > NtHiveNameString::Latin1(b"a")); 1527 | assert!(NtHiveNameString::Latin1(b"a") < NtHiveNameString::Latin1(b"b")); 1528 | 1529 | // Even though Unicode character 0x10331 (Gothic Letter Bairkan) has a higher 1530 | // code point than Unicode character 0xFF21 (Full-Width Latin Capital Letter A), Windows 1531 | // hives order name strings by their UTF-16 representation. 1532 | // Hence, 0x10331 is encoded as 0xD800 0xDF31 and comes before 0xFF21 1533 | // (which is the same in UTF-16). 1534 | let full_width_a = "\u{FF21}" 1535 | .encode_utf16() 1536 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1537 | .collect::>(); 1538 | let gothic_bairkan = "\u{10331}" 1539 | .encode_utf16() 1540 | .flat_map(|utf16_code_point| utf16_code_point.to_le_bytes().to_vec()) 1541 | .collect::>(); 1542 | assert!( 1543 | NtHiveNameString::Utf16LE(&gothic_bairkan) < NtHiveNameString::Utf16LE(&full_width_a) 1544 | ); 1545 | } 1546 | } 1547 | --------------------------------------------------------------------------------