Skip to main content

vmi_os_windows/comps/hive/
key_value.rs

1use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead, trace::Hex};
2
3use super::{HCELL_INDEX_SIZE, WindowsHive, WindowsHiveCellIndex};
4use crate::{ArchAdapter, KeyValueIterator, WindowsError, WindowsOs, offset};
5
6/// Marker in `_CM_KEY_VALUE.DataLength` indicating that the data is stored
7/// inline in the `Data` field itself.
8const CM_KEY_VALUE_SPECIAL_SIZE: u32 = 0x8000_0000;
9
10/// Threshold above which a value may be stored as `_CM_BIG_DATA`.
11///
12/// Whether it actually is depends on the hive's minor version.
13/// See [`data_bytes`] for the full discriminator.
14///
15/// [`data_bytes`]: WindowsKeyValue::data_bytes
16const CM_KEY_VALUE_BIG: u32 = 0x3FD8;
17
18/// First hive version that supports `_CM_BIG_DATA`.
19///
20/// Hives at version 3 or below store every non-inline value in a single cell,
21/// regardless of size.
22const HSYS_VERSION_4: u32 = 4;
23
24/// Signature of a `_CM_BIG_DATA` cell.
25const CM_BIG_DATA_SIGNATURE: u16 = 0x6264; // "db"
26
27/// A Windows registry value.
28///
29/// A named value attached to a registry key. Values are the leaves of the
30/// registry tree - the actual settings that callers read.
31///
32/// # Implementation Details
33///
34/// Corresponds to `_CM_KEY_VALUE`.
35pub struct WindowsKeyValue<'a, Driver>
36where
37    Driver: VmiRead,
38    Driver::Architecture: ArchAdapter<Driver>,
39{
40    /// The VMI state.
41    vmi: VmiState<'a, WindowsOs<Driver>>,
42
43    /// Address of the owning `_CMHIVE`.
44    hive_va: Va,
45
46    /// Address of the `_CM_KEY_VALUE` structure.
47    va: Va,
48}
49
50impl<Driver> VmiVa for WindowsKeyValue<'_, Driver>
51where
52    Driver: VmiRead,
53    Driver::Architecture: ArchAdapter<Driver>,
54{
55    fn va(&self) -> Va {
56        self.va
57    }
58}
59
60bitflags::bitflags! {
61    /// Flags stored in `_CM_KEY_VALUE.Flags`.
62    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
63    pub struct WindowsKeyValueFlags: u16 {
64        /// The value name is stored in a compressed (ASCII) form.
65        const COMP_NAME = 0x0001;
66    }
67}
68
69/// Registry value type.
70///
71/// Corresponds to `REG_*` constants in the Windows API.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum WindowsKeyValueType {
74    /// No defined type.
75    ///
76    /// # Implementation Details
77    ///
78    /// Corresponds to `REG_NONE`.
79    None,
80
81    /// Null-terminated UTF-16 string.
82    ///
83    /// # Implementation Details
84    ///
85    /// Corresponds to `REG_SZ`.
86    Sz,
87
88    /// UTF-16 string containing unexpanded `%VAR%` references.
89    ///
90    /// # Implementation Details
91    ///
92    /// Corresponds to `REG_EXPAND_SZ`.
93    ExpandSz,
94
95    /// Binary data in any form.
96    ///
97    /// # Implementation Details
98    ///
99    /// Corresponds to `REG_BINARY`.
100    Binary,
101
102    /// A 32-bit number.
103    ///
104    /// # Implementation Details
105    ///
106    /// Corresponds to `REG_DWORD`.
107    Dword,
108
109    /// A 32-bit number in big-endian format.
110    ///
111    /// # Implementation Details
112    ///
113    /// Corresponds to `REG_DWORD_BIG_ENDIAN`.
114    DwordBigEndian,
115
116    /// Symbolic link stored as a UTF-16 string.
117    ///
118    /// # Implementation Details
119    ///
120    /// Corresponds to `REG_LINK`.
121    Link,
122
123    /// Sequence of null-terminated UTF-16 strings, terminated by an empty string.
124    ///
125    /// # Implementation Details
126    ///
127    /// Corresponds to `REG_MULTI_SZ`.
128    MultiSz,
129
130    /// Device-driver resource list (opaque bytes).
131    ///
132    /// # Implementation Details
133    ///
134    /// Corresponds to `REG_RESOURCE_LIST`.
135    ResourceList,
136
137    /// Device-driver resource descriptor (opaque bytes).
138    ///
139    /// # Implementation Details
140    ///
141    /// Corresponds to `REG_FULL_RESOURCE_DESCRIPTOR`.
142    FullResourceDescriptor,
143
144    /// Device-driver resource requirements list (opaque bytes).
145    ///
146    /// # Implementation Details
147    ///
148    /// Corresponds to `REG_RESOURCE_REQUIREMENTS_LIST`.
149    ResourceRequirementsList,
150
151    /// `REG_QWORD` (little-endian 64-bit integer).
152    ///
153    /// # Implementation Details
154    ///
155    /// Corresponds to `REG_QWORD`.
156    Qword,
157
158    /// Type not recognized by this crate.
159    Unknown(u32),
160}
161
162impl From<WindowsKeyValueType> for u32 {
163    fn from(value: WindowsKeyValueType) -> Self {
164        match value {
165            WindowsKeyValueType::None => 0,
166            WindowsKeyValueType::Sz => 1,
167            WindowsKeyValueType::ExpandSz => 2,
168            WindowsKeyValueType::Binary => 3,
169            WindowsKeyValueType::Dword => 4,
170            WindowsKeyValueType::DwordBigEndian => 5,
171            WindowsKeyValueType::Link => 6,
172            WindowsKeyValueType::MultiSz => 7,
173            WindowsKeyValueType::ResourceList => 8,
174            WindowsKeyValueType::FullResourceDescriptor => 9,
175            WindowsKeyValueType::ResourceRequirementsList => 10,
176            WindowsKeyValueType::Qword => 11,
177            WindowsKeyValueType::Unknown(raw) => raw,
178        }
179    }
180}
181
182impl From<u32> for WindowsKeyValueType {
183    fn from(value: u32) -> Self {
184        match value {
185            0 => Self::None,
186            1 => Self::Sz,
187            2 => Self::ExpandSz,
188            3 => Self::Binary,
189            4 => Self::Dword,
190            5 => Self::DwordBigEndian,
191            6 => Self::Link,
192            7 => Self::MultiSz,
193            8 => Self::ResourceList,
194            9 => Self::FullResourceDescriptor,
195            10 => Self::ResourceRequirementsList,
196            11 => Self::Qword,
197            other => Self::Unknown(other),
198        }
199    }
200}
201
202/// Parsed registry value data, dispatched on [`WindowsKeyValueType`].
203///
204/// For string variants the trailing NUL (if any) is stripped.
205/// For [`WindowsKeyValueData::MultiSz`] each element is likewise stripped
206/// and the empty terminator is dropped.
207#[derive(Debug, Clone)]
208pub enum WindowsKeyValueData {
209    /// `REG_NONE`, or any value whose declared length is zero.
210    None,
211
212    /// `REG_SZ`.
213    Sz(String),
214
215    /// `REG_EXPAND_SZ`.
216    ExpandSz(String),
217
218    /// `REG_BINARY`.
219    Binary(Vec<u8>),
220
221    /// `REG_DWORD`.
222    Dword(u32),
223
224    /// `REG_DWORD_BIG_ENDIAN`.
225    DwordBigEndian(u32),
226
227    /// `REG_LINK`.
228    Link(String),
229
230    /// `REG_MULTI_SZ`.
231    MultiSz(Vec<String>),
232
233    /// `REG_RESOURCE_LIST`.
234    ResourceList(Vec<u8>),
235
236    /// `REG_FULL_RESOURCE_DESCRIPTOR`.
237    FullResourceDescriptor(Vec<u8>),
238
239    /// `REG_RESOURCE_REQUIREMENTS_LIST`.
240    ResourceRequirementsList(Vec<u8>),
241
242    /// `REG_QWORD`.
243    Qword(u64),
244
245    /// Value whose type was not recognized.
246    Unknown {
247        /// Raw `REG_*` type code.
248        ty: u32,
249
250        /// Raw data bytes as stored in the hive.
251        bytes: Vec<u8>,
252    },
253}
254
255impl WindowsKeyValueData {
256    /// Decodes raw value bytes into the typed enum.
257    fn from_raw(value_type: WindowsKeyValueType, bytes: Vec<u8>) -> Self {
258        if bytes.is_empty() {
259            return Self::from_empty(value_type);
260        }
261
262        /// Decodes a UTF-16 byte buffer into a `String`, stripping a single trailing
263        /// NUL if present.
264        fn decode_utf16(bytes: &[u8]) -> String {
265            let (chunks, _) = bytes.as_chunks::<2>();
266            let units = chunks
267                .iter()
268                .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
269                .collect::<Vec<_>>();
270            let end = units.iter().rposition(|&ch| ch != 0).map_or(0, |i| i + 1);
271            String::from_utf16_lossy(&units[..end])
272        }
273
274        /// Decodes a UTF-16 `REG_MULTI_SZ` buffer into a vector of strings, dropping
275        /// empty elements that mark the terminator.
276        fn decode_utf16_multi(bytes: &[u8]) -> Vec<String> {
277            let (chunks, _) = bytes.as_chunks::<2>();
278            let units = chunks
279                .iter()
280                .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
281                .collect::<Vec<_>>();
282
283            units
284                .split(|&ch| ch == 0)
285                .filter(|slice| !slice.is_empty())
286                .map(String::from_utf16_lossy)
287                .collect()
288        }
289
290        /// Decodes the first four bytes of `bytes` as a little-endian `u32`, padding
291        /// with zeros if the buffer is short.
292        fn decode_u32_le(bytes: &[u8]) -> u32 {
293            let mut buffer = [0u8; 4];
294            let n = bytes.len().min(4);
295            buffer[..n].copy_from_slice(&bytes[..n]);
296            u32::from_le_bytes(buffer)
297        }
298
299        /// Decodes the first four bytes of `bytes` as a big-endian `u32`, padding
300        /// with zeros if the buffer is short.
301        fn decode_u32_be(bytes: &[u8]) -> u32 {
302            let mut buffer = [0u8; 4];
303            let n = bytes.len().min(4);
304            buffer[..n].copy_from_slice(&bytes[..n]);
305            u32::from_be_bytes(buffer)
306        }
307
308        /// Decodes the first eight bytes of `bytes` as a little-endian `u64`,
309        /// padding with zeros if the buffer is short.
310        fn decode_u64_le(bytes: &[u8]) -> u64 {
311            let mut buffer = [0u8; 8];
312            let n = bytes.len().min(8);
313            buffer[..n].copy_from_slice(&bytes[..n]);
314            u64::from_le_bytes(buffer)
315        }
316
317        match value_type {
318            WindowsKeyValueType::None => Self::None,
319            WindowsKeyValueType::Sz => Self::Sz(decode_utf16(&bytes)),
320            WindowsKeyValueType::ExpandSz => Self::ExpandSz(decode_utf16(&bytes)),
321            WindowsKeyValueType::Link => Self::Link(decode_utf16(&bytes)),
322            WindowsKeyValueType::MultiSz => Self::MultiSz(decode_utf16_multi(&bytes)),
323            WindowsKeyValueType::Binary => Self::Binary(bytes),
324            WindowsKeyValueType::Dword => Self::Dword(decode_u32_le(&bytes)),
325            WindowsKeyValueType::DwordBigEndian => Self::DwordBigEndian(decode_u32_be(&bytes)),
326            WindowsKeyValueType::Qword => Self::Qword(decode_u64_le(&bytes)),
327            WindowsKeyValueType::ResourceList => Self::ResourceList(bytes),
328            WindowsKeyValueType::FullResourceDescriptor => Self::FullResourceDescriptor(bytes),
329            WindowsKeyValueType::ResourceRequirementsList => Self::ResourceRequirementsList(bytes),
330            WindowsKeyValueType::Unknown(raw) => Self::Unknown { ty: raw, bytes },
331        }
332    }
333
334    /// Returns the empty variant matching `value_type`.
335    fn from_empty(value_type: WindowsKeyValueType) -> Self {
336        match value_type {
337            WindowsKeyValueType::None => Self::None,
338            WindowsKeyValueType::Sz => Self::Sz(String::new()),
339            WindowsKeyValueType::ExpandSz => Self::ExpandSz(String::new()),
340            WindowsKeyValueType::Link => Self::Link(String::new()),
341            WindowsKeyValueType::MultiSz => Self::MultiSz(Vec::new()),
342            WindowsKeyValueType::Binary => Self::Binary(Vec::new()),
343            WindowsKeyValueType::ResourceList => Self::ResourceList(Vec::new()),
344            WindowsKeyValueType::FullResourceDescriptor => Self::FullResourceDescriptor(Vec::new()),
345            WindowsKeyValueType::ResourceRequirementsList => {
346                Self::ResourceRequirementsList(Vec::new())
347            }
348            WindowsKeyValueType::Dword | WindowsKeyValueType::DwordBigEndian => Self::Unknown {
349                ty: u32::from(value_type),
350                bytes: Vec::new(),
351            },
352            WindowsKeyValueType::Qword => Self::Unknown {
353                ty: u32::from(value_type),
354                bytes: Vec::new(),
355            },
356            WindowsKeyValueType::Unknown(raw) => Self::Unknown {
357                ty: raw,
358                bytes: Vec::new(),
359            },
360        }
361    }
362}
363
364impl<'a, Driver> WindowsKeyValue<'a, Driver>
365where
366    Driver: VmiRead,
367    Driver::Architecture: ArchAdapter<Driver>,
368{
369    /// Signature of a `_CM_KEY_VALUE` (`kv`).
370    ///
371    /// # Implementation Details
372    ///
373    /// Corresponds to `CM_KEY_VALUE_SIGNATURE`.
374    pub const SIGNATURE: u16 = 0x6b76; // "kv"
375
376    /// Creates a new key value bound to the given hive.
377    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, hive_va: Va, va: Va) -> Self {
378        Self { vmi, hive_va, va }
379    }
380
381    /// Returns the signature of the key value.
382    ///
383    /// # Implementation Details
384    ///
385    /// Corresponds to `_CM_KEY_VALUE.Signature`.
386    pub fn signature(&self) -> Result<u16, VmiError> {
387        let CM_KEY_VALUE = offset!(self.vmi, _CM_KEY_VALUE);
388
389        self.vmi.read_u16(self.va + CM_KEY_VALUE.Signature.offset())
390    }
391
392    /// Returns the flags of the key value.
393    ///
394    /// # Implementation Details
395    ///
396    /// Corresponds to `_CM_KEY_VALUE.Flags`.
397    pub fn flags(&self) -> Result<WindowsKeyValueFlags, VmiError> {
398        let CM_KEY_VALUE = offset!(self.vmi, _CM_KEY_VALUE);
399
400        let flags = self.vmi.read_u16(self.va + CM_KEY_VALUE.Flags.offset())?;
401        Ok(WindowsKeyValueFlags::from_bits_truncate(flags))
402    }
403
404    /// Returns the type of the key value.
405    ///
406    /// # Implementation Details
407    ///
408    /// Corresponds to `_CM_KEY_VALUE.Type`.
409    pub fn value_type(&self) -> Result<WindowsKeyValueType, VmiError> {
410        let CM_KEY_VALUE = offset!(self.vmi, _CM_KEY_VALUE);
411
412        let ty = self.vmi.read_u32(self.va + CM_KEY_VALUE.Type.offset())?;
413        Ok(WindowsKeyValueType::from(ty))
414    }
415
416    /// Returns the name of the key value.
417    ///
418    /// # Implementation Details
419    ///
420    /// Corresponds to `_CM_KEY_VALUE.Name`. If the `VALUE_COMP_NAME` bit is
421    /// set in `_CM_KEY_VALUE.Flags`, the name is read as an ASCII string.
422    /// Otherwise, the name is read as a UTF-16 string.
423    pub fn name(&self) -> Result<String, VmiError> {
424        let CM_KEY_VALUE = offset!(self.vmi, _CM_KEY_VALUE);
425
426        let flags = self.flags()?;
427        let name_length = self
428            .vmi
429            .read_u16(self.va + CM_KEY_VALUE.NameLength.offset())?;
430        let name_va = self.va + CM_KEY_VALUE.Name.offset();
431
432        if flags.contains(WindowsKeyValueFlags::COMP_NAME) {
433            self.vmi.read_string_limited(name_va, name_length as usize)
434        }
435        else {
436            self.vmi
437                .read_string_utf16_limited(name_va, name_length as usize)
438        }
439    }
440
441    /// Returns the raw bytes of the key value.
442    ///
443    /// # Implementation Details
444    ///
445    /// Corresponds to `_CM_KEY_VALUE.Data`.
446    ///
447    /// If the `DataLength` field has the `CM_KEY_VALUE_SPECIAL_SIZE` bit set,
448    /// the data is stored inline in the `Data` field itself. Otherwise,
449    /// the `Data` field contains an index into the hive's cell map where
450    /// the actual data is stored. If the data is large enough, it may be
451    /// stored as a `_CM_BIG_DATA` structure.
452    pub fn data_bytes(&self) -> Result<Vec<u8>, VmiError> {
453        let CM_KEY_VALUE = offset!(self.vmi, _CM_KEY_VALUE);
454
455        let data_length_raw = self
456            .vmi
457            .read_u32(self.va + CM_KEY_VALUE.DataLength.offset())?;
458
459        if data_length_raw & CM_KEY_VALUE_SPECIAL_SIZE != 0 {
460            let size = ((data_length_raw & !CM_KEY_VALUE_SPECIAL_SIZE) as usize).min(4);
461            let mut buffer = vec![0u8; size];
462            self.vmi
463                .read(self.va + CM_KEY_VALUE.Data.offset(), &mut buffer)?;
464            return Ok(buffer);
465        }
466
467        let data_length = data_length_raw as usize;
468        if data_length == 0 {
469            return Ok(Vec::new());
470        }
471
472        // The kernel only stores `_CM_KEY_VALUE.Data` as an `HCELL_INDEX` when
473        // `DataLength` is non-zero and the special-size flag is clear, and
474        // guarantees that index is valid.
475        let data_index = self.vmi.read_u32(self.va + CM_KEY_VALUE.Data.offset())?;
476        let hive = WindowsHive::new(self.vmi, self.hive_va);
477        let data_va = match hive.cell(WindowsHiveCellIndex::new(data_index))? {
478            Some(data_va) => data_va,
479            None => return Err(WindowsError::CorruptedStruct("CM_KEY_VALUE.Data").into()),
480        };
481
482        if hive.version()? >= HSYS_VERSION_4 && data_length_raw > CM_KEY_VALUE_BIG {
483            return self.read_big_data(&hive, data_va, data_length);
484        }
485
486        let mut buffer = vec![0u8; data_length];
487        self.vmi.read(data_va, &mut buffer)?;
488        Ok(buffer)
489    }
490
491    /// Returns the parsed data of the key value, interpreting the raw
492    /// bytes per [`value_type`].
493    ///
494    /// [`value_type`]: Self::value_type
495    pub fn data(&self) -> Result<WindowsKeyValueData, VmiError> {
496        let value_type = self.value_type()?;
497        let bytes = self.data_bytes()?;
498
499        Ok(WindowsKeyValueData::from_raw(value_type, bytes))
500    }
501
502    /// Reads the contents of a `_CM_BIG_DATA` cell.
503    ///
504    /// The caller is responsible for confirming the cell is a
505    /// `_CM_BIG_DATA` before calling. [`data_bytes`] does so via a
506    /// `(version, size)` check. A signature mismatch is logged as a
507    /// warning but parsing continues.
508    ///
509    /// [`data_bytes`]: Self::data_bytes
510    fn read_big_data(
511        &self,
512        hive: &WindowsHive<'a, Driver>,
513        big_data_va: Va,
514        total_length: usize,
515    ) -> Result<Vec<u8>, VmiError> {
516        let CM_BIG_DATA = offset!(self.vmi, _CM_BIG_DATA);
517
518        let signature = self
519            .vmi
520            .read_u16(big_data_va + CM_BIG_DATA.Signature.offset())?;
521        if signature != CM_BIG_DATA_SIGNATURE {
522            tracing::warn!(
523                signature = %Hex(signature),
524                expected = %Hex(CM_BIG_DATA_SIGNATURE),
525                hive = %hive.va(),
526                cell = %big_data_va,
527                "cell classified as _CM_BIG_DATA but signature mismatches"
528            );
529        }
530
531        let count = self
532            .vmi
533            .read_u16(big_data_va + CM_BIG_DATA.Count.offset())? as usize;
534        let list_index = self.vmi.read_u32(big_data_va + CM_BIG_DATA.List.offset())?;
535
536        // The kernel always populates `_CM_BIG_DATA.List` and each segment
537        // it points at with valid `HCELL_INDEX`es.
538        let list_va = match hive.cell(WindowsHiveCellIndex::new(list_index))? {
539            Some(list_va) => list_va,
540            None => return Err(WindowsError::CorruptedStruct("CM_BIG_DATA.List").into()),
541        };
542
543        let segment_size = CM_KEY_VALUE_BIG as usize;
544        let mut buffer = Vec::with_capacity(total_length);
545        let mut remaining = total_length;
546
547        for i in 0..count {
548            if remaining == 0 {
549                break;
550            }
551
552            let segment_index = self.vmi.read_u32(list_va + (i as u64) * HCELL_INDEX_SIZE)?;
553            let segment_va = match hive.cell(WindowsHiveCellIndex::new(segment_index))? {
554                Some(segment_va) => segment_va,
555                None => return Err(WindowsError::CorruptedStruct("CM_BIG_DATA.List[]").into()),
556            };
557
558            let chunk = remaining.min(segment_size);
559            let start = buffer.len();
560            buffer.resize(start + chunk, 0);
561
562            self.vmi
563                .read(segment_va, &mut buffer[start..start + chunk])?;
564
565            remaining -= chunk;
566        }
567
568        Ok(buffer)
569    }
570}
571
572/// Returns an iterator over the direct values of a key node.
573pub(super) fn values_iterator<'a, Driver>(
574    vmi: VmiState<'a, WindowsOs<Driver>>,
575    hive_va: Va,
576    list_index: WindowsHiveCellIndex,
577    count: u32,
578) -> Result<KeyValueIterator<'a, Driver>, VmiError>
579where
580    Driver: VmiRead,
581    Driver::Architecture: ArchAdapter<Driver>,
582{
583    if count == 0 || list_index.is_nil() {
584        return Ok(KeyValueIterator::empty(vmi, hive_va));
585    }
586
587    // The kernel only dereferences `_CM_KEY_NODE.ValueList.List` when
588    // `Count != 0`, and never plants `HCELL_NIL` in it under that guard.
589    let hive = WindowsHive::new(vmi, hive_va);
590    let list_va = match hive.cell(list_index)? {
591        Some(list_va) => list_va,
592        None => return Err(WindowsError::CorruptedStruct("CM_KEY_NODE.ValueList.List").into()),
593    };
594
595    Ok(KeyValueIterator::new(vmi, hive_va, list_va, count))
596}