vmi_core/os/
struct_reader.rs

1use isr_macros::Field;
2
3use super::VmiOs;
4use crate::{AccessContext, Registers, Va, VmiCore, VmiDriver, VmiError, 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::{AccessContext, VmiCore, VmiDriver, VmiError};
17/// # use vmi_core::os::StructReader;
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<Driver: VmiDriver>(
31/// #     vmi: &VmiCore<Driver>,
32/// #     ctx: impl Into<AccessContext>,
33/// # ) -> Result<(), VmiError> {
34/// # let profile = unimplemented!();
35///
36/// let offsets = Offsets::new(profile)?;
37/// let UNICODE_STRING = &offsets._UNICODE_STRING;
38///
39/// // Read the structure from memory.
40/// let us = StructReader::new(vmi, ctx, UNICODE_STRING.effective_len())?;
41///
42/// // Access the field values.
43/// let length = us.read(UNICODE_STRING.Length)?;
44/// let buffer = us.read(UNICODE_STRING.Buffer)?;
45///
46/// # Ok(())
47/// # }
48/// ```
49pub struct StructReader(Vec<u8>);
50
51impl StructReader {
52    /// Creates a new structure reader.
53    ///
54    /// Reads `len` bytes from the guest memory at the specified address into
55    /// a new `StructReader` instance. The data can then be accessed using the
56    /// [`read`] method with appropriate field descriptors.
57    ///
58    /// [`read`]: Self::read
59    pub fn new<Driver, Os>(vmi: &VmiState<Driver, Os>, va: Va, len: usize) -> Result<Self, VmiError>
60    where
61        Driver: VmiDriver,
62        Os: VmiOs<Driver>,
63    {
64        Self::new_in(vmi, vmi.registers().address_context(va), len)
65    }
66
67    /// Creates a new structure reader.
68    ///
69    /// Reads `len` bytes from the guest memory at the specified address into
70    /// a new `StructReader` instance. The data can then be accessed using the
71    /// [`read`] method with appropriate field descriptors.
72    ///
73    /// [`read`]: Self::read
74    pub fn new_in<Driver>(
75        vmi: &VmiCore<Driver>,
76        ctx: impl Into<AccessContext>,
77        len: usize,
78    ) -> Result<Self, VmiError>
79    where
80        Driver: VmiDriver,
81    {
82        let mut buffer = vec![0u8; len];
83        vmi.read(ctx.into(), &mut buffer)?;
84        Ok(Self(buffer))
85    }
86
87    /// Reads a field value from the data buffer.
88    ///
89    /// Extracts a value from the buffer using the provided field descriptor,
90    /// which specifies the offset and size of the field.
91    /// The value is interpreted as a little-endian integer of the appropriate
92    /// size and returned as a [`u64`].
93    ///
94    /// # Endianness
95    ///
96    /// Values are always read as little-endian integers. The returned [`u64`]
97    /// will contain the zero-extended value.
98    pub fn read(&self, field: Field) -> Result<u64, VmiError> {
99        let offset = field.offset() as usize;
100        let size = field.size() as usize;
101
102        let offset_end = match offset.checked_add(size) {
103            Some(offset_end) => offset_end,
104            None => return Err(VmiError::OutOfBounds),
105        };
106
107        if offset_end > self.0.len() {
108            return Err(VmiError::OutOfBounds);
109        }
110
111        let data = &self.0[offset..offset_end];
112
113        match size {
114            1 => Ok(data[0] as u64),
115            2 => Ok(u16::from_le_bytes([data[0], data[1]]) as u64),
116            4 => Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as u64),
117            8 => Ok(u64::from_le_bytes([
118                data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
119            ])),
120            _ => Err(VmiError::OutOfBounds),
121        }
122    }
123}