Skip to main content

vmi_os_windows/comps/hive/
key_control_block.rs

1use once_cell::unsync::OnceCell;
2use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead};
3
4use super::{WindowsHive, WindowsHiveCellIndex, WindowsKeyNode};
5use crate::{ArchAdapter, WindowsError, WindowsOs, offset};
6
7/// A Windows registry key control block.
8///
9/// A registry key in the kernel mode registry cache. It helps the Configuration
10/// Manager manage registry keys efficiently by avoiding redundant registry
11/// lookups.
12///
13/// # Implementation Details
14///
15/// Corresponds to `_CM_KEY_CONTROL_BLOCK`.
16pub struct WindowsKeyControlBlock<'a, Driver>
17where
18    Driver: VmiRead,
19    Driver::Architecture: ArchAdapter<Driver>,
20{
21    /// The VMI state.
22    vmi: VmiState<'a, WindowsOs<Driver>>,
23
24    /// Address of the `_CM_KEY_CONTROL_BLOCK` structure.
25    va: Va,
26
27    /// Cached address of the `_CM_NAME_CONTROL_BLOCK` structure.
28    name_block: OnceCell<Va>,
29}
30
31impl<Driver> VmiVa for WindowsKeyControlBlock<'_, Driver>
32where
33    Driver: VmiRead,
34    Driver::Architecture: ArchAdapter<Driver>,
35{
36    fn va(&self) -> Va {
37        self.va
38    }
39}
40
41impl<'a, Driver> WindowsKeyControlBlock<'a, Driver>
42where
43    Driver: VmiRead,
44    Driver::Architecture: ArchAdapter<Driver>,
45{
46    /// Creates a new Windows key control block.
47    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
48        Self {
49            vmi,
50            va,
51            name_block: OnceCell::new(),
52        }
53    }
54
55    /// Returns the reference count of the key control block.
56    ///
57    /// # Implementation Details
58    ///
59    /// Corresponds to `_CM_KEY_CONTROL_BLOCK.RefCount`.
60    pub fn refcount(&self) -> Result<u64, VmiError> {
61        let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
62
63        let refcount = self
64            .vmi
65            .read_field(self.va, &CM_KEY_CONTROL_BLOCK.RefCount)?;
66
67        Ok(refcount)
68    }
69
70    /// Returns whether the key control block has been marked as discarded.
71    ///
72    /// A discarded KCB no longer backs a live key node. The cache slot is
73    /// kept alive only until its reference count drops to zero.
74    ///
75    /// # Implementation Details
76    ///
77    /// Corresponds to `_CM_KEY_CONTROL_BLOCK.Discarded` or
78    /// `_CM_KEY_CONTROL_BLOCK.Delete`.
79    pub fn discarded(&self) -> Result<bool, VmiError> {
80        let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
81
82        let discarded = self
83            .vmi
84            .read_field(self.va, &CM_KEY_CONTROL_BLOCK.Discarded)?;
85
86        Ok(CM_KEY_CONTROL_BLOCK.Discarded.extract(discarded) != 0)
87    }
88
89    /// Returns the parent key control block.
90    ///
91    /// # Implementation Details
92    ///
93    /// Corresponds to `_CM_KEY_CONTROL_BLOCK.ParentKcb`.
94    pub fn parent(&self) -> Result<Option<WindowsKeyControlBlock<'a, Driver>>, VmiError> {
95        let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
96
97        let parent_kcb = self
98            .vmi
99            .read_va_native(self.va + CM_KEY_CONTROL_BLOCK.ParentKcb.offset())?;
100
101        if parent_kcb.is_null() {
102            return Ok(None);
103        }
104
105        Ok(Some(WindowsKeyControlBlock::new(self.vmi, parent_kcb)))
106    }
107
108    /// Returns the name of the key.
109    ///
110    /// # Implementation Details
111    ///
112    /// Corresponds to `_CM_KEY_CONTROL_BLOCK.NameBlock`.
113    /// If the `_CM_NAME_CONTROL_BLOCK.Compressed` field is set, the name is
114    /// read as an ASCII string. Otherwise, the name is read as a UTF-16
115    /// string.
116    pub fn name(&self) -> Result<String, VmiError> {
117        let CM_NAME_CONTROL_BLOCK = offset!(self.vmi, _CM_NAME_CONTROL_BLOCK);
118
119        let name_block = self.name_block()?;
120
121        let compressed = self
122            .vmi
123            .read_field(name_block, &CM_NAME_CONTROL_BLOCK.Compressed)?;
124
125        let compressed = CM_NAME_CONTROL_BLOCK.Compressed.extract(compressed) != 0;
126
127        let name_length = self
128            .vmi
129            .read_field(name_block, &CM_NAME_CONTROL_BLOCK.NameLength)?;
130
131        if compressed {
132            self.vmi.read_string_limited(
133                name_block + CM_NAME_CONTROL_BLOCK.Name.offset(),
134                name_length as usize,
135            )
136        }
137        else {
138            self.vmi.read_string_utf16_limited(
139                name_block + CM_NAME_CONTROL_BLOCK.Name.offset(),
140                name_length as usize,
141            )
142        }
143    }
144
145    /// Returns the name control block associated with the key control block.
146    fn name_block(&self) -> Result<Va, VmiError> {
147        self.name_block
148            .get_or_try_init(|| {
149                let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
150
151                let name_block = self
152                    .vmi
153                    .read_va_native(self.va + CM_KEY_CONTROL_BLOCK.NameBlock.offset())?;
154
155                if name_block.is_null() {
156                    return Err(
157                        WindowsError::CorruptedStruct("CM_KEY_CONTROL_BLOCK.NameBlock").into(),
158                    );
159                }
160
161                Ok(name_block)
162            })
163            .copied()
164    }
165
166    /// Returns the hive that owns this key control block.
167    ///
168    /// # Implementation Details
169    ///
170    /// Corresponds to `_CM_KEY_CONTROL_BLOCK.KeyHive`.
171    pub fn hive(&self) -> Result<Option<WindowsHive<'a, Driver>>, VmiError> {
172        let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
173
174        let key_hive = self
175            .vmi
176            .read_va_native(self.va + CM_KEY_CONTROL_BLOCK.KeyHive.offset())?;
177
178        if key_hive.is_null() {
179            return Ok(None);
180        }
181
182        Ok(Some(WindowsHive::new(self.vmi, key_hive)))
183    }
184
185    /// Returns the key node backing this key control block.
186    ///
187    /// # Implementation Details
188    ///
189    /// Resolves `_CM_KEY_CONTROL_BLOCK.KeyCell` against
190    /// `_CM_KEY_CONTROL_BLOCK.KeyHive`.
191    pub fn key_node(&self) -> Result<Option<WindowsKeyNode<'a, Driver>>, VmiError> {
192        let CM_KEY_CONTROL_BLOCK = offset!(self.vmi, _CM_KEY_CONTROL_BLOCK);
193
194        let hive = match self.hive()? {
195            Some(hive) => hive,
196            None => return Ok(None),
197        };
198
199        let key_cell = self
200            .vmi
201            .read_u32(self.va + CM_KEY_CONTROL_BLOCK.KeyCell.offset())?;
202
203        if key_cell == 0 {
204            return Ok(None);
205        }
206
207        Ok(hive
208            .cell(WindowsHiveCellIndex::new(key_cell))?
209            .map(|va| WindowsKeyNode::new(self.vmi, hive.va, va)))
210    }
211}