mod key_control_block;
mod key_index;
mod key_node;
mod key_value;
use once_cell::unsync::OnceCell;
use vmi_core::{Registers as _, Va, VmiError, VmiState, VmiVa, driver::VmiRead};
pub use self::{
key_control_block::WindowsKeyControlBlock,
key_index::WindowsKeyIndex,
key_node::WindowsKeyNode,
key_value::{WindowsKeyValue, WindowsKeyValueData, WindowsKeyValueFlags, WindowsKeyValueType},
};
use crate::{
ArchAdapter, KeyControlBlockIterator, WindowsError, WindowsOs, WindowsOsExt as _, offset,
};
pub const HCELL_HEADER_SIZE: u64 = 4;
pub const HCELL_INDEX_SIZE: u64 = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowsHiveStorageType {
Stable,
Volatile,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct WindowsHiveCellIndex(u32);
impl WindowsHiveCellIndex {
const HCELL_TYPE_MASK: u32 = 0x80000000;
const HCELL_TYPE_SHIFT: u32 = 31;
const HCELL_TABLE_MASK: u32 = 0x7fe00000;
const HCELL_TABLE_SHIFT: u32 = 21;
const HCELL_BLOCK_MASK: u32 = 0x001ff000;
const HCELL_BLOCK_SHIFT: u32 = 12;
const HCELL_OFFSET_MASK: u32 = 0x00000fff;
pub const NIL: Self = Self(0xFFFFFFFF);
pub fn new(value: u32) -> Self {
Self(value)
}
pub fn is_nil(&self) -> bool {
self == &Self::NIL
}
pub fn raw(&self) -> u32 {
self.0
}
pub fn storage(&self) -> WindowsHiveStorageType {
match (self.0 & Self::HCELL_TYPE_MASK) >> Self::HCELL_TYPE_SHIFT {
0 => WindowsHiveStorageType::Stable,
1 => WindowsHiveStorageType::Volatile,
_ => unreachable!(),
}
}
pub fn table(&self) -> u32 {
(self.0 & Self::HCELL_TABLE_MASK) >> Self::HCELL_TABLE_SHIFT
}
pub fn block(&self) -> u32 {
(self.0 & Self::HCELL_BLOCK_MASK) >> Self::HCELL_BLOCK_SHIFT
}
pub fn offset(&self) -> u32 {
self.0 & Self::HCELL_OFFSET_MASK
}
}
impl std::fmt::Debug for WindowsHiveCellIndex {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("WindowsHiveCellIndex")
.field("storage", &self.storage())
.field("table", &self.table())
.field("block", &self.block())
.field("offset", &self.offset())
.finish()
}
}
pub struct WindowsHiveMapDirectory<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
}
impl<Driver> VmiVa for WindowsHiveMapDirectory<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsHiveMapDirectory<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self { vmi, va }
}
pub fn table(&self, index: u32) -> Result<Option<WindowsHiveMapTable<'a, Driver>>, VmiError> {
let HMAP_DIRECTORY = offset!(self.vmi, _HMAP_DIRECTORY);
let table = self.vmi.read_va_native(
self.va
+ HMAP_DIRECTORY.Directory.offset()
+ (index as u64) * self.vmi.registers().address_width() as u64,
)?;
if table.is_null() {
return Ok(None);
}
Ok(Some(WindowsHiveMapTable::new(self.vmi, table)))
}
}
pub struct WindowsHiveMapTable<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
}
impl<Driver> VmiVa for WindowsHiveMapTable<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsHiveMapTable<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self { vmi, va }
}
pub fn entry(&self, index: u32) -> Result<WindowsHiveMapEntry<'a, Driver>, VmiError> {
let HMAP_TABLE = offset!(self.vmi, _HMAP_TABLE);
let HMAP_ENTRY = offset!(self.vmi, _HMAP_ENTRY);
let entry = self.va + HMAP_TABLE.Table.offset() + (index as u64) * HMAP_ENTRY.len() as u64;
Ok(WindowsHiveMapEntry::new(self.vmi, entry))
}
}
pub struct WindowsHiveMapEntry<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
}
impl<Driver> VmiVa for WindowsHiveMapEntry<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsHiveMapEntry<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self { vmi, va }
}
pub fn block_address(&self) -> Result<Va, VmiError> {
let HMAP_ENTRY = offset!(self.vmi, _HMAP_ENTRY);
match (
&HMAP_ENTRY.BlockAddress,
&HMAP_ENTRY.BlockOffset,
&HMAP_ENTRY.PermanentBinAddress,
) {
(Some(BlockAddress), _, _) => self.vmi.read_va_native(self.va + BlockAddress.offset()),
(None, Some(BlockOffset), Some(PermanentBinAddress)) => {
let block_offset = self
.vmi
.read_address_native(self.va + BlockOffset.offset())?;
let permanent_bin_address = self
.vmi
.read_address_native(self.va + PermanentBinAddress.offset())?;
Ok(Va((permanent_bin_address & !0xf) + block_offset))
}
_ => Err(WindowsError::CorruptedStruct("_HMAP_ENTRY").into()),
}
}
}
pub struct WindowsHiveBaseBlock<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
}
impl<Driver> VmiVa for WindowsHiveBaseBlock<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsHiveBaseBlock<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub const SIGNATURE: u32 = 0x6667_6572;
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self { vmi, va }
}
pub fn signature(&self) -> Result<u32, VmiError> {
let HBASE_BLOCK = offset!(self.vmi, _HBASE_BLOCK);
self.vmi.read_u32(self.va + HBASE_BLOCK.Signature.offset())
}
pub fn root_cell_index(&self) -> Result<WindowsHiveCellIndex, VmiError> {
let HBASE_BLOCK = offset!(self.vmi, _HBASE_BLOCK);
self.vmi
.read_u32(self.va + HBASE_BLOCK.RootCell.offset())
.map(WindowsHiveCellIndex)
}
}
pub struct WindowsHive<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
base_block: OnceCell<Va>,
}
impl<Driver> VmiVa for WindowsHive<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsHive<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub const SIGNATURE: u32 = 0xBEE0_BEE0;
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self {
vmi,
va,
base_block: OnceCell::new(),
}
}
pub fn signature(&self) -> Result<u32, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
self.vmi.read_u32(self.va + CMHIVE.Signature.offset())
}
pub fn base_block(&self) -> Result<WindowsHiveBaseBlock<'a, Driver>, VmiError> {
self.base_block
.get_or_try_init(|| {
let CMHIVE = offset!(self.vmi, _CMHIVE);
let base_block = self
.vmi
.read_va_native(self.va + CMHIVE.BaseBlock.offset())?;
if base_block.is_null() {
return Err(WindowsError::CorruptedStruct("CMHIVE.BaseBlock").into());
}
Ok(base_block)
})
.copied()
.map(|va| WindowsHiveBaseBlock::new(self.vmi, va))
}
pub fn file_full_path(&self) -> Result<String, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
self.vmi
.os()
.read_unicode_string(self.va + CMHIVE.FileFullPath.offset())
}
pub fn file_user_name(&self) -> Result<String, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
self.vmi
.os()
.read_unicode_string(self.va + CMHIVE.FileUserName.offset())
}
pub fn hive_root_path(&self) -> Result<String, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
self.vmi
.os()
.read_unicode_string(self.va + CMHIVE.HiveRootPath.offset())
}
pub fn storage_directory(
&self,
ty: WindowsHiveStorageType,
) -> Result<Option<WindowsHiveMapDirectory<'a, Driver>>, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
let DUAL = offset!(self.vmi, _DUAL);
let storage = self.va + CMHIVE.Storage.offset() + (ty as u64) * DUAL.len() as u64;
let map = self.vmi.read_va_native(storage + DUAL.Map.offset())?;
if map.is_null() {
return Ok(None);
}
Ok(Some(WindowsHiveMapDirectory::new(self.vmi, map)))
}
pub fn version(&self) -> Result<u32, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
self.vmi.read_u32(self.va + CMHIVE.Version.offset())
}
pub fn root_cell_index(&self) -> Result<WindowsHiveCellIndex, VmiError> {
self.base_block()?.root_cell_index()
}
pub fn cell(&self, index: WindowsHiveCellIndex) -> Result<Option<Va>, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
let HBASE_BLOCK = offset!(self.vmi, _HBASE_BLOCK);
let flat = self.vmi.read_field(self.va, &CMHIVE.Flat)?;
let flat = CMHIVE.Flat.extract(flat) != 0;
if flat {
let base = self
.vmi
.read_va_native(self.va + CMHIVE.BaseBlock.offset())?;
return Ok(Some(
base + HBASE_BLOCK.len() as u64 + (index.0 as u64) + HCELL_HEADER_SIZE,
));
}
let directory = match self.storage_directory(index.storage())? {
Some(directory) => directory,
None => return Ok(None),
};
let table = match directory.table(index.table())? {
Some(table) => table,
None => return Ok(None),
};
let entry = table.entry(index.block())?;
Ok(Some(
entry.block_address()? + (index.offset() as u64) + HCELL_HEADER_SIZE,
))
}
pub fn root_key(&self) -> Result<WindowsKeyNode<'a, Driver>, VmiError> {
let index = self.root_cell_index()?;
match self.cell(index)? {
Some(va) => Ok(WindowsKeyNode::new(self.vmi, self.va, va)),
None => Err(WindowsError::CorruptedStruct("CMHIVE.BaseBlock.RootCell").into()),
}
}
pub fn lookup(
&self,
path: impl AsRef<str>,
) -> Result<Option<WindowsKeyNode<'a, Driver>>, VmiError> {
self.root_key()?.lookup(path)
}
pub fn kcbs(&self) -> Result<KeyControlBlockIterator<'a, Driver>, VmiError> {
let CMHIVE = offset!(self.vmi, _CMHIVE);
let cache_va = self
.vmi
.read_va_native(self.va + CMHIVE.KcbCacheTable.offset())?;
if cache_va.is_null() {
return Ok(KeyControlBlockIterator::empty(self.vmi));
}
let bucket_count = self
.vmi
.read_u32(self.va + CMHIVE.KcbCacheTableSize.offset())?;
Ok(KeyControlBlockIterator::new(
self.vmi,
cache_va,
bucket_count,
))
}
}