Skip to main content

vmi_core/os/
struct_reader.rs

1use isr_macros::Field;
2
3use super::VmiOs;
4use crate::{AccessContext, Registers, Va, VmiCore, VmiError, VmiRead, VmiState};
5
6/// A handler for reading structured data from guest memory.
7///
8/// Provides safe access to structured data (like C structs) read from guest memory,
9/// with proper bounds checking and endianness handling. It reads the data as a byte buffer
10/// and provides methods to safely access fields at specific offsets and sizes.
11///
12/// # Examples
13///
14/// ```no_run
15/// # use isr_macros::{offsets, Field};
16/// # use vmi_core::{Va, VmiError, VmiRead, VmiState};
17/// # use vmi_core::os::{StructReader, VmiOs};
18///
19/// offsets! {
20///     #[derive(Debug)]
21///     pub struct Offsets {
22///         struct _UNICODE_STRING {
23///             Length: Field,          // USHORT
24///             MaximumLength: Field,   // USHORT
25///             Buffer: Field,          // PWSTR
26///         }
27///     }
28/// }
29///
30/// # fn example<Os>(vmi: &VmiState<Os>, va: Va) -> Result<(), VmiError>
31/// # where
32/// #     Os: VmiOs,
33/// #     Os::Driver: VmiRead
34/// # {
35/// # let profile = unimplemented!();
36///
37/// let offsets = Offsets::new(profile)?;
38/// let UNICODE_STRING = &offsets._UNICODE_STRING;
39///
40/// // Read the structure from memory.
41/// let us = StructReader::new(vmi, va, UNICODE_STRING.effective_len())?;
42///
43/// // Access the field values.
44/// let length = us.read(UNICODE_STRING.Length)?;
45/// let buffer = us.read(UNICODE_STRING.Buffer)?;
46///
47/// # Ok(())
48/// # }
49/// ```
50pub struct StructReader(Vec<u8>);
51
52impl StructReader {
53    /// Creates a new structure reader.
54    ///
55    /// Reads `len` bytes from the guest memory at the specified address into
56    /// a new `StructReader` instance. The data can then be accessed using the
57    /// [`read`] method with appropriate field descriptors.
58    ///
59    /// [`read`]: Self::read
60    pub fn new<Os>(vmi: &VmiState<Os>, va: Va, len: usize) -> Result<Self, VmiError>
61    where
62        Os: VmiOs,
63        Os::Driver: VmiRead,
64    {
65        Self::new_in(vmi, vmi.registers().address_context(va), len)
66    }
67
68    /// Creates a new structure reader.
69    ///
70    /// Reads `len` bytes from the guest memory at the specified address into
71    /// a new `StructReader` instance. The data can then be accessed using the
72    /// [`read`] method with appropriate field descriptors.
73    ///
74    /// [`read`]: Self::read
75    pub fn new_in<Driver>(
76        vmi: &VmiCore<Driver>,
77        ctx: impl Into<AccessContext>,
78        len: usize,
79    ) -> Result<Self, VmiError>
80    where
81        Driver: VmiRead,
82    {
83        let mut buffer = vec![0u8; len];
84        vmi.read(ctx.into(), &mut buffer)?;
85        Ok(Self(buffer))
86    }
87
88    /// Reads a field value from the data buffer.
89    ///
90    /// Extracts a value from the buffer using the provided field descriptor,
91    /// which specifies the offset and size of the field.
92    /// The value is interpreted as a little-endian integer of the appropriate
93    /// size and returned as a [`u64`].
94    ///
95    /// # Endianness
96    ///
97    /// Values are always read as little-endian integers. The returned [`u64`]
98    /// will contain the zero-extended value.
99    pub fn read(&self, field: Field) -> Result<u64, VmiError> {
100        let offset = field.offset() as usize;
101        let size = field.size() as usize;
102
103        let offset_end = match offset.checked_add(size) {
104            Some(offset_end) => offset_end,
105            None => return Err(VmiError::OutOfBounds),
106        };
107
108        if offset_end > self.0.len() {
109            return Err(VmiError::OutOfBounds);
110        }
111
112        let data = &self.0[offset..offset_end];
113
114        match size {
115            1 => Ok(data[0] as u64),
116            2 => Ok(u16::from_le_bytes([data[0], data[1]]) as u64),
117            4 => Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as u64),
118            8 => Ok(u64::from_le_bytes([
119                data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
120            ])),
121            _ => Err(VmiError::OutOfBounds),
122        }
123    }
124}