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}