vmi_os_windows/comps/object/
section.rs

1use once_cell::unsync::OnceCell;
2use vmi_core::{Architecture, Va, VmiDriver, VmiError, VmiState, VmiVa};
3
4use super::{
5    super::{
6        WindowsControlArea,
7        macros::{impl_offsets, impl_offsets_ext_v1, impl_offsets_ext_v2},
8    },
9    WindowsFileObject, WindowsObject,
10};
11use crate::{ArchAdapter, OffsetsExt, WindowsOs};
12
13/// A Windows section object.
14///
15/// A section object in Windows is a kernel structure used for memory mapping
16/// and shared memory management. It allows multiple processes to share
17/// memory regions or map files into their address space.
18///
19/// # Implementation Details
20///
21/// Corresponds to `_SECTION_OBJECT` or `_SECTION`.
22pub struct WindowsSectionObject<'a, Driver>
23where
24    Driver: VmiDriver,
25    Driver::Architecture: Architecture + ArchAdapter<Driver>,
26{
27    inner: Inner<'a, Driver>,
28}
29
30impl<'a, Driver> From<WindowsSectionObject<'a, Driver>> for WindowsObject<'a, Driver>
31where
32    Driver: VmiDriver,
33    Driver::Architecture: Architecture + ArchAdapter<Driver>,
34{
35    fn from(value: WindowsSectionObject<'a, Driver>) -> Self {
36        let (vmi, va) = match value.inner {
37            Inner::V1(inner) => (inner.vmi, inner.va),
38            Inner::V2(inner) => (inner.vmi, inner.va),
39        };
40
41        Self::new(vmi, va)
42    }
43}
44
45impl<Driver> VmiVa for WindowsSectionObject<'_, Driver>
46where
47    Driver: VmiDriver,
48    Driver::Architecture: Architecture + ArchAdapter<Driver>,
49{
50    fn va(&self) -> Va {
51        match &self.inner {
52            Inner::V1(inner) => inner.va,
53            Inner::V2(inner) => inner.va,
54        }
55    }
56}
57
58impl<'a, Driver> WindowsSectionObject<'a, Driver>
59where
60    Driver: VmiDriver,
61    Driver::Architecture: Architecture + ArchAdapter<Driver>,
62{
63    /// Creates a new Windows section object.
64    pub fn new(vmi: VmiState<'a, Driver, WindowsOs<Driver>>, va: Va) -> Self {
65        let inner = match vmi.underlying_os().offsets.ext() {
66            Some(OffsetsExt::V1(_)) => Inner::V1(WindowsSectionObjectV1::new(vmi, va)),
67            Some(OffsetsExt::V2(_)) => Inner::V2(WindowsSectionObjectV2::new(vmi, va)),
68            None => unimplemented!(),
69        };
70
71        Self { inner }
72    }
73
74    /// Returns the starting address of the section.
75    ///
76    /// # Implementation Details
77    ///
78    /// Corresponds to `_SECTION_OBJECT.StartingVa` or `_SECTION.StartingVpn`
79    /// shifted left by 12 bits.
80    pub fn start(&self) -> Result<Va, VmiError> {
81        match &self.inner {
82            Inner::V1(inner) => inner.start(),
83            Inner::V2(inner) => inner.start(),
84        }
85    }
86
87    /// Returns the ending address of the section (exclusive).
88    ///
89    /// # Implementation Details
90    ///
91    /// Corresponds to `_SECTION_OBJECT.EndingVa` or `_SECTION.EndingVpn`
92    /// incremented by 1 and shifted left by 12 bits.
93    pub fn end(&self) -> Result<Va, VmiError> {
94        match &self.inner {
95            Inner::V1(inner) => inner.end(),
96            Inner::V2(inner) => inner.end(),
97        }
98    }
99
100    /// Returns the size of the section.
101    ///
102    /// # Implementation Details
103    ///
104    /// Corresponds to `_SECTION_OBJECT.SizeOfSegment` or `_SECTION.SizeOfSection`.
105    pub fn size(&self) -> Result<u64, VmiError> {
106        match &self.inner {
107            Inner::V1(inner) => inner.size(),
108            Inner::V2(inner) => inner.size(),
109        }
110    }
111
112    /// Returns the flags of the section.
113    ///
114    /// # Implementation Details
115    ///
116    /// Corresponds to `_SECTION.Flags` or `_SEGMENT_OBJECT.MmSectionFlags`.
117    pub fn flags(&self) -> Result<u64, VmiError> {
118        match &self.inner {
119            Inner::V1(inner) => inner.flags(),
120            Inner::V2(inner) => inner.flags(),
121        }
122    }
123
124    /// Returns the file object of the section.
125    ///
126    /// # Implementation Details
127    ///
128    /// Corresponds to `_SECTION.ControlArea.FilePointer` or
129    /// `_SEGMENT_OBJECT.ControlArea.FilePointer`.
130    pub fn file_object(&self) -> Result<Option<WindowsFileObject<'a, Driver>>, VmiError> {
131        match &self.inner {
132            Inner::V1(inner) => inner.file_object(),
133            Inner::V2(inner) => inner.file_object(),
134        }
135    }
136
137    /// Constructs the full path of the file object associated with the section.
138    ///
139    /// Shortcut for [`self.file_object()?.full_path()`].
140    ///
141    /// [`self.file_object()?.full_path()`]: WindowsFileObject::full_path
142    pub fn full_path(&self) -> Result<Option<String>, VmiError> {
143        match self.file_object() {
144            Ok(Some(file_object)) => Ok(Some(file_object.full_path()?)),
145            _ => Ok(None),
146        }
147    }
148}
149
150/// Inner representation of a Windows section object.
151enum Inner<'a, Driver>
152where
153    Driver: VmiDriver,
154    Driver::Architecture: Architecture + ArchAdapter<Driver>,
155{
156    V1(WindowsSectionObjectV1<'a, Driver>),
157    V2(WindowsSectionObjectV2<'a, Driver>),
158}
159
160/// A Windows section object.
161struct WindowsSectionObjectV1<'a, Driver>
162where
163    Driver: VmiDriver,
164    Driver::Architecture: Architecture + ArchAdapter<Driver>,
165{
166    /// The VMI state.
167    vmi: VmiState<'a, Driver, WindowsOs<Driver>>,
168
169    /// The virtual address of the `_SECTION_OBJECT` structure.
170    va: Va,
171
172    /// Cached virtual address of the `_SEGMENT_OBJECT` structure.
173    segment: OnceCell<Va>,
174}
175
176impl<'a, Driver> WindowsSectionObjectV1<'a, Driver>
177where
178    Driver: VmiDriver,
179    Driver::Architecture: Architecture + ArchAdapter<Driver>,
180{
181    impl_offsets!();
182    impl_offsets_ext_v1!();
183
184    fn new(vmi: VmiState<'a, Driver, WindowsOs<Driver>>, va: Va) -> Self {
185        Self {
186            vmi,
187            va,
188            segment: OnceCell::new(),
189        }
190    }
191
192    fn start(&self) -> Result<Va, VmiError> {
193        let offsets_ext = self.offsets_ext();
194        let SECTION_OBJECT = &offsets_ext._SECTION_OBJECT;
195
196        let starting_vpn = self.vmi.read_field(self.va, &SECTION_OBJECT.StartingVa)?;
197
198        Ok(Va(starting_vpn << 12))
199    }
200
201    fn end(&self) -> Result<Va, VmiError> {
202        let offsets_ext = self.offsets_ext();
203        let SECTION_OBJECT = &offsets_ext._SECTION_OBJECT;
204
205        let ending_vpn = self.vmi.read_field(self.va, &SECTION_OBJECT.EndingVa)?;
206
207        Ok(Va((ending_vpn + 1) << 12))
208    }
209
210    fn size(&self) -> Result<u64, VmiError> {
211        let offsets_ext = self.offsets_ext();
212        let SEGMENT_OBJECT = &offsets_ext._SEGMENT_OBJECT;
213
214        let size = self
215            .vmi
216            .read_field(self.segment()?, &SEGMENT_OBJECT.SizeOfSegment)?;
217
218        Ok(size)
219    }
220
221    fn flags(&self) -> Result<u64, VmiError> {
222        let offsets_ext = self.offsets_ext();
223        let SEGMENT_OBJECT = &offsets_ext._SEGMENT_OBJECT;
224
225        let flags = Va(self
226            .vmi
227            .read_field(self.segment()?, &SEGMENT_OBJECT.MmSectionFlags)?);
228
229        Ok(self.vmi.read_u32(flags)? as u64)
230    }
231
232    fn file_object(&self) -> Result<Option<WindowsFileObject<'a, Driver>>, VmiError> {
233        let offsets = self.offsets();
234        let offsets_ext = self.offsets_ext();
235        let SEGMENT_OBJECT = &offsets_ext._SEGMENT_OBJECT;
236        let MMSECTION_FLAGS = &offsets._MMSECTION_FLAGS;
237
238        let flags = self.flags()?;
239        let file = MMSECTION_FLAGS.File.extract(flags) != 0;
240
241        if !file {
242            return Ok(None);
243        }
244
245        let control_area = Va(self
246            .vmi
247            .read_field(self.segment()?, &SEGMENT_OBJECT.ControlArea)?);
248
249        WindowsControlArea::new(self.vmi, control_area).file_object()
250    }
251
252    fn segment(&self) -> Result<Va, VmiError> {
253        self.segment
254            .get_or_try_init(|| {
255                let offsets_ext = self.offsets_ext();
256                let SECTION_OBJECT = &offsets_ext._SECTION_OBJECT;
257
258                let segment = self.vmi.read_field(self.va, &SECTION_OBJECT.Segment)?;
259
260                Ok(Va(segment))
261            })
262            .copied()
263    }
264}
265
266struct WindowsSectionObjectV2<'a, Driver>
267where
268    Driver: VmiDriver,
269    Driver::Architecture: Architecture + ArchAdapter<Driver>,
270{
271    /// The VMI state.
272    vmi: VmiState<'a, Driver, WindowsOs<Driver>>,
273
274    /// The virtual address of the `_SECTION` structure.
275    va: Va,
276}
277
278impl<'a, Driver> WindowsSectionObjectV2<'a, Driver>
279where
280    Driver: VmiDriver,
281    Driver::Architecture: Architecture + ArchAdapter<Driver>,
282{
283    impl_offsets!();
284    impl_offsets_ext_v2!();
285
286    fn new(vmi: VmiState<'a, Driver, WindowsOs<Driver>>, va: Va) -> Self {
287        Self { vmi, va }
288    }
289
290    fn start(&self) -> Result<Va, VmiError> {
291        let offsets_ext = self.offsets_ext();
292        let SECTION = &offsets_ext._SECTION;
293
294        let starting_vpn = self.vmi.read_field(self.va, &SECTION.StartingVpn)?;
295
296        Ok(Va(starting_vpn << 12))
297    }
298
299    fn end(&self) -> Result<Va, VmiError> {
300        let offsets_ext = self.offsets_ext();
301        let SECTION = &offsets_ext._SECTION;
302
303        let ending_vpn = self.vmi.read_field(self.va, &SECTION.EndingVpn)?;
304
305        Ok(Va((ending_vpn + 1) << 12))
306    }
307
308    fn size(&self) -> Result<u64, VmiError> {
309        let offsets_ext = self.offsets_ext();
310        let SECTION = &offsets_ext._SECTION;
311
312        self.vmi.read_field(self.va, &SECTION.SizeOfSection)
313    }
314
315    fn flags(&self) -> Result<u64, VmiError> {
316        let offsets_ext = self.offsets_ext();
317        let SECTION = &offsets_ext._SECTION;
318
319        self.vmi.read_field(self.va, &SECTION.Flags)
320    }
321
322    fn file_object(&self) -> Result<Option<WindowsFileObject<'a, Driver>>, VmiError> {
323        let offsets = self.offsets();
324        let offsets_ext = self.offsets_ext();
325        let MMSECTION_FLAGS = &offsets._MMSECTION_FLAGS;
326        let SECTION = &offsets_ext._SECTION;
327
328        let flags = self.flags()?;
329        let file = MMSECTION_FLAGS.File.extract(flags) != 0;
330
331        if !file {
332            return Ok(None);
333        }
334
335        //
336        // We have to distinguish between FileObject and ControlArea.
337        // Here's an excerpt from _SECTION:
338        //
339        //     union {
340        //       union {
341        //         PCONTROL_AREA ControlArea;
342        //         PFILE_OBJECT FileObject;
343        //         struct {
344        //           ULONG_PTR RemoteImageFileObject : 1;
345        //           ULONG_PTR RemoteDataFileObject : 1;
346        //         };
347        //       };
348        //     };
349        //
350        // Based on information from Geoff Chappell's website, we can determine whether
351        // ControlArea is in fact FileObject by checking the lowest 2 bits of the
352        // pointer.
353        //
354        // ref: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/mi/section.htm
355        //
356
357        let control_area = Va(self.vmi.read_field(self.va, &SECTION.ControlArea)?);
358
359        if control_area.0 & 0x3 != 0 {
360            let file_object = control_area;
361            return Ok(Some(WindowsFileObject::new(self.vmi, file_object)));
362        }
363
364        WindowsControlArea::new(self.vmi, control_area).file_object()
365    }
366}