Skip to main content

vmi_os_windows/comps/
user_module.rs

1use vmi_core::{
2    Pa, Registers as _, Va, VmiError, VmiState, VmiVa, driver::VmiRead, os::VmiOsUserModule,
3};
4
5use super::{LdrDataTableEntry, LdrDataTableEntryLayout, WindowsWow64Kind};
6use crate::{
7    WindowsOs,
8    arch::{ArchAdapter, StructLayout, StructLayout32, StructLayout64},
9};
10
11/// User-mode module accessor with a compile-time pointer width.
12///
13/// # Implementation Details
14///
15/// Corresponds to `_LDR_DATA_TABLE_ENTRY`.
16pub struct WindowsUserModuleBase<'a, Driver, Layout>
17where
18    Driver: VmiRead,
19    Driver::Architecture: ArchAdapter<Driver>,
20    Layout: StructLayout,
21    LdrDataTableEntryLayout: LdrDataTableEntry<Layout>,
22{
23    /// The VMI state.
24    vmi: VmiState<'a, WindowsOs<Driver>>,
25
26    /// Address of the `_LDR_DATA_TABLE_ENTRY` structure.
27    va: Va,
28
29    /// The user-mode translation root.
30    root: Pa,
31
32    _marker: std::marker::PhantomData<Layout>,
33}
34
35impl<'a, Driver, Layout> WindowsUserModuleBase<'a, Driver, Layout>
36where
37    Driver: VmiRead,
38    Driver::Architecture: ArchAdapter<Driver>,
39    Layout: StructLayout,
40    LdrDataTableEntryLayout: LdrDataTableEntry<Layout>,
41{
42    /// Creates a new user-mode module accessor.
43    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, root: Pa) -> Self {
44        Self {
45            vmi,
46            va,
47            root,
48            _marker: std::marker::PhantomData,
49        }
50    }
51
52    /// Returns the entry point of the module.
53    ///
54    /// # Implementation Details
55    ///
56    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.EntryPoint`.
57    pub fn entry_point(&self) -> Result<Va, VmiError> {
58        Layout::read_va(
59            self.vmi,
60            (
61                self.va + LdrDataTableEntryLayout::OFFSET_ENTRY_POINT,
62                self.root,
63            ),
64        )
65    }
66
67    /// Returns the full name of the module.
68    ///
69    /// # Implementation Details
70    ///
71    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.FullDllName`.
72    pub fn full_name(&self) -> Result<String, VmiError> {
73        Layout::read_unicode_string(
74            self.vmi,
75            (
76                self.va + LdrDataTableEntryLayout::OFFSET_FULL_DLL_NAME,
77                self.root,
78            ),
79        )
80    }
81
82    /// Returns the timestamp of the module.
83    ///
84    /// # Implementation Details
85    ///
86    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.TimeDateStamp`.
87    pub fn time_date_stamp(&self) -> Result<u32, VmiError> {
88        self.vmi.read_u32_in((
89            self.va + LdrDataTableEntryLayout::OFFSET_TIME_DATE_STAMP,
90            self.root,
91        ))
92    }
93
94    /// Returns the base address of the module.
95    ///
96    /// # Implementation Details
97    ///
98    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.DllBase`.
99    pub fn base_address(&self) -> Result<Va, VmiError> {
100        Layout::read_va(
101            self.vmi,
102            (
103                self.va + LdrDataTableEntryLayout::OFFSET_DLL_BASE,
104                self.root,
105            ),
106        )
107    }
108
109    /// Returns the size of the module.
110    ///
111    /// # Implementation Details
112    ///
113    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.SizeOfImage`.
114    pub fn size(&self) -> Result<u64, VmiError> {
115        Ok(self.vmi.read_u32_in((
116            self.va + LdrDataTableEntryLayout::OFFSET_SIZE_OF_IMAGE,
117            self.root,
118        ))? as u64)
119    }
120
121    /// Returns the name of the module.
122    ///
123    /// # Implementation Details
124    ///
125    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.BaseDllName`.
126    pub fn name(&self) -> Result<String, VmiError> {
127        Layout::read_unicode_string(
128            self.vmi,
129            (
130                self.va + LdrDataTableEntryLayout::OFFSET_BASE_DLL_NAME,
131                self.root,
132            ),
133        )
134    }
135}
136
137enum WindowsUserModuleWrapper<'a, Driver>
138where
139    Driver: VmiRead,
140    Driver::Architecture: ArchAdapter<Driver>,
141{
142    W32(WindowsUserModuleBase<'a, Driver, StructLayout32>),
143    W64(WindowsUserModuleBase<'a, Driver, StructLayout64>),
144}
145
146impl<'a, Driver> WindowsUserModuleWrapper<'a, Driver>
147where
148    Driver: VmiRead,
149    Driver::Architecture: ArchAdapter<Driver>,
150{
151    fn w32(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, root: Pa) -> Self {
152        Self::W32(WindowsUserModuleBase::new(vmi, va, root))
153    }
154
155    fn w64(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, root: Pa) -> Self {
156        Self::W64(WindowsUserModuleBase::new(vmi, va, root))
157    }
158
159    fn native(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, root: Pa) -> Self {
160        match vmi.registers().address_width() {
161            4 => Self::w32(vmi, va, root),
162            8 => Self::w64(vmi, va, root),
163            _ => panic!("Unsupported address width"),
164        }
165    }
166
167    fn entry_point(&self) -> Result<Va, VmiError> {
168        match self {
169            Self::W32(inner) => inner.entry_point(),
170            Self::W64(inner) => inner.entry_point(),
171        }
172    }
173
174    fn full_name(&self) -> Result<String, VmiError> {
175        match self {
176            Self::W32(inner) => inner.full_name(),
177            Self::W64(inner) => inner.full_name(),
178        }
179    }
180
181    fn time_date_stamp(&self) -> Result<u32, VmiError> {
182        match self {
183            Self::W32(inner) => inner.time_date_stamp(),
184            Self::W64(inner) => inner.time_date_stamp(),
185        }
186    }
187
188    fn base_address(&self) -> Result<Va, VmiError> {
189        match self {
190            Self::W32(inner) => inner.base_address(),
191            Self::W64(inner) => inner.base_address(),
192        }
193    }
194
195    fn size(&self) -> Result<u64, VmiError> {
196        match self {
197            Self::W32(inner) => inner.size(),
198            Self::W64(inner) => inner.size(),
199        }
200    }
201
202    fn name(&self) -> Result<String, VmiError> {
203        match self {
204            Self::W32(inner) => inner.name(),
205            Self::W64(inner) => inner.name(),
206        }
207    }
208}
209
210/// A Windows user-mode module.
211///
212/// Represents a module loaded into a process address space, as enumerated
213/// from the PEB loader data (`_LDR_DATA_TABLE_ENTRY`). Reads are performed
214/// through a user-mode translation root, so the module's PE image and
215/// metadata are accessible even on KPTI-enabled systems.
216///
217/// A module enumerated from the 32-bit loader list of a WoW64 process uses the
218/// 32-bit structure layout. The pointer width is selected at construction, so
219/// the same type serves both native and WoW64 modules.
220///
221/// # Implementation Details
222///
223/// Corresponds to `_LDR_DATA_TABLE_ENTRY`.
224pub struct WindowsUserModule<'a, Driver>
225where
226    Driver: VmiRead,
227    Driver::Architecture: ArchAdapter<Driver>,
228{
229    inner: WindowsUserModuleWrapper<'a, Driver>,
230}
231
232impl<'a, Driver> From<WindowsUserModuleBase<'a, Driver, StructLayout32>>
233    for WindowsUserModule<'a, Driver>
234where
235    Driver: VmiRead,
236    Driver::Architecture: ArchAdapter<Driver>,
237{
238    fn from(value: WindowsUserModuleBase<'a, Driver, StructLayout32>) -> Self {
239        Self {
240            inner: WindowsUserModuleWrapper::W32(value),
241        }
242    }
243}
244
245impl<'a, Driver> From<WindowsUserModuleBase<'a, Driver, StructLayout64>>
246    for WindowsUserModule<'a, Driver>
247where
248    Driver: VmiRead,
249    Driver::Architecture: ArchAdapter<Driver>,
250{
251    fn from(value: WindowsUserModuleBase<'a, Driver, StructLayout64>) -> Self {
252        Self {
253            inner: WindowsUserModuleWrapper::W64(value),
254        }
255    }
256}
257
258impl<Driver> VmiVa for WindowsUserModule<'_, Driver>
259where
260    Driver: VmiRead,
261    Driver::Architecture: ArchAdapter<Driver>,
262{
263    fn va(&self) -> Va {
264        match &self.inner {
265            WindowsUserModuleWrapper::W32(inner) => inner.va,
266            WindowsUserModuleWrapper::W64(inner) => inner.va,
267        }
268    }
269}
270
271impl<'a, Driver> WindowsUserModule<'a, Driver>
272where
273    Driver: VmiRead,
274    Driver::Architecture: ArchAdapter<Driver>,
275{
276    /// Creates a new native Windows user-mode module.
277    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, root: Pa) -> Self {
278        Self::with_kind(vmi, va, root, WindowsWow64Kind::Native)
279    }
280
281    /// Creates a new Windows user-mode module with an explicit address space
282    /// layout, selecting the native or 32-bit (WoW64) `_LDR_DATA_TABLE_ENTRY`.
283    pub fn with_kind(
284        vmi: VmiState<'a, WindowsOs<Driver>>,
285        va: Va,
286        root: Pa,
287        kind: WindowsWow64Kind,
288    ) -> Self {
289        let inner = match kind {
290            WindowsWow64Kind::Native => WindowsUserModuleWrapper::native(vmi, va, root),
291            WindowsWow64Kind::X86 => WindowsUserModuleWrapper::w32(vmi, va, root),
292        };
293
294        Self { inner }
295    }
296
297    /// Returns the entry point of the module.
298    ///
299    /// # Implementation Details
300    ///
301    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.EntryPoint`.
302    pub fn entry_point(&self) -> Result<Va, VmiError> {
303        self.inner.entry_point()
304    }
305
306    /// Returns the full name of the module.
307    ///
308    /// # Implementation Details
309    ///
310    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.FullDllName`.
311    pub fn full_name(&self) -> Result<String, VmiError> {
312        self.inner.full_name()
313    }
314
315    /// Returns the timestamp of the module.
316    ///
317    /// # Implementation Details
318    ///
319    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.TimeDateStamp`.
320    pub fn time_date_stamp(&self) -> Result<u32, VmiError> {
321        self.inner.time_date_stamp()
322    }
323}
324
325impl<'a, Driver> VmiOsUserModule<'a, Driver> for WindowsUserModule<'a, Driver>
326where
327    Driver: VmiRead,
328    Driver::Architecture: ArchAdapter<Driver>,
329{
330    type Os = WindowsOs<Driver>;
331
332    /// Returns the base address of the module.
333    ///
334    /// # Implementation Details
335    ///
336    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.DllBase`.
337    fn base_address(&self) -> Result<Va, VmiError> {
338        self.inner.base_address()
339    }
340
341    /// Returns the size of the module.
342    ///
343    /// # Implementation Details
344    ///
345    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.SizeOfImage`.
346    fn size(&self) -> Result<u64, VmiError> {
347        self.inner.size()
348    }
349
350    /// Returns the name of the module.
351    ///
352    /// # Implementation Details
353    ///
354    /// Corresponds to `_LDR_DATA_TABLE_ENTRY.BaseDllName`.
355    fn name(&self) -> Result<String, VmiError> {
356        self.inner.name()
357    }
358}