vmi_os_windows/comps/
region.rs

1use once_cell::unsync::OnceCell;
2use vmi_core::{
3    Architecture, MemoryAccess, Va, VmiDriver, VmiError, VmiState, VmiVa,
4    os::{VmiOsRegion, VmiOsRegionKind},
5};
6
7use super::{WindowsControlArea, macros::impl_offsets};
8use crate::{ArchAdapter, OffsetsExt, WindowsOs};
9
10/// A Windows memory region.
11///
12/// A memory region represents a range of virtual memory allocated
13/// within a process. It is managed by the Windows memory manager
14/// and described by a **Virtual Address Descriptor (VAD)**.
15///
16/// # Implementation Details
17///
18/// Corresponds to `_MMVAD`.
19pub struct WindowsRegion<'a, Driver>
20where
21    Driver: VmiDriver,
22    Driver::Architecture: Architecture + ArchAdapter<Driver>,
23{
24    /// The VMI state.
25    vmi: VmiState<'a, Driver, WindowsOs<Driver>>,
26
27    /// The virtual address of the `_MMVAD` structure.
28    va: Va,
29
30    /// Cached VAD flags.
31    vad_flags: OnceCell<u64>,
32}
33
34impl<Driver> VmiVa for WindowsRegion<'_, Driver>
35where
36    Driver: VmiDriver,
37    Driver::Architecture: Architecture + ArchAdapter<Driver>,
38{
39    fn va(&self) -> Va {
40        self.va
41    }
42}
43
44impl<Driver> std::fmt::Debug for WindowsRegion<'_, Driver>
45where
46    Driver: VmiDriver,
47    Driver::Architecture: Architecture + ArchAdapter<Driver>,
48{
49    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
50        let start = self.start();
51        let end = self.end();
52        let protection = self.protection();
53        //let kind = self.kind();
54
55        f.debug_struct("WindowsOsRegion")
56            .field("start", &start)
57            .field("end", &end)
58            .field("protection", &protection)
59            //.field("kind", &kind)
60            .finish()
61    }
62}
63
64impl<'a, Driver> WindowsRegion<'a, Driver>
65where
66    Driver: VmiDriver,
67    Driver::Architecture: Architecture + ArchAdapter<Driver>,
68{
69    impl_offsets!();
70
71    /// Creates a new Windows memory region.
72    pub fn new(vmi: VmiState<'a, Driver, WindowsOs<Driver>>, vad: Va) -> Self {
73        Self {
74            vmi,
75            va: vad,
76            vad_flags: OnceCell::new(),
77        }
78    }
79
80    /// Returns the starting VPN of the VAD.
81    ///
82    /// # Implementation Details
83    ///
84    /// The starting VPN is calculated from `_MMVAD_SHORT.StartingVpn` and,
85    /// if present, `_MMVAD_SHORT.StartingVpnHigh` fields.
86    pub fn starting_vpn(&self) -> Result<u64, VmiError> {
87        let offsets = self.offsets();
88        let MMVAD_SHORT = &offsets._MMVAD_SHORT;
89
90        let starting_vpn_low = self.vmi.read_field(self.va, &MMVAD_SHORT.StartingVpn)?;
91        let starting_vpn_high = match &MMVAD_SHORT.StartingVpnHigh {
92            Some(StartingVpnHigh) => self.vmi.read_field(self.va, StartingVpnHigh)?,
93            None => 0,
94        };
95
96        Ok((starting_vpn_high << 32) | starting_vpn_low)
97    }
98
99    /// Returns the ending VPN of the VAD.
100    ///
101    /// # Implementation Details
102    ///
103    /// The ending VPN is calculated from `_MMVAD_SHORT.EndingVpn` and,
104    /// if present, `_MMVAD_SHORT.EndingVpnHigh` fields.
105    pub fn ending_vpn(&self) -> Result<u64, VmiError> {
106        let offsets = self.offsets();
107        let MMVAD_SHORT = &offsets._MMVAD_SHORT;
108
109        let ending_vpn_low = self.vmi.read_field(self.va, &MMVAD_SHORT.EndingVpn)?;
110        let ending_vpn_high = match &MMVAD_SHORT.EndingVpnHigh {
111            Some(EndingVpnHigh) => self.vmi.read_field(self.va, EndingVpnHigh)?,
112            None => 0,
113        };
114
115        Ok((ending_vpn_high << 32) | ending_vpn_low)
116    }
117
118    /// Returns the VAD flags.
119    ///
120    /// # Notes
121    ///
122    /// This value is cached after the first read.
123    ///
124    /// # Implementation Details
125    ///
126    /// Corresponds to `_MMVAD_SHORT.VadFlags`.
127    pub fn vad_flags(&self) -> Result<u64, VmiError> {
128        self.vad_flags
129            .get_or_try_init(|| {
130                let offsets = self.offsets();
131                let MMVAD_SHORT = &offsets._MMVAD_SHORT;
132
133                self.vmi.read_field(self.va, &MMVAD_SHORT.VadFlags)
134            })
135            .copied()
136    }
137
138    /// Returns the VAD type.
139    ///
140    /// # Implementation Details
141    ///
142    /// Corresponds to `_MMVAD_SHORT.VadFlags.VadType`.
143    pub fn vad_type(&self) -> Result<u8, VmiError> {
144        let offsets = self.offsets();
145        let MMVAD_FLAGS = &offsets._MMVAD_FLAGS;
146
147        let vad_flags = self.vad_flags()?;
148        Ok(MMVAD_FLAGS.VadType.extract(vad_flags) as u8)
149    }
150
151    /// Returns the memory protection of the VAD.
152    ///
153    /// # Implementation Details
154    ///
155    /// Calculated from `_MMVAD_SHORT.VadFlags.Protection` field.
156    pub fn vad_protection(&self) -> Result<u8, VmiError> {
157        let offsets = self.offsets();
158        let MMVAD_FLAGS = &offsets._MMVAD_FLAGS;
159
160        let flags = self.vad_flags()?;
161        let protection = MMVAD_FLAGS.Protection.extract(flags) as u8;
162
163        Ok(protection)
164    }
165
166    /// Checks if the VAD represents private memory.
167    ///
168    /// # Implementation Details
169    ///
170    /// Corresponds to `_MMVAD_SHORT.VadFlags.PrivateMemory`.
171    pub fn private_memory(&self) -> Result<bool, VmiError> {
172        let offsets = self.offsets();
173        let MMVAD_FLAGS = &offsets._MMVAD_FLAGS;
174
175        let vad_flags = self.vad_flags()?;
176        Ok(MMVAD_FLAGS.PrivateMemory.extract(vad_flags) != 0)
177    }
178
179    /// Checks if the memory of the VAD is committed.
180    ///
181    /// # Implementation Details
182    ///
183    /// Corresponds to `_MMVAD_SHORT.VadFlags.MemCommit` (Windows 7) or
184    /// `_MMVAD_SHORT.VadFlags1.MemCommit` (Windows 8+).
185    pub fn mem_commit(&self) -> Result<bool, VmiError> {
186        let offsets = self.offsets();
187        let MMVAD_FLAGS = &offsets._MMVAD_FLAGS;
188        let MMVAD_SHORT = &offsets._MMVAD_SHORT;
189
190        let vad_flags = self.vad_flags()?;
191
192        // If `MMVAD_FLAGS.MemCommit` is present (Windows 7), then we fetch the
193        // value from it. Otherwise, we load the `VadFlags1` field from the VAD
194        // and fetch it from there.
195        let mem_commit = match MMVAD_FLAGS.MemCommit {
196            // `MemCommit` is present in `MMVAD_FLAGS`
197            Some(MemCommit) => MemCommit.extract(vad_flags) != 0,
198            None => match (&self.offsets().ext(), MMVAD_SHORT.VadFlags1) {
199                // `MemCommit` is present in `MMVAD_FLAGS1`
200                (Some(OffsetsExt::V2(offsets)), Some(VadFlags1)) => {
201                    let MMVAD_FLAGS1 = &offsets._MMVAD_FLAGS1;
202                    let vad_flags1 = self.vmi.read_field(self.va, &VadFlags1)?;
203                    MMVAD_FLAGS1.MemCommit.extract(vad_flags1) != 0
204                }
205                _ => {
206                    panic!("Failed to read MemCommit from VAD");
207                }
208            },
209        };
210
211        Ok(mem_commit)
212    }
213
214    /// Returns the left child of the VAD.
215    ///
216    /// # Implementation Details
217    ///
218    /// Corresponds to `_MMVAD_SHORT.Left`.
219    pub fn left_child(&self) -> Result<Option<WindowsRegion<'a, Driver>>, VmiError> {
220        let offsets = self.offsets();
221        let MMVAD_SHORT = &offsets._MMVAD_SHORT;
222
223        let left_child = self.vmi.read_field(self.va, &MMVAD_SHORT.Left)?;
224
225        if left_child == 0 {
226            return Ok(None);
227        }
228
229        Ok(Some(WindowsRegion::new(self.vmi, Va(left_child))))
230    }
231
232    /// Returns the right child of the VAD.
233    ///
234    /// # Implementation Details
235    ///
236    /// Corresponds to `_MMVAD_SHORT.Right`.
237    pub fn right_child(&self) -> Result<Option<WindowsRegion<'a, Driver>>, VmiError> {
238        let offsets = self.offsets();
239        let MMVAD_SHORT = &offsets._MMVAD_SHORT;
240
241        let right_child = self.vmi.read_field(self.va, &MMVAD_SHORT.Right)?;
242
243        if right_child == 0 {
244            return Ok(None);
245        }
246
247        Ok(Some(WindowsRegion::new(self.vmi, Va(right_child))))
248    }
249}
250
251impl<'a, Driver> VmiOsRegion<'a, Driver> for WindowsRegion<'a, Driver>
252where
253    Driver: VmiDriver,
254    Driver::Architecture: Architecture + ArchAdapter<Driver>,
255{
256    type Os = WindowsOs<Driver>;
257
258    /// Returns the starting virtual address of the memory region.
259    ///
260    /// # Implementation Details
261    ///
262    /// The starting address is calculated from `_MMVAD_SHORT.StartingVpn` and,
263    /// if present, `_MMVAD_SHORT.StartingVpnHigh` fields.
264    fn start(&self) -> Result<Va, VmiError> {
265        Ok(Va(self.starting_vpn()? << 12))
266    }
267
268    /// Returns the ending virtual address of the memory region.
269    ///
270    /// # Implementation Details
271    ///
272    /// The ending address is calculated from `_MMVAD_SHORT.EndingVpn` and,
273    /// if present, `_MMVAD_SHORT.EndingVpnHigh` fields.
274    fn end(&self) -> Result<Va, VmiError> {
275        Ok(Va((self.ending_vpn()? + 1) << 12))
276    }
277
278    /// Returns the memory protection of the memory region.
279    ///
280    /// # Implementation Details
281    ///
282    /// Calculated from `_MMVAD_SHORT.VadFlags.Protection` field.
283    fn protection(&self) -> Result<MemoryAccess, VmiError> {
284        const MM_ZERO_ACCESS: u8 = 0; // this value is not used.
285        const MM_READONLY: u8 = 1;
286        const MM_EXECUTE: u8 = 2;
287        const MM_EXECUTE_READ: u8 = 3;
288        const MM_READWRITE: u8 = 4; // bit 2 is set if this is writable.
289        const MM_WRITECOPY: u8 = 5;
290        const MM_EXECUTE_READWRITE: u8 = 6;
291        const MM_EXECUTE_WRITECOPY: u8 = 7;
292
293        match self.vad_protection()? {
294            MM_ZERO_ACCESS => Ok(MemoryAccess::default()),
295            MM_READONLY => Ok(MemoryAccess::R),
296            MM_EXECUTE => Ok(MemoryAccess::X),
297            MM_EXECUTE_READ => Ok(MemoryAccess::RX),
298            MM_READWRITE => Ok(MemoryAccess::RW),
299            MM_WRITECOPY => Ok(MemoryAccess::RW), // REVIEW: is this correct?
300            MM_EXECUTE_READWRITE => Ok(MemoryAccess::RWX),
301            MM_EXECUTE_WRITECOPY => Ok(MemoryAccess::RWX), // REVIEW: is this correct?
302            _ => Ok(MemoryAccess::default()),
303        }
304    }
305
306    /// Returns the memory region's kind.
307    fn kind(&self) -> Result<VmiOsRegionKind<'a, Driver, Self::Os>, VmiError> {
308        let offsets = self.offsets();
309        let MMVAD = &offsets._MMVAD;
310        let SUBSECTION = &offsets._SUBSECTION;
311
312        /*
313        const VadImageMap: u8 = 2;
314
315        let vad_type = self.vad_type()?;
316        if vad_type != VadImageMap {
317            return Ok(VmiOsRegionKind::Private);
318        }
319        */
320
321        if self.private_memory()? {
322            return Ok(VmiOsRegionKind::Private);
323        }
324
325        let subsection = Va(self.vmi.read_field(self.va, &MMVAD.Subsection)?);
326        let control_area = Va(self.vmi.read_field(subsection, &SUBSECTION.ControlArea)?);
327
328        let region_kind = WindowsControlArea::new(self.vmi, control_area);
329
330        const VadImageMap: u8 = 2;
331        let vad_type = self.vad_type()?;
332        if vad_type == VadImageMap {
333            Ok(VmiOsRegionKind::MappedImage(region_kind))
334        }
335        else {
336            Ok(VmiOsRegionKind::MappedData(region_kind))
337        }
338    }
339}