use once_cell::unsync::OnceCell;
use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead};
use super::{WindowsHive, WindowsHiveCellIndex, WindowsKeyNode};
use crate::{ArchAdapter, WindowsError, WindowsOs, offset};
pub struct WindowsKeyControlBlock<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
vmi: VmiState<'a, WindowsOs<Driver>>,
va: Va,
name_block: OnceCell<Va>,
}
impl<Driver> VmiVa for WindowsKeyControlBlock<'_, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
fn va(&self) -> Va {
self.va
}
}
impl<'a, Driver> WindowsKeyControlBlock<'a, Driver>
where
Driver: VmiRead,
Driver::Architecture: ArchAdapter<Driver>,
{
pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
Self {
vmi,
va,
name_block: OnceCell::new(),
}
}
pub fn refcount(&self) -> Result<u64, VmiError> {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let refcount = self
.vmi
.read_field(self.va, &CM_KEY_CONTROL_BLOCK.RefCount)?;
Ok(refcount)
}
pub fn discarded(&self) -> Result<bool, VmiError> {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let discarded = self
.vmi
.read_field(self.va, &CM_KEY_CONTROL_BLOCK.Discarded)?;
Ok(CM_KEY_CONTROL_BLOCK.Discarded.extract(discarded) != 0)
}
pub fn parent(&self) -> Result<Option<WindowsKeyControlBlock<'a, Driver>>, VmiError> {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let parent_kcb = self
.vmi
.read_va_native(self.va + CM_KEY_CONTROL_BLOCK.ParentKcb.offset())?;
if parent_kcb.is_null() {
return Ok(None);
}
Ok(Some(WindowsKeyControlBlock::new(self.vmi, parent_kcb)))
}
pub fn name(&self) -> Result<String, VmiError> {
let CM_NAME_CONTROL_BLOCK = offset!(self.vmi, _CM_NAME_CONTROL_BLOCK);
let name_block = self.name_block()?;
let compressed = self
.vmi
.read_field(name_block, &CM_NAME_CONTROL_BLOCK.Compressed)?;
let compressed = CM_NAME_CONTROL_BLOCK.Compressed.extract(compressed) != 0;
let name_length = self
.vmi
.read_field(name_block, &CM_NAME_CONTROL_BLOCK.NameLength)?;
if compressed {
self.vmi.read_string_limited(
name_block + CM_NAME_CONTROL_BLOCK.Name.offset(),
name_length as usize,
)
}
else {
self.vmi.read_string_utf16_limited(
name_block + CM_NAME_CONTROL_BLOCK.Name.offset(),
name_length as usize,
)
}
}
fn name_block(&self) -> Result<Va, VmiError> {
self.name_block
.get_or_try_init(|| {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let name_block = self
.vmi
.read_va_native(self.va + CM_KEY_CONTROL_BLOCK.NameBlock.offset())?;
if name_block.is_null() {
return Err(
WindowsError::CorruptedStruct("CM_KEY_CONTROL_BLOCK.NameBlock").into(),
);
}
Ok(name_block)
})
.copied()
}
pub fn hive(&self) -> Result<Option<WindowsHive<'a, Driver>>, VmiError> {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let key_hive = self
.vmi
.read_va_native(self.va + CM_KEY_CONTROL_BLOCK.KeyHive.offset())?;
if key_hive.is_null() {
return Ok(None);
}
Ok(Some(WindowsHive::new(self.vmi, key_hive)))
}
pub fn key_node(&self) -> Result<Option<WindowsKeyNode<'a, Driver>>, VmiError> {
let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
let hive = match self.hive()? {
Some(hive) => hive,
None => return Ok(None),
};
let key_cell = self
.vmi
.read_u32(self.va + CM_KEY_CONTROL_BLOCK.KeyCell.offset())?;
if key_cell == 0 {
return Ok(None);
}
Ok(hive
.cell(WindowsHiveCellIndex::new(key_cell))?
.map(|va| WindowsKeyNode::new(self.vmi, hive.va, va)))
}
}