use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufWriter, Cursor, Seek, Write};
use std::path::Path;
use crate::error::{Error, Result};
use crate::structures::*;
fn encode_utf16le_string(s: &str) -> Vec<u8> {
let mut bytes: Vec<u8> = s
.encode_utf16()
.flat_map(|c| c.to_le_bytes())
.collect();
bytes.extend_from_slice(&[0, 0]);
bytes
}
#[derive(Debug, Clone)]
pub struct KeyTreeValue {
pub name: String,
pub data_type: DataType,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct KeyTreeNode {
pub name: String,
pub values: Vec<KeyTreeValue>,
pub children: Vec<KeyTreeNode>,
}
impl KeyTreeNode {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
values: Vec::new(),
children: Vec::new(),
}
}
pub fn get_or_create_path(&mut self, path: &str) -> &mut KeyTreeNode {
if path.is_empty() {
return self;
}
let parts: Vec<&str> = path.split('\\').filter(|s| !s.is_empty()).collect();
let mut current = self;
for part in parts {
let idx = current.children.iter().position(|c| c.name.eq_ignore_ascii_case(part));
if let Some(idx) = idx {
current = &mut current.children[idx];
} else {
current.children.push(KeyTreeNode::new(part));
let len = current.children.len();
current = &mut current.children[len - 1];
}
}
current
}
pub fn sort_recursive(&mut self) {
self.children.sort_by(|a, b| a.name.to_uppercase().cmp(&b.name.to_uppercase()));
for child in &mut self.children {
child.sort_recursive();
}
}
pub fn count_keys(&self) -> usize {
1 + self.children.iter().map(|c| c.count_keys()).sum::<usize>()
}
pub fn count_values(&self) -> usize {
self.values.len() + self.children.iter().map(|c| c.count_values()).sum::<usize>()
}
}
pub struct HiveBuilder {
base_block: BaseBlock,
cells: Vec<CellEntry>,
free_cells: Vec<FreeCell>,
hive_bins_size: u32,
root_offset: u32,
#[allow(dead_code)]
security_cache: HashMap<Vec<u8>, u32>,
next_offset: u32,
minor_version: u32,
}
#[derive(Debug, Clone)]
struct CellEntry {
offset: u32,
data: Vec<u8>,
allocated_size: usize,
}
#[derive(Debug, Clone)]
struct FreeCell {
offset: u32,
size: u32,
}
impl HiveBuilder {
pub fn new() -> Self {
Self::with_version(1, 6)
}
pub fn new_with_name(root_name: &str) -> Self {
Self::with_version_and_name(1, 6, root_name)
}
pub fn new_with_version(root_name: &str, minor_version: u32) -> Self {
Self::with_version_and_name(1, minor_version, root_name)
}
pub fn with_version(major: u32, minor: u32) -> Self {
Self::with_version_and_name(major, minor, "")
}
fn with_version_and_name(major: u32, minor: u32, root_name: &str) -> Self {
let mut builder = Self {
base_block: BaseBlock::default(),
cells: Vec::new(),
free_cells: Vec::new(),
hive_bins_size: MIN_HIVE_BIN_SIZE as u32,
root_offset: HIVE_BIN_HEADER_SIZE as u32,
security_cache: HashMap::new(),
next_offset: HIVE_BIN_HEADER_SIZE as u32,
minor_version: minor,
};
builder.base_block.major_version = major;
builder.base_block.minor_version = minor;
let security_offset = HIVE_BIN_HEADER_SIZE as u32;
let mut security = KeySecurity::new_default();
security.set_self_referencing(security_offset);
let security_bytes = security.to_bytes();
let actual_security_offset = builder.allocate_cell(&security_bytes);
debug_assert_eq!(actual_security_offset, security_offset);
let mut root_key = KeyNode::new(root_name, INVALID_OFFSET, true);
root_key.security_offset = actual_security_offset;
let root_bytes = root_key.to_bytes();
builder.root_offset = builder.allocate_cell(&root_bytes);
builder.base_block.root_cell_offset = builder.root_offset;
builder
}
pub fn from_tree(tree: KeyTreeNode) -> Self {
Self::from_tree_with_version(tree, 1, 6)
}
pub fn from_tree_with_version(mut tree: KeyTreeNode, major: u32, minor: u32) -> Self {
tree.sort_recursive();
let mut builder = Self {
base_block: BaseBlock::default(),
cells: Vec::new(),
free_cells: Vec::new(),
hive_bins_size: MIN_HIVE_BIN_SIZE as u32,
root_offset: HIVE_BIN_HEADER_SIZE as u32,
security_cache: HashMap::new(),
next_offset: HIVE_BIN_HEADER_SIZE as u32,
minor_version: minor,
};
builder.base_block.major_version = major;
builder.base_block.minor_version = minor;
let security_offset = HIVE_BIN_HEADER_SIZE as u32;
let mut security = KeySecurity::new_default();
security.set_self_referencing(security_offset);
let security_bytes = security.to_bytes();
let actual_security_offset = builder.allocate_cell(&security_bytes);
debug_assert_eq!(actual_security_offset, security_offset);
let root_offset = builder.build_key_node(&tree, INVALID_OFFSET, actual_security_offset, true);
builder.root_offset = root_offset;
builder.base_block.root_cell_offset = root_offset;
builder
}
fn build_key_node(
&mut self,
tree_node: &KeyTreeNode,
parent_offset: u32,
security_offset: u32,
is_root: bool,
) -> u32 {
let use_hash_leaf = self.supports_hash_leaf();
let mut child_offsets: Vec<(u32, String)> = Vec::with_capacity(tree_node.children.len());
for child in &tree_node.children {
let child_offset = self.build_key_node(child, INVALID_OFFSET, security_offset, false);
child_offsets.push((child_offset, child.name.to_uppercase()));
}
let values_list_offset = if tree_node.values.is_empty() {
INVALID_OFFSET
} else {
let mut offsets = Vec::with_capacity(tree_node.values.len());
for value in &tree_node.values {
let value_offset = self.build_value_cell(value);
offsets.push(value_offset);
}
let list_bytes: Vec<u8> = offsets.iter()
.flat_map(|&o| o.to_le_bytes())
.collect();
self.allocate_cell(&list_bytes)
};
let subkeys_list_offset = if child_offsets.is_empty() {
INVALID_OFFSET
} else if child_offsets.len() <= Self::MAX_LEAF_ELEMENTS {
let list_bytes = if use_hash_leaf {
let mut hash_leaf = HashLeaf::new();
hash_leaf.elements = child_offsets
.iter()
.map(|(offset, name)| HashLeafElement {
key_node_offset: *offset,
name_hash: calculate_name_hash(name),
})
.collect();
hash_leaf.num_elements = hash_leaf.elements.len() as u16;
hash_leaf.to_bytes()
} else {
let mut fast_leaf = FastLeaf::new();
fast_leaf.elements = child_offsets
.iter()
.enumerate()
.map(|(i, (offset, _))| {
FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(&tree_node.children[i].name),
}
})
.collect();
fast_leaf.num_elements = fast_leaf.elements.len() as u16;
fast_leaf.to_bytes()
};
self.allocate_cell(&list_bytes)
} else {
let mut index_root = IndexRoot::new();
let mut leaf_offsets = Vec::new();
for chunk in child_offsets.chunks(Self::MAX_LEAF_ELEMENTS) {
let chunk_start_idx = leaf_offsets.len() * Self::MAX_LEAF_ELEMENTS;
let leaf_bytes = if use_hash_leaf {
let mut hash_leaf = HashLeaf::new();
hash_leaf.elements = chunk
.iter()
.map(|(offset, name)| HashLeafElement {
key_node_offset: *offset,
name_hash: calculate_name_hash(name),
})
.collect();
hash_leaf.num_elements = hash_leaf.elements.len() as u16;
hash_leaf.to_bytes()
} else {
let mut fast_leaf = FastLeaf::new();
fast_leaf.elements = chunk
.iter()
.enumerate()
.map(|(i, (offset, _))| {
let child_idx = chunk_start_idx + i;
FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(&tree_node.children[child_idx].name),
}
})
.collect();
fast_leaf.num_elements = fast_leaf.elements.len() as u16;
fast_leaf.to_bytes()
};
let leaf_offset = self.allocate_cell(&leaf_bytes);
leaf_offsets.push(leaf_offset);
}
index_root.elements = leaf_offsets
.iter()
.map(|&offset| IndexRootElement { subkeys_list_offset: offset })
.collect();
index_root.num_elements = index_root.elements.len() as u16;
let root_bytes = index_root.to_bytes();
self.allocate_cell(&root_bytes)
};
let mut key_node = KeyNode::new(&tree_node.name, parent_offset, is_root);
key_node.security_offset = security_offset;
key_node.num_subkeys = tree_node.children.len() as u32;
key_node.subkeys_list_offset = subkeys_list_offset;
key_node.num_values = tree_node.values.len() as u32;
key_node.values_list_offset = values_list_offset;
if !tree_node.children.is_empty() {
key_node.largest_subkey_name_length = tree_node.children
.iter()
.map(|c| (c.name.len() * 2) as u16)
.max()
.unwrap_or(0);
}
if !tree_node.values.is_empty() {
key_node.largest_value_name_length = tree_node.values
.iter()
.map(|v| (v.name.len() * 2) as u32)
.max()
.unwrap_or(0);
key_node.largest_value_data_size = tree_node.values
.iter()
.map(|v| v.data.len() as u32)
.max()
.unwrap_or(0);
}
let key_bytes = key_node.to_bytes();
let key_offset = self.allocate_cell(&key_bytes);
for (child_offset, _) in &child_offsets {
if let Some(cell) = self.cells.iter_mut().find(|c| c.offset == *child_offset) {
if let Ok(mut child_node) = KeyNode::parse(&cell.data) {
child_node.parent = key_offset;
cell.data = child_node.to_bytes();
}
}
}
key_offset
}
fn build_value_cell(&mut self, value: &KeyTreeValue) -> u32 {
let mut key_value = KeyValue::new(&value.name, value.data_type);
if value.data.len() <= MAX_RESIDENT_DATA_SIZE {
key_value.set_resident_data(&value.data);
} else if value.data.len() <= BIG_DATA_THRESHOLD {
let data_offset = self.allocate_cell(&value.data);
key_value.data_offset = data_offset;
key_value.data_size = value.data.len() as u32;
} else if self.supports_big_data() {
if let Ok(data_offset) = self.allocate_big_data(&value.data) {
key_value.data_offset = data_offset;
key_value.data_size = value.data.len() as u32;
}
}
let value_bytes = key_value.to_bytes();
self.allocate_cell(&value_bytes)
}
fn supports_hash_leaf(&self) -> bool {
self.minor_version > 4
}
fn supports_big_data(&self) -> bool {
self.minor_version > 3
}
fn allocate_cell(&mut self, data: &[u8]) -> u32 {
self.allocate_cell_with_min_size(data, required_cell_size(data.len()))
}
fn bin_end_for_offset(&self, offset: u32) -> u32 {
let bin_index = offset / MIN_HIVE_BIN_SIZE as u32;
(bin_index + 1) * MIN_HIVE_BIN_SIZE as u32
}
fn bin_data_start_for_offset(&self, offset: u32) -> u32 {
let bin_index = offset / MIN_HIVE_BIN_SIZE as u32;
bin_index * MIN_HIVE_BIN_SIZE as u32 + HIVE_BIN_HEADER_SIZE as u32
}
fn is_valid_cell_offset(&self, offset: u32) -> bool {
let bin_start = (offset / MIN_HIVE_BIN_SIZE as u32) * MIN_HIVE_BIN_SIZE as u32;
let header_end = bin_start + HIVE_BIN_HEADER_SIZE as u32;
offset >= header_end
}
fn ensure_past_bin_header(&self, offset: u32) -> u32 {
let data_start = self.bin_data_start_for_offset(offset);
if offset < data_start {
data_start
} else {
offset
}
}
fn allocate_cell_with_min_size(&mut self, data: &[u8], min_size: usize) -> u32 {
let cell_size = min_size.max(required_cell_size(data.len()));
let mut best_fit_idx: Option<usize> = None;
let mut best_fit_size: u32 = u32::MAX;
for i in 0..self.free_cells.len() {
let free = &self.free_cells[i];
if free.size >= cell_size as u32 && free.size < best_fit_size {
let offset = free.offset;
if !self.is_valid_cell_offset(offset) {
continue;
}
let bin_end = self.bin_end_for_offset(offset);
if offset + cell_size as u32 > bin_end {
continue; }
best_fit_idx = Some(i);
best_fit_size = free.size;
if free.size == cell_size as u32 {
break;
}
}
}
if let Some(i) = best_fit_idx {
let free = &self.free_cells[i];
let offset = free.offset;
let actual_size = free.size as usize;
if free.size > cell_size as u32 + 8 {
let remaining_offset = offset + cell_size as u32;
let remaining_size = free.size - cell_size as u32;
if self.is_valid_cell_offset(remaining_offset) {
self.free_cells[i] = FreeCell {
offset: remaining_offset,
size: remaining_size,
};
} else {
self.free_cells.remove(i);
}
self.cells.push(CellEntry {
offset,
data: data.to_vec(),
allocated_size: cell_size,
});
} else {
self.free_cells.remove(i);
self.cells.push(CellEntry {
offset,
data: data.to_vec(),
allocated_size: actual_size,
});
}
return offset;
}
let mut offset = self.ensure_past_bin_header(self.next_offset);
let max_cell_in_bin = MIN_HIVE_BIN_SIZE - HIVE_BIN_HEADER_SIZE;
assert!(cell_size <= max_cell_in_bin,
"Cell size {} exceeds maximum {} for a single bin", cell_size, max_cell_in_bin);
loop {
offset = self.ensure_past_bin_header(offset);
let bin_end = self.bin_end_for_offset(offset);
if offset + cell_size as u32 <= bin_end {
break; }
let leftover = bin_end - offset;
if leftover >= 8 {
self.free_cells.push(FreeCell {
offset,
size: leftover,
});
}
offset = bin_end + HIVE_BIN_HEADER_SIZE as u32;
self.next_offset = offset;
}
debug_assert!(self.is_valid_cell_offset(offset),
"Invalid cell offset {} - within bin header", offset);
while offset + cell_size as u32 > self.hive_bins_size {
self.grow_hive();
}
self.cells.push(CellEntry {
offset,
data: data.to_vec(),
allocated_size: cell_size,
});
self.next_offset = offset + cell_size as u32;
offset
}
fn grow_hive(&mut self) {
let new_bin_start = self.hive_bins_size;
self.hive_bins_size += MIN_HIVE_BIN_SIZE as u32;
self.next_offset = new_bin_start + HIVE_BIN_HEADER_SIZE as u32;
}
pub fn add_key(&mut self, parent_offset: u32, name: &str) -> Result<u32> {
let security_offset = if parent_offset != INVALID_OFFSET {
let parent_cell = self.find_cell(parent_offset)?;
let parent_node = KeyNode::parse(&parent_cell.data)?;
parent_node.security_offset
} else {
INVALID_OFFSET
};
let mut key_node = KeyNode::new(name, parent_offset, false);
key_node.security_offset = security_offset;
let key_bytes = key_node.to_bytes();
let key_offset = self.allocate_cell(&key_bytes);
if parent_offset != INVALID_OFFSET {
self.add_subkey_to_parent(parent_offset, key_offset, name)?;
}
Ok(key_offset)
}
fn create_name_hint(name: &str) -> [u8; 4] {
let mut hint = [0u8; 4];
for (i, c) in name.chars().take(4).enumerate() {
let code = c as u32;
if code <= 255 {
hint[i] = code as u8;
} else {
hint[0] = 0;
break;
}
}
hint
}
const MAX_LEAF_ELEMENTS: usize = 500;
fn add_subkey_to_parent(&mut self, parent_offset: u32, child_offset: u32, name: &str) -> Result<()> {
let parent_cell = self.find_cell(parent_offset)?;
let mut parent_node = KeyNode::parse(&parent_cell.data)?;
let name_upper = name.to_uppercase();
let use_hash_leaf = self.supports_hash_leaf();
let mut elements_with_names: Vec<(u32, String)> = Vec::new();
if parent_node.subkeys_list_offset != INVALID_OFFSET {
let list_offset = parent_node.subkeys_list_offset;
let list_cell = self.find_cell(list_offset)?;
let list = SubkeysList::parse(&list_cell.data)?;
if list.is_index_root() {
for sublist_offset in list.get_offsets() {
if let Ok(sub_cell) = self.find_cell(sublist_offset) {
if let Ok(sub_list) = SubkeysList::parse(&sub_cell.data) {
for key_offset in sub_list.get_offsets() {
if let Ok(node) = self.find_cell(key_offset).and_then(|c| KeyNode::parse(&c.data)) {
elements_with_names.push((key_offset, node.name().to_uppercase()));
}
}
}
}
}
} else {
for offset in list.get_offsets() {
if let Ok(node) = self.find_cell(offset).and_then(|c| KeyNode::parse(&c.data)) {
elements_with_names.push((offset, node.name().to_uppercase()));
}
}
}
}
elements_with_names.push((child_offset, name_upper));
elements_with_names.sort_by(|a, b| a.1.cmp(&b.1));
let new_list_offset = if elements_with_names.len() <= Self::MAX_LEAF_ELEMENTS {
let list_bytes = if use_hash_leaf {
let mut hash_leaf = HashLeaf::new();
hash_leaf.elements = elements_with_names
.iter()
.map(|(offset, name)| HashLeafElement {
key_node_offset: *offset,
name_hash: calculate_name_hash(name),
})
.collect();
hash_leaf.num_elements = hash_leaf.elements.len() as u16;
hash_leaf.to_bytes()
} else {
let mut fast_leaf = FastLeaf::new();
fast_leaf.elements = elements_with_names
.iter()
.filter_map(|(offset, _)| {
if *offset == child_offset {
Some(FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(name),
})
} else {
self.find_cell(*offset)
.ok()
.and_then(|c| KeyNode::parse(&c.data).ok())
.map(|node| FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(&node.name()),
})
}
})
.collect();
fast_leaf.num_elements = fast_leaf.elements.len() as u16;
fast_leaf.to_bytes()
};
self.allocate_cell(&list_bytes)
} else {
let mut index_root = IndexRoot::new();
let mut leaf_offsets = Vec::new();
for chunk in elements_with_names.chunks(Self::MAX_LEAF_ELEMENTS) {
let leaf_bytes = if use_hash_leaf {
let mut hash_leaf = HashLeaf::new();
hash_leaf.elements = chunk
.iter()
.map(|(offset, name)| HashLeafElement {
key_node_offset: *offset,
name_hash: calculate_name_hash(name),
})
.collect();
hash_leaf.num_elements = hash_leaf.elements.len() as u16;
hash_leaf.to_bytes()
} else {
let mut fast_leaf = FastLeaf::new();
fast_leaf.elements = chunk
.iter()
.filter_map(|(offset, _)| {
if *offset == child_offset {
Some(FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(name),
})
} else {
self.find_cell(*offset)
.ok()
.and_then(|c| KeyNode::parse(&c.data).ok())
.map(|node| FastLeafElement {
key_node_offset: *offset,
name_hint: Self::create_name_hint(&node.name()),
})
}
})
.collect();
fast_leaf.num_elements = fast_leaf.elements.len() as u16;
fast_leaf.to_bytes()
};
let leaf_offset = self.allocate_cell(&leaf_bytes);
leaf_offsets.push(leaf_offset);
}
index_root.elements = leaf_offsets
.iter()
.map(|&offset| IndexRootElement { subkeys_list_offset: offset })
.collect();
index_root.num_elements = index_root.elements.len() as u16;
let root_bytes = index_root.to_bytes();
self.allocate_cell(&root_bytes)
};
parent_node.subkeys_list_offset = new_list_offset;
parent_node.num_subkeys += 1;
let name_len_utf16 = (name.len() * 2) as u16;
if name_len_utf16 > parent_node.largest_subkey_name_length {
parent_node.largest_subkey_name_length = name_len_utf16;
}
let parent_bytes = parent_node.to_bytes();
self.update_cell(parent_offset, &parent_bytes)?;
Ok(())
}
pub fn add_value(
&mut self,
key_offset: u32,
name: &str,
data_type: DataType,
data: &[u8],
) -> Result<u32> {
let mut key_value = KeyValue::new(name, data_type);
if data.len() <= MAX_RESIDENT_DATA_SIZE {
key_value.set_resident_data(data);
} else if data.len() <= BIG_DATA_THRESHOLD {
let data_offset = self.allocate_cell(data);
key_value.data_offset = data_offset;
key_value.data_size = data.len() as u32;
} else if self.supports_big_data() {
let data_offset = self.allocate_big_data(data)?;
key_value.data_offset = data_offset;
key_value.data_size = data.len() as u32;
} else {
return Err(Error::DataTooLarge {
size: data.len(),
max: BIG_DATA_THRESHOLD,
});
}
let value_bytes = key_value.to_bytes();
let value_offset = self.allocate_cell(&value_bytes);
self.add_value_to_key(key_offset, value_offset, name, data.len())?;
Ok(value_offset)
}
fn allocate_big_data(&mut self, data: &[u8]) -> Result<u32> {
let num_segments = BigData::segments_needed(data.len());
let mut segment_offsets = Vec::with_capacity(num_segments as usize);
for i in 0..num_segments as usize {
let start = i * MAX_DATA_SEGMENT_SIZE;
let end = ((i + 1) * MAX_DATA_SEGMENT_SIZE).min(data.len());
let segment = &data[start..end];
let segment_offset = self.allocate_cell(segment);
segment_offsets.push(segment_offset);
}
let mut segments_list = DataSegmentsList::new();
segments_list.offsets = segment_offsets;
let list_bytes = segments_list.to_bytes();
let list_offset = self.allocate_cell(&list_bytes);
let big_data = BigData::new(num_segments, list_offset);
let bd_bytes = big_data.to_bytes();
let bd_offset = self.allocate_cell(&bd_bytes);
Ok(bd_offset)
}
fn add_value_to_key(
&mut self,
key_offset: u32,
value_offset: u32,
name: &str,
data_size: usize,
) -> Result<()> {
let key_cell = self.find_cell(key_offset)?;
let mut key_node = KeyNode::parse(&key_cell.data)?;
if key_node.values_list_offset == INVALID_OFFSET {
let list_bytes = value_offset.to_le_bytes().to_vec();
let list_offset = self.allocate_cell(&list_bytes);
key_node.values_list_offset = list_offset;
} else {
let list_offset = key_node.values_list_offset;
let list_cell = self.find_cell(list_offset)?;
let mut values: Vec<u32> = list_cell
.data
.chunks_exact(4)
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect();
values.push(value_offset);
let new_bytes: Vec<u8> = values
.iter()
.flat_map(|&v| v.to_le_bytes())
.collect();
let new_list_offset = self.update_cell(list_offset, &new_bytes)?;
if new_list_offset != list_offset {
key_node.values_list_offset = new_list_offset;
}
}
key_node.num_values += 1;
let name_len_utf16 = (name.len() * 2) as u32;
if name_len_utf16 > key_node.largest_value_name_length {
key_node.largest_value_name_length = name_len_utf16;
}
if (data_size as u32) > key_node.largest_value_data_size {
key_node.largest_value_data_size = data_size as u32;
}
let key_bytes = key_node.to_bytes();
self.update_cell(key_offset, &key_bytes)?;
Ok(())
}
fn find_cell(&self, offset: u32) -> Result<&CellEntry> {
self.cells
.iter()
.find(|c| c.offset == offset)
.ok_or_else(|| Error::InvalidCellOffset(offset))
}
fn update_cell(&mut self, offset: u32, data: &[u8]) -> Result<u32> {
let new_size = required_cell_size(data.len());
let cell_idx = self.cells
.iter()
.position(|c| c.offset == offset)
.ok_or_else(|| Error::InvalidCellOffset(offset))?;
if new_size <= self.cells[cell_idx].allocated_size {
self.cells[cell_idx].data = data.to_vec();
Ok(offset)
} else {
let old_size = self.cells[cell_idx].allocated_size;
self.free_cells.push(FreeCell {
offset,
size: old_size as u32,
});
self.cells.remove(cell_idx);
let new_offset = self.allocate_cell(data);
Ok(new_offset)
}
}
pub fn root_offset(&self) -> u32 {
self.root_offset
}
pub fn set_root_name(&mut self, name: &str) {
if let Some(cell) = self.cells.iter_mut().find(|c| c.offset == self.root_offset) {
if let Ok(mut root_node) = KeyNode::parse(&cell.data) {
root_node.set_name(name);
cell.data = root_node.to_bytes();
}
}
}
pub fn find_key(&self, path: &str) -> Result<u32> {
if path.is_empty() {
return Ok(self.root_offset);
}
let components: Vec<&str> = path.split('\\').filter(|s| !s.is_empty()).collect();
let mut current_offset = self.root_offset;
for component in components {
current_offset = self.find_subkey(current_offset, component)?;
}
Ok(current_offset)
}
fn find_subkey(&self, parent_offset: u32, name: &str) -> Result<u32> {
let parent_cell = self.find_cell(parent_offset)?;
let parent_node = KeyNode::parse(&parent_cell.data)?;
if parent_node.subkeys_list_offset == INVALID_OFFSET {
return Err(Error::KeyNotFound(name.to_string()));
}
let list_cell = self.find_cell(parent_node.subkeys_list_offset)?;
let name_upper = name.to_uppercase();
let list = SubkeysList::parse(&list_cell.data)?;
let key_offsets = self.get_all_key_offsets_from_list(&list)?;
for offset in key_offsets {
if let Ok(cell) = self.find_cell(offset) {
if let Ok(node) = KeyNode::parse(&cell.data) {
if node.name().to_uppercase() == name_upper {
return Ok(offset);
}
}
}
}
Err(Error::KeyNotFound(name.to_string()))
}
fn get_all_key_offsets_from_list(&self, list: &SubkeysList) -> Result<Vec<u32>> {
match list {
SubkeysList::IndexRoot(ir) => {
let mut all_offsets = Vec::new();
for elem in &ir.elements {
if let Ok(sub_cell) = self.find_cell(elem.subkeys_list_offset) {
if let Ok(sub_list) = SubkeysList::parse(&sub_cell.data) {
let sub_offsets = sub_list.get_offsets();
all_offsets.extend(sub_offsets);
}
}
}
Ok(all_offsets)
}
_ => Ok(list.get_offsets()),
}
}
pub fn create_key(&mut self, path: &str) -> Result<u32> {
if path.is_empty() {
return Ok(self.root_offset);
}
let components: Vec<&str> = path.split('\\').filter(|s| !s.is_empty()).collect();
let mut current_offset = self.root_offset;
for component in components {
match self.find_subkey(current_offset, component) {
Ok(offset) => {
current_offset = offset;
}
Err(Error::KeyNotFound(_)) => {
current_offset = self.add_key(current_offset, component)?;
}
Err(e) => return Err(e),
}
}
Ok(current_offset)
}
pub fn add_value_string(&mut self, key_path: &str, name: Option<&str>, value: &str) -> Result<u32> {
let key_offset = self.find_key(key_path)?;
let data = encode_utf16le_string(value);
self.add_value(key_offset, name.unwrap_or(""), DataType::String, &data)
}
pub fn add_value_dword(&mut self, key_path: &str, name: Option<&str>, value: u32) -> Result<u32> {
let key_offset = self.find_key(key_path)?;
self.add_value(key_offset, name.unwrap_or(""), DataType::Dword, &value.to_le_bytes())
}
pub fn add_value_qword(&mut self, key_path: &str, name: Option<&str>, value: u64) -> Result<u32> {
let key_offset = self.find_key(key_path)?;
self.add_value(key_offset, name.unwrap_or(""), DataType::Qword, &value.to_le_bytes())
}
pub fn add_value_binary(&mut self, key_path: &str, name: Option<&str>, data: &[u8]) -> Result<u32> {
let key_offset = self.find_key(key_path)?;
self.add_value(key_offset, name.unwrap_or(""), DataType::Binary, data)
}
pub fn add_value_with_type(
&mut self,
key_path: &str,
name: Option<&str>,
data: &[u8],
data_type: DataType,
) -> Result<u32> {
let key_offset = self.find_key(key_path)?;
self.add_value(key_offset, name.unwrap_or(""), data_type, data)
}
pub fn build(&mut self) -> Result<Vec<u8>> {
self.to_bytes().map_err(Error::Io)
}
pub fn build_to_writer<W: Write + Seek>(&mut self, writer: &mut W) -> io::Result<()> {
for cell in &self.cells {
let bin_start = (cell.offset / MIN_HIVE_BIN_SIZE as u32) * MIN_HIVE_BIN_SIZE as u32;
let header_end = bin_start + HIVE_BIN_HEADER_SIZE as u32;
if cell.offset < header_end {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Cell at offset 0x{:08x} is within bin header (bin starts at 0x{:08x}, data starts at 0x{:08x})",
cell.offset, bin_start, header_end)
));
}
}
let max_cell_end = self.cells.iter()
.map(|c| c.offset + c.allocated_size as u32)
.max()
.unwrap_or(HIVE_BIN_HEADER_SIZE as u32);
let actual_hive_size = ((max_cell_end + MIN_HIVE_BIN_SIZE as u32 - 1)
/ MIN_HIVE_BIN_SIZE as u32) * MIN_HIVE_BIN_SIZE as u32;
self.base_block.hive_bins_data_size = actual_hive_size;
self.base_block.root_cell_offset = self.root_offset;
self.base_block.prepare_for_write();
self.base_block.complete_write();
self.base_block.write(writer)?;
let mut current_bin_offset = 0u32;
while current_bin_offset < actual_hive_size {
let bin_size = MIN_HIVE_BIN_SIZE as u32;
let bin_header = HiveBinHeader::new(current_bin_offset, bin_size);
bin_header.write(writer)?;
let bin_end = current_bin_offset + bin_size;
let mut cell_offset = current_bin_offset + HIVE_BIN_HEADER_SIZE as u32;
let cells_in_bin: Vec<_> = self
.cells
.iter()
.filter(|c| c.offset >= cell_offset && c.offset < bin_end)
.cloned()
.collect();
let mut sorted_cells = cells_in_bin;
sorted_cells.sort_by_key(|c| c.offset);
for cell in &sorted_cells {
let cell_end = cell.offset + cell.allocated_size as u32;
if cell_end > bin_end {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Cell at offset {} with size {} exceeds bin boundary {}",
cell.offset, cell.allocated_size, bin_end)
));
}
if cell.offset > cell_offset {
let free_size = cell.offset - cell_offset;
writer.write_all(&(free_size as i32).to_le_bytes())?;
writer.write_all(&vec![0u8; free_size as usize - 4])?;
}
let cell_size = cell.allocated_size;
let size_value = -(cell_size as i32);
writer.write_all(&size_value.to_le_bytes())?;
writer.write_all(&cell.data)?;
let padding = cell_size - 4 - cell.data.len();
if padding > 0 {
writer.write_all(&vec![0u8; padding])?;
}
cell_offset = cell.offset + cell_size as u32;
}
if cell_offset < bin_end {
let free_size = bin_end - cell_offset;
writer.write_all(&(free_size as i32).to_le_bytes())?;
writer.write_all(&vec![0u8; free_size as usize - 4])?;
}
current_bin_offset += bin_size;
}
writer.flush()?;
Ok(())
}
pub fn write_to_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
let file = File::create(path)?;
let mut writer = BufWriter::new(file);
self.build_to_writer(&mut writer)
}
pub fn to_bytes(&mut self) -> io::Result<Vec<u8>> {
let mut buffer = Vec::new();
let mut cursor = Cursor::new(&mut buffer);
self.build_to_writer(&mut cursor)?;
Ok(buffer)
}
}
impl Default for HiveBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hive::RegistryHive;
#[test]
fn test_create_empty_hive() {
let mut builder = HiveBuilder::new();
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let root = hive.root_key().unwrap();
assert!(root.is_root());
}
#[test]
fn test_add_key() {
let mut builder = HiveBuilder::new();
let root_offset = builder.root_offset();
let software_offset = builder.add_key(root_offset, "Software").unwrap();
assert!(software_offset > 0);
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let root = hive.root_key().unwrap();
let subkeys = root.subkeys().unwrap();
assert_eq!(subkeys.len(), 1);
assert_eq!(subkeys[0].name(), "Software");
}
#[test]
fn test_add_value() {
let mut builder = HiveBuilder::new();
let root_offset = builder.root_offset();
builder
.add_value(root_offset, "TestValue", DataType::Dword, &42u32.to_le_bytes())
.unwrap();
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let root = hive.root_key().unwrap();
let values = root.values().unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values[0].name(), "TestValue");
assert_eq!(values[0].dword_data().unwrap(), 42);
}
#[test]
fn test_nested_keys() {
let mut builder = HiveBuilder::new();
let root_offset = builder.root_offset();
let software_offset = builder.add_key(root_offset, "Software").unwrap();
let microsoft_offset = builder.add_key(software_offset, "Microsoft").unwrap();
builder.add_key(microsoft_offset, "Windows").unwrap();
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let key = hive.open_key("Software\\Microsoft\\Windows").unwrap();
assert_eq!(key.name(), "Windows");
}
#[test]
fn test_tree_builder() {
let mut root = KeyTreeNode::new("ROOT");
let mut software = KeyTreeNode::new("Software");
software.values.push(KeyTreeValue {
name: "Version".to_string(),
data_type: DataType::String,
data: encode_utf16le_string("1.0.0"),
});
let mut microsoft = KeyTreeNode::new("Microsoft");
microsoft.values.push(KeyTreeValue {
name: "ProductID".to_string(),
data_type: DataType::Dword,
data: 12345u32.to_le_bytes().to_vec(),
});
software.children.push(microsoft);
root.children.push(software);
let mut builder = HiveBuilder::from_tree(root);
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let root_key = hive.root_key().unwrap();
assert_eq!(root_key.name(), "ROOT");
let software_key = hive.open_key("Software").unwrap();
assert_eq!(software_key.name(), "Software");
let values = software_key.values().unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values[0].name(), "Version");
let microsoft_key = hive.open_key("Software\\Microsoft").unwrap();
let ms_values = microsoft_key.values().unwrap();
assert_eq!(ms_values.len(), 1);
assert_eq!(ms_values[0].name(), "ProductID");
assert_eq!(ms_values[0].dword_data().unwrap(), 12345);
}
#[test]
fn test_version_constructors() {
let builder1 = HiveBuilder::new();
assert!(builder1.root_offset() > 0);
let builder2 = HiveBuilder::with_version(1, 5);
assert!(builder2.root_offset() > 0);
let mut builder3 = HiveBuilder::new_with_name("TestRoot");
let bytes = builder3.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
assert_eq!(hive.root_key().unwrap().name(), "TestRoot");
}
#[test]
fn test_key_tree_path_creation() {
let mut root = KeyTreeNode::new("ROOT");
let leaf = root.get_or_create_path("Software\\Microsoft\\Windows\\CurrentVersion");
leaf.values.push(KeyTreeValue {
name: "Version".to_string(),
data_type: DataType::Dword,
data: 10u32.to_le_bytes().to_vec(),
});
assert_eq!(root.count_keys(), 5); assert_eq!(root.count_values(), 1);
let mut builder = HiveBuilder::from_tree(root);
let bytes = builder.to_bytes().unwrap();
let hive = RegistryHive::from_bytes(bytes).unwrap();
let key = hive.open_key("Software\\Microsoft\\Windows\\CurrentVersion").unwrap();
let values = key.values().unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values[0].dword_data().unwrap(), 10);
}
}