Skip to main content

vmi_os_windows/comps/
session.rs

1use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead, os::ProcessObject};
2
3use super::WindowsProcess;
4use crate::{ArchAdapter, ListEntryIterator, WindowsOs, offset};
5
6//
7// The `_MM_SESSION_SPACE` structure got replaced in Windows 11 24H2
8// with `_PSP_SESSION_SPACE`, however, its fields aren't included in
9// the PDB symbols anymore.
10//
11// Fortunately, the layout of the beginning of the structure (up to
12// and including the `ProcessList` field) appears to be unchanged so far,
13// so, with heavy heart, we'll just hardcode the offsets.
14//
15// ```ignore
16// typedef struct _MM_SESSION_SPACE {
17//     /* 0x0000 */ volatile LONG ReferenceCount;
18//     /* 0x0004 */ ULONG Flags;
19//     /* 0x0008 */ ULONG SessionId;
20//     /* 0x0010 */ LIST_ENTRY ProcessList;
21//     ...
22// } MM_SESSION_SPACE, *PMM_SESSION_SPACE;
23// ```
24//
25
26struct Field {
27    offset: u64,
28}
29
30impl Field {
31    const fn offset(&self) -> u64 {
32        self.offset
33    }
34}
35
36#[expect(non_camel_case_types)]
37struct _MM_SESSION_SPACE {
38    SessionId: Field,   // ULONG
39    ProcessList: Field, // _LIST_ENTRY
40}
41
42const MM_SESSION_SPACE: _MM_SESSION_SPACE = _MM_SESSION_SPACE {
43    SessionId: Field { offset: 0x0008 },
44    ProcessList: Field { offset: 0x0010 },
45};
46
47/// A Windows session space.
48///
49/// The session space is a kernel structure that contains the
50/// session-specific data for a process.
51///
52/// Each logon session (e.g., when a user connects via Remote Desktop) gets
53/// a separate instance of `_MM_SESSION_SPACE`.
54///
55/// # Implementation Details
56///
57/// Corresponds to `_MM_SESSION_SPACE`.
58pub struct WindowsSession<'a, Driver>
59where
60    Driver: VmiRead,
61    Driver::Architecture: ArchAdapter<Driver>,
62{
63    /// The VMI state.
64    vmi: VmiState<'a, WindowsOs<Driver>>,
65
66    /// Address of the `_MM_SESSION_SPACE` structure.
67    va: Va,
68}
69
70impl<Driver> VmiVa for WindowsSession<'_, Driver>
71where
72    Driver: VmiRead,
73    Driver::Architecture: ArchAdapter<Driver>,
74{
75    fn va(&self) -> Va {
76        self.va
77    }
78}
79
80impl<'a, Driver> WindowsSession<'a, Driver>
81where
82    Driver: VmiRead,
83    Driver::Architecture: ArchAdapter<Driver>,
84{
85    /// Creates a new Windows session space.
86    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
87        Self { vmi, va }
88    }
89
90    /// Returns the session ID.
91    ///
92    /// # Implementation Details
93    ///
94    /// Corresponds to `_MM_SESSION_SPACE.SessionId`.
95    pub fn id(&self) -> Result<u32, VmiError> {
96        //let MM_SESSION_SPACE = offset!(self.vmi, _MM_SESSION_SPACE);
97
98        self.vmi
99            .read_u32(self.va + MM_SESSION_SPACE.SessionId.offset())
100    }
101
102    /// Returns the list of processes in the session.
103    ///
104    /// # Implementation Details
105    ///
106    /// Corresponds to `_MM_SESSION_SPACE.ProcessList`.
107    pub fn processes(
108        &self,
109    ) -> Result<
110        impl Iterator<Item = Result<WindowsProcess<'a, Driver>, VmiError>> + use<'a, Driver>,
111        VmiError,
112    > {
113        //let MM_SESSION_SPACE = offset!(self.vmi, _MM_SESSION_SPACE);
114        let EPROCESS = offset!(self.vmi, _EPROCESS);
115
116        let vmi = self.vmi;
117        Ok(ListEntryIterator::new(
118            vmi,
119            self.va + MM_SESSION_SPACE.ProcessList.offset(),
120            EPROCESS.SessionProcessLinks.offset(),
121        )
122        .map(move |result| result.map(|entry| WindowsProcess::new(vmi, ProcessObject(entry)))))
123    }
124}