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}