Skip to main content

vmi_os_windows/comps/
region.rs

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