Skip to main content

vmi_os_windows/comps/object/
section.rs

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