vmi_os_windows/comps/
handle_table.rs

1use once_cell::unsync::OnceCell;
2use vmi_core::{Architecture, Va, VmiDriver, VmiError, VmiState, VmiVa};
3
4use super::{WindowsHandleTableEntry, macros::impl_offsets};
5use crate::{ArchAdapter, HandleTableEntryIterator, WindowsOs};
6
7/// A Windows handle table.
8///
9/// A handle table in Windows tracks handles to kernel objects
10/// for a specific process, allowing access control and management.
11///
12/// # Implementation Details
13///
14/// Corresponds to `_HANDLE_TABLE`.
15pub struct WindowsHandleTable<'a, Driver>
16where
17    Driver: VmiDriver,
18    Driver::Architecture: Architecture + ArchAdapter<Driver>,
19{
20    /// The VMI state.
21    vmi: VmiState<'a, Driver, WindowsOs<Driver>>,
22
23    /// The virtual address of the handle table.
24    va: Va,
25
26    /// Corresponds to `_HANDLE_TABLE.TableCode`.
27    table_code: OnceCell<u64>,
28
29    /// Corresponds to `_HANDLE_TABLE.NextHandleNeedingPool`.
30    next_handle_needing_pool: OnceCell<u64>,
31}
32
33impl<Driver> VmiVa for WindowsHandleTable<'_, Driver>
34where
35    Driver: VmiDriver,
36    Driver::Architecture: Architecture + ArchAdapter<Driver>,
37{
38    fn va(&self) -> Va {
39        self.va
40    }
41}
42
43impl<'a, Driver> WindowsHandleTable<'a, Driver>
44where
45    Driver: VmiDriver,
46    Driver::Architecture: Architecture + ArchAdapter<Driver>,
47{
48    impl_offsets!();
49
50    /// Creates a new Windows module object.
51    pub fn new(vmi: VmiState<'a, Driver, WindowsOs<Driver>>, va: Va) -> Self {
52        Self {
53            vmi,
54            va,
55            table_code: OnceCell::new(),
56            next_handle_needing_pool: OnceCell::new(),
57        }
58    }
59
60    /// Returns the table code of the handle table.
61    ///
62    /// # Notes
63    ///
64    /// This value is cached after the first read.
65    ///
66    /// # Implementation Details
67    ///
68    /// Corresponds to `_HANDLE_TABLE.TableCode`.
69    pub fn table_code(&self) -> Result<u64, VmiError> {
70        self.table_code
71            .get_or_try_init(|| {
72                let offsets = self.offsets();
73                let HANDLE_TABLE = &offsets._HANDLE_TABLE;
74
75                self.vmi.read_field(self.va, &HANDLE_TABLE.TableCode)
76            })
77            .copied()
78    }
79
80    /// Returns the next handle needing pool.
81    ///
82    /// This value tracks the next handle slot that requires additional pool
83    /// allocation.
84    ///
85    /// # Notes
86    ///
87    /// This value is cached after the first read.
88    ///
89    /// # Implementation Details
90    ///
91    /// Corresponds to `_HANDLE_TABLE.NextHandleNeedingPool`.
92    pub fn next_handle_needing_pool(&self) -> Result<u64, VmiError> {
93        self.next_handle_needing_pool
94            .get_or_try_init(|| {
95                let offsets = self.offsets();
96                let HANDLE_TABLE = &offsets._HANDLE_TABLE;
97
98                self.vmi
99                    .read_field(self.va, &HANDLE_TABLE.NextHandleNeedingPool)
100            })
101            .copied()
102    }
103
104    /// Iterates over all handle table entries.
105    ///
106    /// Returns an iterator over all handle table entries that have a valid
107    /// object pointer. The iterator yields a tuple containing the handle
108    /// value and the handle table entry.
109    ///
110    /// # Implementation Details
111    ///
112    /// The functionality is similar to the Windows kernel's internal
113    /// `ExpSnapShotHandleTables()` function.
114    pub fn iter(&'a self) -> Result<HandleTableEntryIterator<'a, Driver>, VmiError> {
115        Ok(HandleTableEntryIterator::new(self))
116    }
117
118    /// Performs a lookup in the handle table to find the address of a handle
119    /// table entry.
120    ///
121    /// Implements the multi-level handle table lookup algorithm used by
122    /// Windows. Returns the virtual address of the handle table entry.
123    ///
124    /// # Implementation Details
125    ///
126    /// The functionality is similar to the Windows kernel's internal
127    /// `ExpLookupHandleTableEntry()` function.
128    pub fn lookup(
129        &self,
130        handle: u64,
131    ) -> Result<Option<WindowsHandleTableEntry<'a, Driver>>, VmiError> {
132        const SIZEOF_POINTER: u64 = 8;
133        const SIZEOF_HANDLE_TABLE_ENTRY: u64 = 16;
134
135        const LOWLEVEL_COUNT: u64 = 256; // (TABLE_PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
136        const MIDLEVEL_COUNT: u64 = 512; // (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))
137
138        const LEVEL_CODE_MASK: u64 = 3;
139        const HANDLE_VALUE_INC: u64 = 4;
140
141        // The 2 least significant bits of a handle are available to the
142        // application and are ignored by the system.
143        let mut index = handle & !0b11;
144
145        // See if this can be a valid handle given the table levels.
146        if index >= self.next_handle_needing_pool()? {
147            return Ok(None);
148        }
149
150        let table_code = self.table_code()?;
151        let level = table_code & LEVEL_CODE_MASK;
152        let table = Va(table_code - level);
153
154        let entry = match level {
155            0 => table + index * (SIZEOF_HANDLE_TABLE_ENTRY / HANDLE_VALUE_INC),
156
157            1 => {
158                let table2 = table;
159                let i = index % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
160
161                index -= i;
162                let j = index / (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
163
164                let table1 = self.vmi.read_va_native(table2 + j * SIZEOF_POINTER)?;
165
166                table1 + i * (SIZEOF_HANDLE_TABLE_ENTRY / HANDLE_VALUE_INC)
167            }
168
169            2 => {
170                let table3 = table;
171                let i = index % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
172
173                index -= i;
174                let mut k = index / (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
175
176                let j = k % MIDLEVEL_COUNT;
177                k -= j;
178                k /= MIDLEVEL_COUNT;
179
180                let table2 = self.vmi.read_va_native(table3 + k * SIZEOF_POINTER)?;
181                let table1 = self.vmi.read_va_native(table2 + j * SIZEOF_POINTER)?;
182
183                table1 + i * (SIZEOF_HANDLE_TABLE_ENTRY / HANDLE_VALUE_INC)
184            }
185
186            _ => unreachable!(),
187        };
188
189        Ok(Some(WindowsHandleTableEntry::new(self.vmi, entry)))
190    }
191}