Skip to main content

vmi_os_windows/
lib.rs

1//! # Windows OS-specific VMI operations
2//!
3//! This crate provides functionality for introspecting Windows-based
4//! virtual machines, working in conjunction with the `vmi-core` crate.
5//! It offers abstractions and utilities for navigating Windows kernel
6//! structures, analyzing processes and memory, and performing Windows-specific
7//! VMI tasks.
8//!
9//! ## Features
10//!
11//! - Windows kernel structure parsing and navigation
12//! - Process and thread introspection
13//! - Memory management operations (VAD tree traversal, PFN database manipulation)
14//! - Windows object handling (files, sections, etc.)
15//! - PE file format parsing and analysis
16//!
17//! ## Safety Considerations
18//!
19//! Many operations in this crate require pausing the VM to ensure consistency.
20//! Always pause the VM when performing operations that could be affected by
21//! concurrent changes in the guest OS. Be aware of the Windows version you're
22//! introspecting, as kernel structures may vary between versions. Handle errors
23//! appropriately, as VMI operations can fail due to various reasons (e.g.,
24//! invalid memory access, incompatible Windows version).
25//!
26//! ## Examples
27//!
28//! ```no_run
29//! # use vmi::{
30//! #     VmiCore,
31//! #     arch::amd64::Amd64,
32//! #     driver::VmiVmControl,
33//! #     os::windows::WindowsOs,
34//! # };
35//! #
36//! # fn example<Driver>(
37//! #     vmi: &VmiCore<Driver>,
38//! #     _os: &WindowsOs<Driver>
39//! # ) -> Result<(), Box<dyn std::error::Error>>
40//! # where
41//! #     Driver: VmiVmControl<Architecture = Amd64>,
42//! # {
43//! let _guard = vmi.pause_guard()?;
44//! // Perform introspection operations here
45//! // VM automatically resumes when `_guard` goes out of scope
46//! # Ok(())
47//! # }
48//! ```
49//!
50//! Always consider the potential for race conditions and ensure you're
51//! working with a consistent state of the guest OS.
52
53// Allow Windows-specific naming conventions to be used throughout this module.
54#![allow(
55    non_snake_case,         // example: AlpcpSendMessage
56    non_upper_case_globals, // example: StandbyPageList
57)]
58
59use std::{cell::RefCell, collections::HashMap};
60
61use isr_core::Profile;
62use once_cell::unsync::OnceCell;
63use vmi_core::{
64    AccessContext, Architecture, Gfn, Pa, Registers as _, Va, VcpuId, VmiCore, VmiDriver, VmiError,
65    VmiState,
66    driver::{VmiRead, VmiWrite},
67    os::{ProcessObject, ThreadObject, VmiOs, VmiOsThread},
68    trace::Hex,
69};
70use vmi_macros::derive_trait_from_impl;
71use zerocopy::{FromBytes, IntoBytes};
72
73mod arch;
74pub use self::arch::{
75    ArchAdapter, CONTEXT_AMD64, CONTEXT_X86, FLOATING_SAVE_AREA, KDESCRIPTOR_AMD64,
76    KDESCRIPTOR_X86, KSPECIAL_REGISTERS_AMD64, KSPECIAL_REGISTERS_X86, M128A,
77    MAXIMUM_SUPPORTED_EXTENSION, SIZE_OF_80387_REGISTERS, StructLayout, StructLayout32,
78    StructLayout64, WindowsContext, WindowsExceptionVector, WindowsInterrupt,
79    WindowsPageTableEntry, WindowsRegistersAdapter, WindowsSpecialRegisters, XSAVE_FORMAT,
80};
81
82mod error;
83pub use self::error::WindowsError;
84
85mod iter;
86pub use self::iter::{
87    DirectoryObjectIterator, HandleTableEntryIterator, KeyControlBlockIterator, KeyNodeIterator,
88    KeyValueIterator, ListEntry, ListEntryIterator, ListEntryIteratorBase, ListEntryLayout,
89    TreeNodeIterator,
90};
91
92pub mod pe;
93pub use self::pe::{CodeView, PeError, PeFile, PeHeader, PeImage, PeImageExt};
94
95pub mod unwind;
96
97mod offsets;
98pub use self::offsets::{Offsets, OffsetsExt, Symbols}; // TODO: make private + remove offsets() & symbols() methods
99
100mod comps;
101pub use self::comps::{
102    CurDir, CurDirLayout, FromWindowsObject, LdrDataTableEntry, LdrDataTableEntryLayout,
103    ParseObjectTypeError, Peb, PebLayout, PebLdrData, PebLdrDataLayout, RtlUserProcessParameters,
104    RtlUserProcessParametersLayout, Teb, TebLayout, WOW64_TLS_APCLIST, WOW64_TLS_CPURESERVED,
105    WOW64_TLS_FILESYSREDIR, WOW64_TLS_TEMPLIST, WOW64_TLS_USERCALLBACKDATA, WOW64_TLS_WOW64INFO,
106    WindowsControlArea, WindowsDirectoryObject, WindowsFileObject, WindowsHandleTable,
107    WindowsHandleTableEntry, WindowsHive, WindowsHiveBaseBlock, WindowsHiveCellIndex,
108    WindowsHiveMapDirectory, WindowsHiveMapEntry, WindowsHiveMapTable, WindowsHiveStorageType,
109    WindowsImage, WindowsImpersonationLevel, WindowsKernelProcessorBlock, WindowsKeyControlBlock,
110    WindowsKeyIndex, WindowsKeyNode, WindowsKeyValue, WindowsKeyValueData, WindowsKeyValueFlags,
111    WindowsKeyValueType, WindowsLuid, WindowsModule, WindowsObject, WindowsObjectAttributes,
112    WindowsObjectHeaderNameInfo, WindowsObjectType, WindowsObjectTypeKind, WindowsPeb,
113    WindowsPebBase, WindowsPebLdrData, WindowsPebLdrDataBase, WindowsPrivilege, WindowsProcess,
114    WindowsProcessParameters, WindowsProcessParametersBase, WindowsProcessorMode, WindowsRegion,
115    WindowsSectionObject, WindowsSegment, WindowsSession, WindowsSid, WindowsSidAndAttributes,
116    WindowsSidAttributes, WindowsTeb, WindowsTebBase, WindowsThread, WindowsThreadState,
117    WindowsThreadWaitReason, WindowsToken, WindowsTokenFlags, WindowsTokenPrivilege,
118    WindowsTokenSource, WindowsTokenType, WindowsTrapFrame, WindowsUnloadedDriver,
119    WindowsUserModule, WindowsUserModuleBase, WindowsWow64Kind,
120};
121
122/// VMI operations for the Windows operating system.
123///
124/// `WindowsOs` provides methods and utilities for introspecting a Windows-based
125/// virtual machine. It encapsulates Windows-specific knowledge and operations,
126/// allowing for high-level interactions with the guest OS structures and processes.
127///
128/// # Usage
129///
130/// Create an instance of [`WindowsOs`] using a [`Profile`] that contains information
131/// about the specific Windows version being introspected:
132///
133/// ```no_run
134/// use isr::cache::IsrCache;
135/// use vmi::{
136///     VcpuId, VmiCore,
137///     arch::amd64::Amd64,
138///     driver::{VmiQueryRegisters, VmiRead, VmiVmControl},
139///     os::windows::WindowsOs,
140/// };
141///
142/// # fn example<Driver>(
143/// #     driver: Driver
144/// # ) -> Result<(), Box<dyn std::error::Error>>
145/// # where
146/// #     Driver: VmiRead<Architecture = Amd64>
147/// #           + VmiQueryRegisters<Architecture = Amd64>
148/// #           + VmiVmControl<Architecture = Amd64>,
149/// # {
150/// // Setup VMI.
151/// let core = VmiCore::new(driver)?;
152///
153/// // Try to find the kernel information.
154/// // This is necessary in order to load the profile.
155/// let kernel_info = {
156///     let _guard = core.pause_guard()?;
157///     let registers = core.registers(VcpuId(0))?;
158///
159///     WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
160/// };
161///
162/// // Load the profile using the ISR library.
163/// let isr = IsrCache::new("cache")?;
164/// let entry = isr.entry_from_codeview(kernel_info.codeview)?;
165/// let profile = entry.profile()?;
166///
167/// // Create a new `WindowsOs` instance.
168/// let os = WindowsOs::<Driver>::new(&profile)?;
169/// # Ok(())
170/// # }
171/// ```
172///
173/// # Important Notes
174///
175/// - Many methods of this struct require pausing the VM to ensure consistency.
176///   Use [`pause_guard`] when performing operations that might be affected
177///   by concurrent changes in the guest OS.
178///
179/// - The behavior and accuracy of some methods may vary depending on the
180///   Windows version being introspected. Always ensure your [`Profile`] matches
181///   the guest OS version.
182///
183/// # Examples
184///
185/// Retrieving information about the current process:
186///
187/// ```no_run
188/// # use vmi::{
189/// #     VmiState,
190/// #     arch::amd64::Amd64,
191/// #     driver::VmiRead,
192/// #     os::{VmiOsProcess as _, windows::WindowsOs},
193/// # };
194/// #
195/// # fn example<Driver>(
196/// #     vmi: &VmiState<WindowsOs<Driver>>,
197/// # ) -> Result<(), Box<dyn std::error::Error>>
198/// # where
199/// #     Driver: VmiRead<Architecture = Amd64>,
200/// # {
201/// let process = vmi.os().current_process()?;
202/// let process_id = process.id()?;
203/// let process_name = process.name()?;
204/// println!("Current process: {} (PID: {})", process_name, process_id);
205/// # Ok(())
206/// # }
207/// ```
208///
209/// Enumerating all processes:
210///
211/// ```no_run
212/// # use vmi::{
213/// #     VmiState,
214/// #     arch::amd64::Amd64,
215/// #     driver::VmiRead,
216/// #     os::{VmiOsProcess as _, windows::WindowsOs},
217/// # };
218/// #
219/// # fn example<Driver>(
220/// #     vmi: &VmiState<WindowsOs<Driver>>,
221/// # ) -> Result<(), Box<dyn std::error::Error>>
222/// # where
223/// #     Driver: VmiRead<Architecture = Amd64>,
224/// # {
225/// for process in vmi.os().processes()? {
226///     let process = process?;
227///     println!("Process: {} (PID: {})", process.name()?, process.id()?);
228/// }
229/// # Ok(())
230/// # }
231/// ```
232///
233/// # Safety
234///
235/// While this struct doesn't use unsafe code directly, many of its methods
236/// interact with raw memory of the guest OS. Incorrect usage can lead to
237/// invalid memory access or misinterpretation of data. Always ensure you're
238/// working with the correct memory regions and OS structures.
239///
240/// [`pause_guard`]: VmiCore::pause_guard
241pub struct WindowsOs<Driver>
242where
243    Driver: VmiDriver,
244{
245    offsets: Offsets,
246    symbols: Symbols,
247
248    kernel_image_base: OnceCell<Va>,
249    highest_user_address: OnceCell<Va>,
250    object_root_directory: OnceCell<Va>, // _OBJECT_DIRECTORY*
251    object_header_cookie: OnceCell<u8>,
252    object_type_cache: RefCell<HashMap<WindowsObjectTypeKind, Va>>,
253    object_type_rcache: RefCell<HashMap<Va, WindowsObjectTypeKind>>,
254    object_type_name_cache: RefCell<HashMap<Va, String>>,
255
256    ke_number_processors: OnceCell<u16>,   // CCHAR
257    ki_processor_block: OnceCell<Vec<Va>>, // _KPRCB*[]
258    ki_kva_shadow: OnceCell<bool>,
259    mm_pfn_database: OnceCell<Va>,     // _MMPFN*
260    mm_unloaded_drivers: OnceCell<Va>, // _UNLOADED_DRIVERS*
261    nt_build_number: OnceCell<u32>,    // ULONG
262    nt_build_lab: OnceCell<String>,
263    nt_build_lab_ex: OnceCell<String>,
264    ps_idle_process: OnceCell<Va>, // _EPROCESS*
265
266    _marker: std::marker::PhantomData<Driver>,
267}
268
269/// Information about the Windows kernel image.
270#[derive(Debug)]
271pub struct WindowsKernelInformation {
272    /// Base virtual address where the kernel image is loaded.
273    pub base_address: Va,
274
275    /// Major version number of the Windows kernel.
276    pub version_major: u16,
277
278    /// Minor version number of the Windows kernel.
279    pub version_minor: u16,
280
281    /// CodeView debugging information for the kernel image.
282    pub codeview: CodeView,
283}
284
285/// Represents a `_EXCEPTION_RECORD` structure.
286#[derive(Debug)]
287pub struct WindowsExceptionRecord {
288    /// The `ExceptionCode` field of the exception record.
289    ///
290    /// The reason the exception occurred. This is the code generated by a
291    /// hardware exception, or the code specified in the `RaiseException`
292    /// function for a software-generated exception.
293    pub code: u32,
294
295    /// The `ExceptionFlags` field of the exception record.
296    ///
297    /// This member contains zero or more exception flags.
298    pub flags: u32,
299
300    /// The `ExceptionRecord` field of the exception record.
301    ///
302    /// A pointer to an associated `EXCEPTION_RECORD` structure.
303    /// Exception records can be chained together to provide additional
304    /// information when nested exceptions occur.
305    pub record: Va,
306
307    /// The `ExceptionAddress` field of the exception record.
308    ///
309    /// The address where the exception occurred.
310    pub address: Va,
311
312    /// The `ExceptionInformation` field of the exception record.
313    ///
314    /// An array of additional arguments that describe the exception.
315    /// The number of elements in the array is determined by the `NumberParameters`
316    /// field of the exception record.
317    pub information: Vec<u64>,
318}
319
320macro_rules! offset {
321    ($vmi:expr, $field:ident) => {
322        &$vmi.underlying_os().offsets.$field
323    };
324}
325
326pub(crate) use offset;
327
328macro_rules! offset_ext_v1 {
329    ($vmi:expr, $field:ident) => {
330        match $vmi.underlying_os().offsets.ext() {
331            Some($crate::offsets::OffsetsExt::V1(offsets)) => &offsets.$field,
332            _ => unreachable!(),
333        }
334    };
335}
336
337pub(crate) use offset_ext_v1;
338
339macro_rules! offset_ext_v2 {
340    ($vmi:expr, $field:ident) => {
341        match $vmi.underlying_os().offsets.ext() {
342            Some($crate::offsets::OffsetsExt::V2(offsets)) => &offsets.$field,
343            _ => unreachable!(),
344        }
345    };
346}
347
348pub(crate) use offset_ext_v2;
349
350macro_rules! symbol {
351    ($vmi:expr, $field:ident) => {
352        $vmi.underlying_os().symbols.$field
353    };
354}
355
356pub(crate) use symbol;
357
358macro_rules! this {
359    ($vmi:expr) => {
360        $vmi.underlying_os()
361    };
362}
363
364#[derive_trait_from_impl(WindowsOsExt)]
365#[expect(non_snake_case, non_upper_case_globals)]
366impl<Driver> WindowsOs<Driver>
367where
368    Driver: VmiRead,
369    Driver::Architecture: ArchAdapter<Driver>,
370{
371    /// 32-bit current process pseudo-handle (-1).
372    pub const NtCurrentProcess32: u64 = 0xffff_ffff;
373
374    /// 64-bit current process pseudo-handle (-1).
375    pub const NtCurrentProcess64: u64 = 0xffff_ffff_ffff_ffff;
376
377    /// 32-bit current thread pseudo-handle (-2).
378    pub const NtCurrentThread32: u64 = 0xffff_fffe;
379
380    /// 64-bit current thread pseudo-handle (-2).
381    pub const NtCurrentThread64: u64 = 0xffff_ffff_ffff_fffe;
382
383    /// Creates a new `WindowsOs` instance.
384    pub fn new(profile: &Profile) -> Result<Self, VmiError> {
385        Self::create(profile, OnceCell::new())
386    }
387
388    /// Creates a new `WindowsOs` instance with a known kernel base address.
389    pub fn with_kernel_base(profile: &Profile, kernel_base: Va) -> Result<Self, VmiError> {
390        Self::create(profile, OnceCell::with_value(kernel_base))
391    }
392
393    fn create(profile: &Profile, kernel_image_base: OnceCell<Va>) -> Result<Self, VmiError> {
394        Ok(Self {
395            offsets: Offsets::new(profile)?,
396            symbols: Symbols::new(profile)?,
397            kernel_image_base,
398            highest_user_address: OnceCell::new(),
399            object_root_directory: OnceCell::new(),
400            object_header_cookie: OnceCell::new(),
401            object_type_cache: RefCell::new(HashMap::new()),
402            object_type_rcache: RefCell::new(HashMap::new()),
403            object_type_name_cache: RefCell::new(HashMap::new()),
404            ke_number_processors: OnceCell::new(),
405            ki_processor_block: OnceCell::new(),
406            ki_kva_shadow: OnceCell::new(),
407            mm_pfn_database: OnceCell::new(),
408            mm_unloaded_drivers: OnceCell::new(),
409            nt_build_number: OnceCell::new(),
410            nt_build_lab: OnceCell::new(),
411            nt_build_lab_ex: OnceCell::new(),
412            ps_idle_process: OnceCell::new(),
413            _marker: std::marker::PhantomData,
414        })
415    }
416
417    /// Returns a reference to the Windows-specific memory offsets.
418    pub fn offsets(vmi: VmiState<'_, Self>) -> &Offsets {
419        &this!(vmi).offsets
420    }
421
422    /// Returns a reference to the Windows-specific symbols.
423    pub fn symbols(vmi: VmiState<'_, Self>) -> &Symbols {
424        &this!(vmi).symbols
425    }
426
427    /// Locates the Windows kernel in memory based on the CPU registers.
428    /// This function is architecture-specific.
429    ///
430    /// On AMD64, the kernel is located by taking the `MSR_LSTAR` value and
431    /// reading the virtual memory page by page backwards until the `MZ` header
432    /// is found.
433    pub fn find_kernel(
434        vmi: &VmiCore<Driver>,
435        registers: &<Driver::Architecture as Architecture>::Registers,
436    ) -> Result<Option<WindowsKernelInformation>, VmiError> {
437        Driver::Architecture::find_kernel(vmi, registers)
438    }
439
440    /// Returns the kernel build number.
441    ///
442    /// # Notes
443    ///
444    /// The value is cached after the first read.
445    ///
446    /// # Implementation Details
447    ///
448    /// Corresponds to `NtBuildNumber` symbol.
449    pub fn kernel_build_number(vmi: VmiState<Self>) -> Result<u32, VmiError> {
450        this!(vmi)
451            .nt_build_number
452            .get_or_try_init(|| {
453                let NtBuildNumber = symbol!(vmi, NtBuildNumber);
454
455                let kernel_image_base = Self::kernel_image_base(vmi)?;
456                vmi.read_u32(kernel_image_base + NtBuildNumber)
457            })
458            .cloned()
459    }
460
461    /// Returns the kernel information string.
462    ///
463    /// # Notes
464    ///
465    /// The kernel information string is cached after the first read.
466    ///
467    /// # Implementation Details
468    ///
469    /// Corresponds to `NtBuildLab` symbol.
470    pub fn kernel_information_string_ex(vmi: VmiState<Self>) -> Result<Option<String>, VmiError> {
471        let NtBuildLabEx = match symbol!(vmi, NtBuildLabEx) {
472            Some(offset) => offset,
473            None => return Ok(None),
474        };
475
476        Ok(Some(
477            this!(vmi)
478                .nt_build_lab_ex
479                .get_or_try_init(|| {
480                    let kernel_image_base = Self::kernel_image_base(vmi)?;
481                    vmi.read_string(kernel_image_base + NtBuildLabEx)
482                })
483                .cloned()?,
484        ))
485    }
486
487    /// Checks if the given handle is a kernel handle.
488    ///
489    /// A kernel handle is a handle with the highest bit set.
490    pub fn is_kernel_handle(vmi: VmiState<Self>, handle: u64) -> Result<bool, VmiError> {
491        const KERNEL_HANDLE_MASK32: u64 = 0x8000_0000;
492        const KERNEL_HANDLE_MASK64: u64 = 0xffff_ffff_8000_0000;
493
494        match vmi.registers().address_width() {
495            4 => Ok(handle & KERNEL_HANDLE_MASK32 == KERNEL_HANDLE_MASK32),
496            8 => Ok(handle & KERNEL_HANDLE_MASK64 == KERNEL_HANDLE_MASK64),
497            _ => Err(VmiError::InvalidAddressWidth),
498        }
499    }
500
501    /// Returns the lowest user-mode address.
502    ///
503    /// This method returns a constant value (0x10000) representing the lowest
504    /// address that can be used by user-mode applications in Windows.
505    ///
506    /// # Notes
507    ///
508    /// * Windows creates a `NO_ACCESS` VAD (Virtual Address Descriptor) for the first 64KB
509    ///   of virtual memory. This means the VA range 0-0x10000 is off-limits for usage.
510    /// * This behavior is consistent across all Windows versions from XP through
511    ///   recent Windows 11, and applies to x86, x64, and ARM64 architectures.
512    /// * Many Windows APIs leverage this fact to determine whether an input argument
513    ///   is a pointer or not. Here are two notable examples:
514    ///
515    ///   1. The `FindResource()` function accepts an `lpName` parameter of type `LPCTSTR`,
516    ///      which can be either:
517    ///      - A pointer to a valid string
518    ///      - A value created by `MAKEINTRESOURCE(ID)`
519    ///
520    ///         This allows `FindResource()` to accept `WORD` values (unsigned shorts) with
521    ///         a maximum value of 0xFFFF, distinguishing them from valid memory addresses.
522    ///
523    ///   2. The `AddAtom()` function similarly accepts an `lpString` parameter of type `LPCTSTR`.
524    ///      This parameter can be:
525    ///      - A pointer to a null-terminated string (max 255 bytes)
526    ///      - An integer atom converted using the `MAKEINTATOM(ID)` macro
527    ///
528    ///   In both cases, the API can distinguish between valid pointers (which will be
529    ///   above 0x10000) and integer values (which will be below 0x10000), allowing
530    ///   for flexible parameter usage without ambiguity.
531    pub fn lowest_user_address(_vmi: VmiState<Self>) -> Result<Va, VmiError> {
532        Ok(Va(0x10000))
533    }
534
535    /// Returns the highest user-mode address.
536    ///
537    /// This method reads the highest user-mode address from the Windows kernel.
538    /// The value is cached after the first read for performance.
539    ///
540    /// # Notes
541    ///
542    /// This value is cached after the first read.
543    ///
544    /// # Implementation Details
545    ///
546    /// Corresponds to `MmHighestUserAddress` symbol.
547    pub fn highest_user_address(vmi: VmiState<Self>) -> Result<Va, VmiError> {
548        this!(vmi)
549            .highest_user_address
550            .get_or_try_init(|| {
551                let MmHighestUserAddress = symbol!(vmi, MmHighestUserAddress);
552
553                let kernel_image_base = Self::kernel_image_base(vmi)?;
554                vmi.read_va_native(kernel_image_base + MmHighestUserAddress)
555            })
556            .copied()
557    }
558
559    /// Checks if a given address is a valid user-mode address.
560    ///
561    /// This method determines whether the provided address falls within
562    /// the range of valid user-mode addresses in Windows.
563    pub fn is_valid_user_address(vmi: VmiState<Self>, address: Va) -> Result<bool, VmiError> {
564        let lowest_user_address = Self::lowest_user_address(vmi)?;
565        let highest_user_address = Self::highest_user_address(vmi)?;
566
567        Ok(address >= lowest_user_address && address <= highest_user_address)
568    }
569
570    /// Returns an iterator over recently unloaded drivers.
571    ///
572    /// Walks the kernel's fixed-size circular buffer of unloaded driver
573    /// records, newest first. The iterator stops at the first empty slot
574    /// or after the buffer's 50-entry capacity has been visited.
575    ///
576    /// # Implementation Details
577    ///
578    /// Corresponds to `MmUnloadedDrivers` and `MmLastUnloadedDriver`.
579    pub fn unloaded_modules<'a>(
580        vmi: VmiState<'a, Self>,
581    ) -> Result<
582        impl Iterator<Item = Result<WindowsUnloadedDriver<'a, Driver>, VmiError>> + use<'a, Driver>,
583        VmiError,
584    > {
585        // Note that the value of `MmLastUnloadedDriver` is intentionally
586        // not cached.
587        let MmLastUnloadedDriver = symbol!(vmi, MmLastUnloadedDriver);
588
589        let kernel_image_base = Self::kernel_image_base(vmi)?;
590        let mm_last_unloaded_driver = vmi.read_u32(kernel_image_base + MmLastUnloadedDriver)?;
591
592        let mm_unloaded_drivers = this!(vmi)
593            .mm_unloaded_drivers
594            .get_or_try_init(|| {
595                let MmUnloadedDrivers = symbol!(vmi, MmUnloadedDrivers);
596
597                let kernel_image_base = Self::kernel_image_base(vmi)?;
598                vmi.read_va_native(kernel_image_base + MmUnloadedDrivers)
599            })
600            .copied()?;
601
602        // Hardcoded in dbgeng.dll.
603        const MI_UNLOADED_DRIVERS: u32 = 50;
604
605        // Corresponds to `_UNLOADED_DRIVERS.Name.Buffer` offset.
606        const UNLOADED_DRIVERS_Name_Buffer: u64 = 0x08; // 32-bit: 0x04
607
608        // Corresponds to `sizeof(_UNLOADED_DRIVERS)`.
609        const UNLOADED_DRIVERS_sizeof: u64 = 0x28; // 32-bit: 0x18
610
611        let mut index = mm_last_unloaded_driver;
612        let mut count = 0;
613
614        Ok(std::iter::from_fn(move || {
615            if count >= MI_UNLOADED_DRIVERS {
616                return None;
617            }
618
619            index = index.checked_sub(1).unwrap_or(MI_UNLOADED_DRIVERS - 1);
620            count += 1;
621
622            let va = mm_unloaded_drivers + index as u64 * UNLOADED_DRIVERS_sizeof;
623            let name_buffer = match vmi.read_va_native(va + UNLOADED_DRIVERS_Name_Buffer) {
624                Ok(name_buffer) => name_buffer,
625                Err(err) => return Some(Err(err)),
626            };
627
628            if name_buffer.is_null() {
629                return None;
630            }
631
632            Some(Ok(WindowsUnloadedDriver::new(vmi, va)))
633        }))
634    }
635
636    /// Returns the number of active processors in the system.
637    ///
638    /// # Implementation Details
639    ///
640    /// Corresponds to `KeNumberProcessors`.
641    pub fn number_of_processors(vmi: VmiState<Self>) -> Result<u16, VmiError> {
642        this!(vmi)
643            .ke_number_processors
644            .get_or_try_init(|| {
645                // KeNumberProcessors is a CCHAR (i8), but we return it as u16
646                // for convenience since VcpuId is u16.
647                let KeNumberProcessors = symbol!(vmi, KeNumberProcessors);
648
649                let kernel_image_base = Self::kernel_image_base(vmi)?;
650                Ok(vmi.read_u8(kernel_image_base + KeNumberProcessors)? as u16)
651            })
652            .copied()
653    }
654
655    /// Returns the virtual address of the current Kernel Processor Control
656    /// Region (KPCR).
657    ///
658    /// The KPCR is a per-processor data structure in Windows that contains
659    /// critical information about the current processor state. This method
660    /// returns the virtual address of the KPCR for the current processor.
661    pub fn current_kpcr(vmi: VmiState<Self>) -> Va {
662        Driver::Architecture::current_kpcr(vmi)
663    }
664
665    /// Returns a Kernel Processor Control Block (KPRCB) for the specified processor.
666    ///
667    /// The KPRCB is an opaque, per-processor structure embedded within the
668    /// KPCR.
669    ///
670    /// # Implementation Details
671    ///
672    /// Corresponds to `KiProcessorBlock` symbol, offset by the processor ID.
673    pub fn kprcb<'a>(
674        vmi: VmiState<'a, Self>,
675        vcpu_id: VcpuId,
676    ) -> Result<WindowsKernelProcessorBlock<'a, Driver>, VmiError> {
677        let number_of_processors = Self::number_of_processors(vmi)? as u64;
678        let ki_processor_block = this!(vmi).ki_processor_block.get_or_try_init(|| {
679            let KiProcessorBlock = symbol!(vmi, KiProcessorBlock);
680
681            let kernel_image_base = Self::kernel_image_base(vmi)?;
682
683            // Pre-read all KPCR addresses for each processor and cache them.
684            let mut blocks = Vec::new();
685            for cpu in 0..number_of_processors {
686                let offset = cpu * vmi.registers().address_width() as u64;
687                let addr = vmi.read_va_native(kernel_image_base + KiProcessorBlock + offset)?;
688                blocks.push(addr);
689            }
690
691            Ok::<_, VmiError>(blocks)
692        })?;
693
694        if vcpu_id.0 as usize >= ki_processor_block.len() {
695            return Err(WindowsError::InvalidProcessor(vcpu_id).into());
696        }
697
698        let prcb = ki_processor_block[vcpu_id.0 as usize];
699        if prcb.is_null() {
700            return Err(WindowsError::CorruptedStruct("KiProcessorBlock").into());
701        }
702
703        Ok(WindowsKernelProcessorBlock::new(vmi, prcb))
704    }
705
706    /// Returns the Kernel Processor Control Block (KPRCB) for the processor
707    /// whose register state is currently loaded.
708    ///
709    /// Unlike [`kprcb`](Self::kprcb), which indexes the `KiProcessorBlock`
710    /// array by processor ID, this resolves the PRCB from the current KPCR
711    /// (`gs`-relative on AMD64) through `KPCR.Prcb`.
712    ///
713    /// # Implementation Details
714    ///
715    /// Corresponds to `KPCR.Prcb`.
716    pub fn current_kprcb<'a>(
717        vmi: VmiState<'a, Self>,
718    ) -> Result<WindowsKernelProcessorBlock<'a, Driver>, VmiError> {
719        let KPCR = offset!(vmi, _KPCR);
720
721        let kpcr = Self::current_kpcr(vmi);
722        if kpcr.is_null() {
723            return Err(WindowsError::CorruptedStruct("KPCR").into());
724        }
725
726        let prcb = kpcr + KPCR.Prcb.offset();
727        Ok(WindowsKernelProcessorBlock::new(vmi, prcb))
728    }
729
730    /// Returns information from an exception record at the specified address.
731    ///
732    /// This method reads and parses an `EXCEPTION_RECORD` structure from
733    /// memory, providing detailed information about an exception that has
734    /// occurred in the system. The returned [`WindowsExceptionRecord`]
735    /// contains data such as the exception code, flags, and related memory
736    /// addresses.
737    pub fn exception_record(
738        vmi: VmiState<Self>,
739        address: Va,
740    ) -> Result<WindowsExceptionRecord, VmiError> {
741        #[repr(C)]
742        #[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
743        #[allow(non_camel_case_types, non_snake_case)]
744        struct _EXCEPTION_RECORD {
745            ExceptionCode: u32,
746            ExceptionFlags: u32,
747            ExceptionRecord: u64,
748            ExceptionAddress: u64,
749            NumberParameters: u64,
750            ExceptionInformation: [u64; 15],
751        }
752
753        let record = vmi.read_struct::<_EXCEPTION_RECORD>(address)?;
754
755        Ok(WindowsExceptionRecord {
756            code: record.ExceptionCode,
757            flags: record.ExceptionFlags,
758            record: record.ExceptionRecord.into(),
759            address: record.ExceptionAddress.into(),
760            information: record.ExceptionInformation
761                [..u64::min(record.NumberParameters, 15) as usize]
762                .to_vec(),
763        })
764    }
765
766    /// Returns the last status value for the current thread.
767    ///
768    /// In Windows, the last status value is typically used to store error codes
769    /// or success indicators from system calls. This method reads this value
770    /// from the Thread Environment Block (TEB) of the current thread, providing
771    /// insight into the outcome of recent operations performed by the thread.
772    ///
773    /// Returns `None` if the TEB is not available.
774    ///
775    /// # Notes
776    ///
777    /// `LastStatusValue` is a `NTSTATUS` value, whereas `LastError` is a Win32
778    /// error code. The two values are related but not identical. You can obtain
779    /// the Win32 error code by calling
780    /// [`VmiOs::last_error`].
781    ///
782    /// # Implementation Details
783    ///
784    /// Corresponds to `NtCurrentTeb()->LastStatusValue`.
785    pub fn last_status(vmi: VmiState<Self>) -> Result<Option<u32>, VmiError> {
786        let KTHREAD = offset!(vmi, _KTHREAD);
787        let TEB = offset!(vmi, _TEB);
788
789        let current_thread = Self::current_thread(vmi)?.object()?;
790        let teb = vmi.read_va_native(current_thread.0 + KTHREAD.Teb.offset())?;
791
792        if teb.is_null() {
793            return Ok(None);
794        }
795
796        let result = vmi.read_u32(teb + TEB.LastStatusValue.offset())?;
797        Ok(Some(result))
798    }
799
800    /// Returns the idle process (PID 0).
801    ///
802    /// The idle process is a special system process that runs when no other
803    /// threads are ready to execute on a processor.
804    ///
805    /// # Notes
806    ///
807    /// This value is cached after the first read.
808    ///
809    /// # Implementation Details
810    ///
811    /// Corresponds to `PsIdleProcess` symbol.
812    pub fn idle_process<'a>(
813        vmi: VmiState<'a, Self>,
814    ) -> Result<WindowsProcess<'a, Driver>, VmiError> {
815        let ps_idle_process = this!(vmi)
816            .ps_idle_process
817            .get_or_try_init(|| {
818                let PsIdleProcess = symbol!(vmi, PsIdleProcess);
819
820                let kernel_image_base = Self::kernel_image_base(vmi)?;
821                vmi.read_va_native(kernel_image_base + PsIdleProcess)
822            })
823            .copied()?;
824
825        Ok(WindowsProcess::new(vmi, ProcessObject(ps_idle_process)))
826    }
827
828    /// Returns the idle thread for the current processor.
829    ///
830    /// The idle thread is a per-processor thread that runs when no other
831    /// threads are scheduled on that processor.
832    ///
833    /// # Implementation Details
834    ///
835    /// Corresponds to `KPCR.Prcb.IdleThread`.
836    pub fn idle_thread<'a>(vmi: VmiState<'a, Self>) -> Result<WindowsThread<'a, Driver>, VmiError> {
837        Self::current_kprcb(vmi)?.idle_thread()
838    }
839
840    /// Returns the virtual address of the Page Frame Number (PFN) database.
841    ///
842    /// The PFN database is a critical data structure in Windows memory management,
843    /// containing information about each physical page in the system.
844    ///
845    /// # Notes
846    ///
847    /// This value is cached after the first read.
848    ///
849    /// # Implementation Details
850    ///
851    /// Corresponds to `MmPfnDatabase` symbol.
852    fn pfn_database(vmi: VmiState<Self>) -> Result<Va, VmiError> {
853        this!(vmi)
854            .mm_pfn_database
855            .get_or_try_init(|| {
856                let MmPfnDatabase = symbol!(vmi, MmPfnDatabase);
857
858                let kernel_image_base = Self::kernel_image_base(vmi)?;
859                vmi.read_va_native(kernel_image_base + MmPfnDatabase)
860            })
861            .copied()
862    }
863
864    /// Returns the Windows object.
865    pub fn object<'a>(
866        vmi: VmiState<'a, Self>,
867        va: Va,
868    ) -> Result<WindowsObject<'a, Driver>, VmiError> {
869        Ok(WindowsObject::new(vmi, va))
870    }
871
872    /// Returns a Windows object type for the given object kind.
873    ///
874    /// # Notes
875    ///
876    /// The object type is cached after the first read.
877    ///
878    /// # Implementation Details
879    ///
880    /// - `File` corresponds to `IoFileObjectType`.
881    /// - `Job` corresponds to `PsJobType`.
882    /// - `Key` corresponds to `CmKeyObjectType`.
883    /// - `Process` corresponds to `PsProcessType`.
884    /// - `Thread` corresponds to `PsThreadType`.
885    /// - `Token` corresponds to `SeTokenObjectType`.
886    /// - Other types are not supported.
887    pub fn object_type<'a>(
888        vmi: VmiState<'a, Self>,
889        kind: WindowsObjectTypeKind,
890    ) -> Result<WindowsObjectType<'a, Driver>, VmiError> {
891        if let Some(va) = this!(vmi).object_type_cache.borrow().get(&kind).copied() {
892            return Ok(WindowsObjectType::new(vmi, va));
893        }
894
895        let symbol = match kind {
896            WindowsObjectTypeKind::File => symbol!(vmi, IoFileObjectType),
897            WindowsObjectTypeKind::Job => symbol!(vmi, PsJobType),
898            WindowsObjectTypeKind::Key => symbol!(vmi, CmKeyObjectType),
899            WindowsObjectTypeKind::Process => symbol!(vmi, PsProcessType),
900            WindowsObjectTypeKind::Thread => symbol!(vmi, PsThreadType),
901            WindowsObjectTypeKind::Token => symbol!(vmi, SeTokenObjectType),
902            _ => return Err(VmiError::NotSupported),
903        };
904
905        let va = vmi.read_va(Self::kernel_image_base(vmi)? + symbol)?;
906        this!(vmi).object_type_cache.borrow_mut().insert(kind, va);
907
908        Ok(WindowsObjectType::new(vmi, va))
909    }
910
911    /// Returns the root directory object for the Windows kernel.
912    ///
913    /// # Notes
914    ///
915    /// The object root directory is cached after the first read.
916    ///
917    /// # Implementation Details
918    ///
919    /// Corresponds to `ObpRootDirectoryObject` symbol.
920    pub fn object_root_directory<'a>(
921        vmi: VmiState<'a, Self>,
922    ) -> Result<WindowsDirectoryObject<'a, Driver>, VmiError> {
923        let object_root_directory = this!(vmi)
924            .object_root_directory
925            .get_or_try_init(|| {
926                let ObpRootDirectoryObject = symbol!(vmi, ObpRootDirectoryObject);
927
928                let kernel_image_base = Self::kernel_image_base(vmi)?;
929                vmi.read_va_native(kernel_image_base + ObpRootDirectoryObject)
930            })
931            .copied()?;
932
933        Ok(WindowsDirectoryObject::new(vmi, object_root_directory))
934    }
935
936    /// Returns the object header cookie used for obfuscating object types.
937    /// Returns `None` if the cookie is not present in the kernel image.
938    ///
939    /// # Notes
940    ///
941    /// Windows 10 introduced a security feature that obfuscates the type
942    /// of kernel objects by XORing the `TypeIndex` field in the object header
943    /// with a random cookie value. This method fetches that cookie, which is
944    /// essential for correctly interpreting object headers in memory.
945    ///
946    /// The cookie is cached after the first read.
947    ///
948    /// # Implementation Details
949    ///
950    /// Corresponds to `ObHeaderCookie` symbol.
951    pub fn object_header_cookie(vmi: VmiState<Self>) -> Result<Option<u8>, VmiError> {
952        let ObHeaderCookie = match symbol!(vmi, ObHeaderCookie) {
953            Some(cookie) => cookie,
954            None => return Ok(None),
955        };
956
957        Ok(Some(
958            this!(vmi)
959                .object_header_cookie
960                .get_or_try_init(|| {
961                    let kernel_image_base = Self::kernel_image_base(vmi)?;
962                    vmi.read_u8(kernel_image_base + ObHeaderCookie)
963                })
964                .copied()?,
965        ))
966    }
967
968    /// Returns the Windows object attributes.
969    pub fn object_attributes<'a>(
970        vmi: VmiState<'a, Self>,
971        object_attributes: Va,
972    ) -> Result<WindowsObjectAttributes<'a, Driver>, VmiError> {
973        Ok(WindowsObjectAttributes::new(vmi, object_attributes))
974    }
975
976    /// Resolves an absolute object-namespace path to a named object.
977    ///
978    /// Descends from the root directory ([`object_root_directory`]) one
979    /// component at a time.
980    ///
981    /// Returns `Ok(None)` when a component does not exist, or when an
982    /// intermediate component resolves to something other than a
983    /// `Directory`. The final component may be any object type.
984    ///
985    /// Does not follow `SymbolicLink` objects.
986    ///
987    /// [`object_root_directory`]: Self::object_root_directory
988    pub fn lookup_object<'a>(
989        vmi: VmiState<'a, Self>,
990        path: impl AsRef<str>,
991    ) -> Result<Option<WindowsObject<'a, Driver>>, VmiError> {
992        Self::object_root_directory(vmi)?.lookup(path)
993    }
994
995    /// Returns an iterator over all loaded registry hives.
996    ///
997    /// This method returns an iterator over all loaded registry hives. It
998    /// reads the `CmpHiveListHead` symbol from the kernel image and iterates
999    /// over the linked list of `_CMHIVE` structures representing each hive.
1000    pub fn hives<'a>(
1001        vmi: VmiState<'a, Self>,
1002    ) -> Result<
1003        impl Iterator<Item = Result<WindowsHive<'a, Driver>, VmiError>> + use<'a, Driver>,
1004        VmiError,
1005    > {
1006        let CmpHiveListHead = Self::kernel_image_base(vmi)? + symbol!(vmi, CmpHiveListHead);
1007        let CMHIVE = offset!(vmi, _CMHIVE);
1008
1009        Ok(
1010            ListEntryIterator::new(vmi, CmpHiveListHead, CMHIVE.HiveList.offset())
1011                .map(move |result| result.map(|entry| WindowsHive::new(vmi, entry))),
1012        )
1013    }
1014
1015    /// Resolves an absolute registry path against the loaded hive set.
1016    ///
1017    /// Picks the hive whose `HiveRootPath` is the longest component-aligned
1018    /// case-insensitive prefix of `path`, strips that prefix, and forwards
1019    /// the remainder to [`WindowsHive::lookup`].
1020    ///
1021    /// Returns `Ok(None)` when no hive root prefixes the path or when the
1022    /// descent does not find the key.
1023    ///
1024    /// Hives with an empty `HiveRootPath` match any input at depth zero,
1025    /// so they only win when no loaded hive owns a real prefix.
1026    ///
1027    /// Does not follow `HIVE_EXIT` links into other hives.
1028    pub fn lookup_key<'a>(
1029        vmi: VmiState<'a, Self>,
1030        path: impl AsRef<str>,
1031    ) -> Result<Option<WindowsKeyNode<'a, Driver>>, VmiError> {
1032        let path = path.as_ref();
1033        let path_components = path
1034            .split('\\')
1035            .filter(|component| !component.is_empty())
1036            .collect::<Vec<_>>();
1037
1038        let mut best = None;
1039
1040        for hive in Self::hives(vmi)? {
1041            let hive = match hive {
1042                Ok(hive) => hive,
1043                Err(err) => {
1044                    tracing::trace!(%err, "skipping unreadable hive entry");
1045                    continue;
1046                }
1047            };
1048
1049            let root = match hive.hive_root_path() {
1050                Ok(root) => root,
1051                Err(err) => {
1052                    tracing::trace!(%err, "skipping hive with unreadable HiveRootPath");
1053                    continue;
1054                }
1055            };
1056
1057            let mut depth = 0;
1058            let mut matched = true;
1059            for root_component in root.split('\\').filter(|component| !component.is_empty()) {
1060                let path_component = match path_components.get(depth) {
1061                    Some(path_component) => path_component,
1062                    None => {
1063                        matched = false;
1064                        break;
1065                    }
1066                };
1067
1068                if !path_component.eq_ignore_ascii_case(root_component) {
1069                    matched = false;
1070                    break;
1071                }
1072
1073                depth += 1;
1074            }
1075
1076            if !matched {
1077                continue;
1078            }
1079
1080            let beats_best = match &best {
1081                Some((_, best_depth)) => depth > *best_depth,
1082                None => true,
1083            };
1084
1085            if beats_best {
1086                best = Some((hive, depth));
1087            }
1088        }
1089
1090        match best {
1091            Some((hive, depth)) => {
1092                let rest = path_components[depth..].join("\\");
1093                hive.lookup(rest)
1094            }
1095            None => Ok(None),
1096        }
1097    }
1098
1099    /// Reads an `_EX_FAST_REF` and returns the referenced object's
1100    /// virtual address, with the low reference-count bits masked off.
1101    pub fn read_fast_ref(vmi: VmiState<Self>, va: Va) -> Result<Va, VmiError> {
1102        let EX_FAST_REF = offset!(vmi, _EX_FAST_REF);
1103        debug_assert_eq!(EX_FAST_REF.RefCnt.offset(), 0);
1104        debug_assert_eq!(EX_FAST_REF.RefCnt.bit_position(), 0);
1105
1106        let object = vmi.read_va_native(va)?;
1107
1108        // For Amd64:
1109        //   let object = object & !0xf;
1110        // For x86:
1111        //   let object = object & !0x7;
1112        Ok(object & !((1 << EX_FAST_REF.RefCnt.bit_length()) - 1))
1113    }
1114
1115    /// Reads string of bytes from an `_ANSI_STRING` structure.
1116    ///
1117    /// This method reads a native `_ANSI_STRING` structure which contains
1118    /// an ASCII/ANSI string. The structure is read according to the current
1119    /// OS's architecture (32-bit or 64-bit).
1120    pub fn read_ansi_string_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u8>, VmiError> {
1121        Self::read_ansi_string_bytes_in(vmi, vmi.access_context(va))
1122    }
1123
1124    /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` structure.
1125    ///
1126    /// This method is specifically for reading `_ANSI_STRING` structures in
1127    /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1128    pub fn read_ansi_string32_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u8>, VmiError> {
1129        Self::read_ansi_string32_bytes_in(vmi, vmi.access_context(va))
1130    }
1131
1132    /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` structure.
1133    ///
1134    /// This method is specifically for reading `_ANSI_STRING` structures in
1135    /// 64-bit processes where pointers are 64 bits.
1136    pub fn read_ansi_string64_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u8>, VmiError> {
1137        Self::read_ansi_string64_bytes_in(vmi, vmi.access_context(va))
1138    }
1139
1140    /// Reads string from an `_ANSI_STRING` structure.
1141    ///
1142    /// This method reads a native `_ANSI_STRING` structure which contains
1143    /// an ASCII/ANSI string. The structure is read according to the current
1144    /// OS's architecture (32-bit or 64-bit).
1145    pub fn read_ansi_string(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1146        Self::read_ansi_string_in(vmi, vmi.access_context(va))
1147    }
1148
1149    /// Reads string from a 32-bit version of `_ANSI_STRING` structure.
1150    ///
1151    /// This method is specifically for reading `_ANSI_STRING` structures in
1152    /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1153    pub fn read_ansi_string32(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1154        Self::read_ansi_string32_in(vmi, vmi.access_context(va))
1155    }
1156
1157    /// Reads string from a 64-bit version of `_ANSI_STRING` structure.
1158    ///
1159    /// This method is specifically for reading `_ANSI_STRING` structures in
1160    /// 64-bit processes where pointers are 64 bits.
1161    pub fn read_ansi_string64(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1162        Self::read_ansi_string64_in(vmi, vmi.access_context(va))
1163    }
1164
1165    /// Reads string from a `_UNICODE_STRING` structure.
1166    ///
1167    /// This method reads a native `_UNICODE_STRING` structure which contains
1168    /// a UTF-16 string. The structure is read according to the current OS's
1169    /// architecture (32-bit or 64-bit).
1170    pub fn read_unicode_string_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u16>, VmiError> {
1171        Self::read_unicode_string_bytes_in(vmi, vmi.access_context(va))
1172    }
1173
1174    /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1175    ///
1176    /// This method is specifically for reading `_UNICODE_STRING` structures
1177    /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1178    pub fn read_unicode_string32_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u16>, VmiError> {
1179        Self::read_unicode_string32_bytes_in(vmi, vmi.access_context(va))
1180    }
1181
1182    /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1183    ///
1184    /// This method is specifically for reading `_UNICODE_STRING` structures
1185    /// in 64-bit processes where pointers are 64 bits.
1186    pub fn read_unicode_string64_bytes(vmi: VmiState<Self>, va: Va) -> Result<Vec<u16>, VmiError> {
1187        Self::read_unicode_string64_bytes_in(vmi, vmi.access_context(va))
1188    }
1189
1190    /// Reads string from a `_UNICODE_STRING` structure.
1191    ///
1192    /// This method reads a native `_UNICODE_STRING` structure which contains
1193    /// a UTF-16 string. The structure is read according to the current OS's
1194    /// architecture (32-bit or 64-bit).
1195    pub fn read_unicode_string(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1196        Self::read_unicode_string_in(vmi, vmi.access_context(va))
1197    }
1198
1199    /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1200    ///
1201    /// This method is specifically for reading `_UNICODE_STRING` structures
1202    /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1203    pub fn read_unicode_string32(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1204        Self::read_unicode_string32_in(vmi, vmi.access_context(va))
1205    }
1206
1207    /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1208    ///
1209    /// This method is specifically for reading `_UNICODE_STRING` structures
1210    /// in 64-bit processes where pointers are 64 bits.
1211    pub fn read_unicode_string64(vmi: VmiState<Self>, va: Va) -> Result<String, VmiError> {
1212        Self::read_unicode_string64_in(vmi, vmi.access_context(va))
1213    }
1214
1215    /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` or
1216    /// `_UNICODE_STRING` structure.
1217    ///
1218    /// This method is specifically for reading `_ANSI_STRING` or
1219    /// `_UNICODE_STRING` structures in 32-bit processes or WoW64 processes
1220    /// where pointers are 32 bits.
1221    fn read_string32_in(
1222        vmi: VmiState<Self>,
1223        ctx: impl Into<AccessContext>,
1224    ) -> Result<Vec<u8>, VmiError> {
1225        let mut ctx = ctx.into();
1226
1227        let mut buffer = [0u8; 8];
1228        vmi.read_in(ctx, &mut buffer)?;
1229
1230        let string_length = u16::from_le_bytes([buffer[0], buffer[1]]);
1231
1232        if string_length == 0 {
1233            return Ok(Vec::new());
1234        }
1235
1236        // let string_maximum_length = u16::from_le_bytes([buffer[2], buffer[3]]);
1237        let string_buffer = u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
1238
1239        if string_buffer == 0 {
1240            tracing::warn!(
1241                addr = %Hex(ctx.address),
1242                len = string_length,
1243                "string buffer is NULL"
1244            );
1245
1246            return Ok(Vec::new());
1247        }
1248
1249        ctx.address = string_buffer as u64;
1250
1251        let mut buffer = vec![0u8; string_length as usize];
1252        vmi.read_in(ctx, &mut buffer)?;
1253
1254        Ok(buffer)
1255    }
1256
1257    /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` or
1258    /// `_UNICODE_STRING` structure.
1259    ///
1260    /// This method is specifically for reading `_ANSI_STRING` or
1261    /// `_UNICODE_STRING` structures in 64-bit processes where pointers
1262    /// are 64 bits.
1263    fn read_string64_in(
1264        vmi: VmiState<Self>,
1265        ctx: impl Into<AccessContext>,
1266    ) -> Result<Vec<u8>, VmiError> {
1267        let mut ctx = ctx.into();
1268
1269        let mut buffer = [0u8; 16];
1270        vmi.read_in(ctx, &mut buffer)?;
1271
1272        let string_length = u16::from_le_bytes([buffer[0], buffer[1]]);
1273
1274        if string_length == 0 {
1275            return Ok(Vec::new());
1276        }
1277
1278        // We intentionally skip reading the MaximumLength field,
1279        // since we don't actually need it.
1280        // let string_maximum_length = u16::from_le_bytes([buffer[2], buffer[3]]);
1281
1282        #[rustfmt::skip]
1283        let string_buffer = u64::from_le_bytes([
1284            buffer[ 8], buffer[ 9], buffer[10], buffer[11],
1285            buffer[12], buffer[13], buffer[14], buffer[15],
1286        ]);
1287
1288        if string_buffer == 0 {
1289            tracing::warn!(
1290                addr = %Hex(ctx.address),
1291                len = string_length,
1292                "string buffer is NULL"
1293            );
1294
1295            return Ok(Vec::new());
1296        }
1297
1298        ctx.address = string_buffer;
1299
1300        let mut buffer = vec![0u8; string_length as usize];
1301        vmi.read_in(ctx, &mut buffer)?;
1302
1303        Ok(buffer)
1304    }
1305
1306    /// Reads string of bytes from an `_ANSI_STRING` structure.
1307    ///
1308    /// This method reads a native `_ANSI_STRING` structure which contains
1309    /// an ASCII/ANSI string. The structure is read according to the current
1310    /// OS's architecture (32-bit or 64-bit).
1311    pub fn read_ansi_string_bytes_in(
1312        vmi: VmiState<Self>,
1313        ctx: impl Into<AccessContext>,
1314    ) -> Result<Vec<u8>, VmiError> {
1315        match vmi.registers().address_width() {
1316            4 => Self::read_ansi_string32_bytes_in(vmi, ctx),
1317            8 => Self::read_ansi_string64_bytes_in(vmi, ctx),
1318            _ => Err(VmiError::InvalidAddressWidth),
1319        }
1320    }
1321
1322    /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` structure.
1323    ///
1324    /// This method is specifically for reading `_ANSI_STRING` structures in
1325    /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1326    pub fn read_ansi_string32_bytes_in(
1327        vmi: VmiState<Self>,
1328        ctx: impl Into<AccessContext>,
1329    ) -> Result<Vec<u8>, VmiError> {
1330        Self::read_string32_in(vmi, ctx)
1331    }
1332
1333    /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` structure.
1334    ///
1335    /// This method is specifically for reading `_ANSI_STRING` structures in
1336    /// 64-bit processes where pointers are 64 bits.
1337    pub fn read_ansi_string64_bytes_in(
1338        vmi: VmiState<Self>,
1339        ctx: impl Into<AccessContext>,
1340    ) -> Result<Vec<u8>, VmiError> {
1341        Self::read_string64_in(vmi, ctx)
1342    }
1343
1344    /// Reads string from an `_ANSI_STRING` structure.
1345    ///
1346    /// This method reads a native `_ANSI_STRING` structure which contains
1347    /// an ASCII/ANSI string. The structure is read according to the current
1348    /// OS's architecture (32-bit or 64-bit).
1349    pub fn read_ansi_string_in(
1350        vmi: VmiState<Self>,
1351        ctx: impl Into<AccessContext>,
1352    ) -> Result<String, VmiError> {
1353        match vmi.registers().address_width() {
1354            4 => Self::read_ansi_string32_in(vmi, ctx),
1355            8 => Self::read_ansi_string64_in(vmi, ctx),
1356            _ => Err(VmiError::InvalidAddressWidth),
1357        }
1358    }
1359
1360    /// Reads string from a 32-bit version of `_ANSI_STRING` structure.
1361    ///
1362    /// This method is specifically for reading `_ANSI_STRING` structures in
1363    /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1364    pub fn read_ansi_string32_in(
1365        vmi: VmiState<Self>,
1366        ctx: impl Into<AccessContext>,
1367    ) -> Result<String, VmiError> {
1368        Ok(String::from_utf8_lossy(&Self::read_ansi_string32_bytes_in(vmi, ctx)?).into())
1369    }
1370
1371    /// Reads string from a 64-bit version of `_ANSI_STRING` structure.
1372    ///
1373    /// This method is specifically for reading `_ANSI_STRING` structures in
1374    /// 64-bit processes where pointers are 64 bits.
1375    pub fn read_ansi_string64_in(
1376        vmi: VmiState<Self>,
1377        ctx: impl Into<AccessContext>,
1378    ) -> Result<String, VmiError> {
1379        Ok(String::from_utf8_lossy(&Self::read_ansi_string64_bytes_in(vmi, ctx)?).into())
1380    }
1381
1382    /// Reads string from a `_UNICODE_STRING` structure.
1383    ///
1384    /// This method reads a native `_UNICODE_STRING` structure which contains
1385    /// a UTF-16 string. The structure is read according to the current OS's
1386    /// architecture (32-bit or 64-bit).
1387    pub fn read_unicode_string_bytes_in(
1388        vmi: VmiState<Self>,
1389        ctx: impl Into<AccessContext>,
1390    ) -> Result<Vec<u16>, VmiError> {
1391        match vmi.registers().address_width() {
1392            4 => Self::read_unicode_string32_bytes_in(vmi, ctx),
1393            8 => Self::read_unicode_string64_bytes_in(vmi, ctx),
1394            _ => Err(VmiError::InvalidAddressWidth),
1395        }
1396    }
1397
1398    /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1399    ///
1400    /// This method is specifically for reading `_UNICODE_STRING` structures
1401    /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1402    pub fn read_unicode_string32_bytes_in(
1403        vmi: VmiState<Self>,
1404        ctx: impl Into<AccessContext>,
1405    ) -> Result<Vec<u16>, VmiError> {
1406        let buffer = Self::read_string32_in(vmi, ctx)?;
1407
1408        Ok(buffer
1409            .chunks_exact(2)
1410            .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
1411            .collect::<Vec<_>>())
1412    }
1413
1414    /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1415    ///
1416    /// This method is specifically for reading `_UNICODE_STRING` structures
1417    /// in 64-bit processes where pointers are 64 bits.
1418    pub fn read_unicode_string64_bytes_in(
1419        vmi: VmiState<Self>,
1420        ctx: impl Into<AccessContext>,
1421    ) -> Result<Vec<u16>, VmiError> {
1422        let buffer = Self::read_string64_in(vmi, ctx)?;
1423
1424        Ok(buffer
1425            .chunks_exact(2)
1426            .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
1427            .collect::<Vec<_>>())
1428    }
1429
1430    /// Reads string from a `_UNICODE_STRING` structure.
1431    ///
1432    /// This method reads a native `_UNICODE_STRING` structure which contains
1433    /// a UTF-16 string. The structure is read according to the current OS's
1434    /// architecture (32-bit or 64-bit).
1435    pub fn read_unicode_string_in(
1436        vmi: VmiState<Self>,
1437        ctx: impl Into<AccessContext>,
1438    ) -> Result<String, VmiError> {
1439        match vmi.registers().address_width() {
1440            4 => Self::read_unicode_string32_in(vmi, ctx),
1441            8 => Self::read_unicode_string64_in(vmi, ctx),
1442            _ => Err(VmiError::InvalidAddressWidth),
1443        }
1444    }
1445
1446    /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1447    ///
1448    /// This method is specifically for reading `_UNICODE_STRING` structures
1449    /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1450    pub fn read_unicode_string32_in(
1451        vmi: VmiState<Self>,
1452        ctx: impl Into<AccessContext>,
1453    ) -> Result<String, VmiError> {
1454        Ok(String::from_utf16_lossy(
1455            &Self::read_unicode_string32_bytes_in(vmi, ctx)?,
1456        ))
1457    }
1458
1459    /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1460    ///
1461    /// This method is specifically for reading `_UNICODE_STRING` structures
1462    /// in 64-bit processes where pointers are 64 bits.
1463    pub fn read_unicode_string64_in(
1464        vmi: VmiState<Self>,
1465        ctx: impl Into<AccessContext>,
1466    ) -> Result<String, VmiError> {
1467        Ok(String::from_utf16_lossy(
1468            &Self::read_unicode_string64_bytes_in(vmi, ctx)?,
1469        ))
1470    }
1471
1472    /// Returns an iterator over a doubly-linked list of `LIST_ENTRY` structures.
1473    ///
1474    /// This method is used to iterate over a doubly-linked list of `LIST_ENTRY`
1475    /// structures in memory. It returns an iterator that yields the virtual
1476    /// addresses of each `LIST_ENTRY` structure in the list.
1477    pub fn linked_list<'a>(
1478        vmi: VmiState<'a, Self>,
1479        list_head: Va,
1480        offset: u64,
1481    ) -> Result<impl Iterator<Item = Result<Va, VmiError>> + use<'a, Driver>, VmiError> {
1482        Ok(ListEntryIterator::new(vmi, list_head, offset))
1483    }
1484
1485    ///////////////////////////////////////////////////////////////////////////
1486    // VmiRead + VmiWrite
1487    ///////////////////////////////////////////////////////////////////////////
1488
1489    fn modify_pfn_reference_count(
1490        vmi: VmiState<Self>,
1491        pfn: Gfn,
1492        increment: i16,
1493    ) -> Result<Option<u16>, VmiError>
1494    where
1495        Driver: VmiWrite,
1496    {
1497        let MMPFN = offset!(vmi, _MMPFN);
1498
1499        // const ZeroedPageList: u16 = 0;
1500        // const FreePageList: u16 = 1;
1501        const StandbyPageList: u16 = 2; //this list and before make up available pages.
1502        const ModifiedPageList: u16 = 3;
1503        const ModifiedNoWritePageList: u16 = 4;
1504        // const BadPageList: u16 = 5;
1505        const ActiveAndValid: u16 = 6;
1506        // const TransitionPage: u16 = 7;
1507
1508        let pfn = Self::pfn_database(vmi)? + u64::from(pfn) * MMPFN.len() as u64;
1509
1510        //
1511        // In the _MMPFN structure, the fields are like this:
1512        //
1513        // ```c
1514        // struct _MMPFN {
1515        //     ...
1516        //     union {
1517        //         USHORT ReferenceCount;
1518        //         struct {
1519        //             UCHAR PageLocation : 3;
1520        //             ...
1521        //         } e1;
1522        //         ...
1523        //     } u3;
1524        // };
1525        // ```
1526        //
1527        // On the systems tested (Win7 - Win11), the `PageLocation` is right
1528        // after `ReferenceCount`. We can read the value of both fields at once.
1529        //
1530
1531        debug_assert_eq!(MMPFN.ReferenceCount.size(), 2);
1532        debug_assert_eq!(
1533            MMPFN.ReferenceCount.offset() + MMPFN.ReferenceCount.size(),
1534            MMPFN.PageLocation.offset()
1535        );
1536        debug_assert_eq!(MMPFN.PageLocation.bit_position(), 0);
1537        debug_assert_eq!(MMPFN.PageLocation.bit_length(), 3);
1538
1539        let pfn_value = vmi.read_u32(pfn + MMPFN.ReferenceCount.offset())?;
1540        let flags = (pfn_value >> 16) as u16;
1541        let ref_count = (pfn_value & 0xFFFF) as u16;
1542
1543        let page_location = flags & 7;
1544
1545        tracing::debug!(
1546            %pfn,
1547            ref_count,
1548            flags = %Hex(flags),
1549            page_location,
1550            increment,
1551            "modifying PFN reference count"
1552        );
1553
1554        //
1555        // Make sure the page is good (when coming from hibernate/standby pages
1556        // can be in modified state).
1557        //
1558
1559        if !matches!(
1560            page_location,
1561            StandbyPageList | ModifiedPageList | ModifiedNoWritePageList | ActiveAndValid
1562        ) {
1563            tracing::warn!(
1564                %pfn,
1565                ref_count,
1566                flags = %Hex(flags),
1567                page_location,
1568                increment,
1569                "page is not active and valid"
1570            );
1571            return Ok(None);
1572        }
1573
1574        if ref_count == 0 {
1575            tracing::warn!(
1576                %pfn,
1577                ref_count,
1578                flags = %Hex(flags),
1579                page_location,
1580                increment,
1581                "page is not initialized"
1582            );
1583            return Ok(None);
1584        }
1585
1586        let new_ref_count = match ref_count.checked_add_signed(increment) {
1587            Some(new_ref_count) => new_ref_count,
1588            None => {
1589                tracing::warn!(
1590                    %pfn,
1591                    ref_count,
1592                    flags = %Hex(flags),
1593                    page_location,
1594                    increment,
1595                    "page is at maximum reference count"
1596                );
1597                return Ok(None);
1598            }
1599        };
1600
1601        vmi.write_u16(pfn + MMPFN.ReferenceCount.offset(), new_ref_count)?;
1602
1603        Ok(Some(new_ref_count))
1604    }
1605
1606    /// Increments the reference count of a Page Frame Number (PFN).
1607    pub fn lock_pfn(vmi: VmiState<Self>, pfn: Gfn) -> Result<Option<u16>, VmiError>
1608    where
1609        Driver: VmiWrite,
1610    {
1611        Self::modify_pfn_reference_count(vmi, pfn, 1)
1612    }
1613
1614    /// Decrements the reference count of a Page Frame Number (PFN).
1615    pub fn unlock_pfn(vmi: VmiState<Self>, pfn: Gfn) -> Result<Option<u16>, VmiError>
1616    where
1617        Driver: VmiWrite,
1618    {
1619        Self::modify_pfn_reference_count(vmi, pfn, -1)
1620    }
1621}
1622
1623#[expect(non_snake_case)]
1624impl<Driver> VmiOs for WindowsOs<Driver>
1625where
1626    Driver: VmiRead,
1627    Driver::Architecture: ArchAdapter<Driver>,
1628{
1629    type Architecture = Driver::Architecture;
1630    type Driver = Driver;
1631
1632    type Process<'a> = WindowsProcess<'a, Driver>;
1633    type Thread<'a> = WindowsThread<'a, Driver>;
1634    type Image<'a> = WindowsImage<'a, Driver>;
1635    type Module<'a> = WindowsModule<'a, Driver>;
1636    type UserModule<'a> = WindowsUserModule<'a, Driver>;
1637    type Region<'a> = WindowsRegion<'a, Driver>;
1638    type Mapped<'a> = WindowsControlArea<'a, Driver>;
1639
1640    fn kernel_image_base(vmi: VmiState<Self>) -> Result<Va, VmiError> {
1641        Driver::Architecture::kernel_image_base(vmi)
1642    }
1643
1644    fn kernel_information_string(vmi: VmiState<Self>) -> Result<String, VmiError> {
1645        this!(vmi)
1646            .nt_build_lab
1647            .get_or_try_init(|| {
1648                let NtBuildLab = symbol!(vmi, NtBuildLab);
1649
1650                let kernel_image_base = Self::kernel_image_base(vmi)?;
1651                vmi.read_string(kernel_image_base + NtBuildLab)
1652            })
1653            .cloned()
1654    }
1655
1656    /// Checks if Kernel Virtual Address Shadow (KVA Shadow) is enabled.
1657    ///
1658    /// KVA Shadow is a security feature introduced in Windows 10 that
1659    /// mitigates Meltdown and Spectre vulnerabilities by isolating
1660    /// kernel memory from user-mode processes.
1661    ///
1662    /// # Notes
1663    ///
1664    /// This value is cached after the first read.
1665    ///
1666    /// # Implementation Details
1667    ///
1668    /// Corresponds to `KiKvaShadow` symbol.
1669    fn kpti_enabled(vmi: VmiState<Self>) -> Result<bool, VmiError> {
1670        this!(vmi)
1671            .ki_kva_shadow
1672            .get_or_try_init(|| {
1673                let KiKvaShadow = symbol!(vmi, KiKvaShadow);
1674
1675                let KiKvaShadow = match KiKvaShadow {
1676                    Some(KiKvaShadow) => KiKvaShadow,
1677                    None => return Ok(false),
1678                };
1679
1680                let kernel_image_base = Self::kernel_image_base(vmi)?;
1681                Ok(vmi.read_u8(kernel_image_base + KiKvaShadow)? != 0)
1682            })
1683            .copied()
1684    }
1685
1686    /// Returns an iterator over all loaded Windows Driver modules.
1687    ///
1688    /// This method returns an iterator over all loaded Windows Driver modules.
1689    /// It reads the `PsLoadedModuleList` symbol from the kernel image and
1690    /// iterates over the linked list of `KLDR_DATA_TABLE_ENTRY` structures
1691    /// representing each loaded module.
1692    fn modules<'a>(
1693        vmi: VmiState<'a, Self>,
1694    ) -> Result<impl Iterator<Item = Result<Self::Module<'a>, VmiError>> + use<'a, Driver>, VmiError>
1695    {
1696        let PsLoadedModuleList = Self::kernel_image_base(vmi)? + symbol!(vmi, PsLoadedModuleList);
1697        let KLDR_DATA_TABLE_ENTRY = offset!(vmi, _KLDR_DATA_TABLE_ENTRY);
1698
1699        Ok(ListEntryIterator::new(
1700            vmi,
1701            PsLoadedModuleList,
1702            KLDR_DATA_TABLE_ENTRY.InLoadOrderLinks.offset(),
1703        )
1704        .map(move |result| result.map(|entry| WindowsModule::new(vmi, entry))))
1705    }
1706
1707    /// Returns an iterator over all Windows processes.
1708    ///
1709    /// This method returns an iterator over all Windows processes. It reads the
1710    /// `PsActiveProcessHead` symbol from the kernel image and iterates over the
1711    /// linked list of `EPROCESS` structures representing each process.
1712    fn processes<'a>(
1713        vmi: VmiState<'a, Self>,
1714    ) -> Result<impl Iterator<Item = Result<Self::Process<'a>, VmiError>> + use<'a, Driver>, VmiError>
1715    {
1716        let PsActiveProcessHead = Self::kernel_image_base(vmi)? + symbol!(vmi, PsActiveProcessHead);
1717        let EPROCESS = offset!(vmi, _EPROCESS);
1718
1719        Ok(ListEntryIterator::new(
1720            vmi,
1721            PsActiveProcessHead,
1722            EPROCESS.ActiveProcessLinks.offset(),
1723        )
1724        .map(move |result| result.map(|entry| WindowsProcess::new(vmi, ProcessObject(entry)))))
1725    }
1726
1727    fn process(
1728        vmi: VmiState<'_, Self>,
1729        process: ProcessObject,
1730    ) -> Result<Self::Process<'_>, VmiError> {
1731        Ok(WindowsProcess::new(vmi, process))
1732    }
1733
1734    /// Returns the current process.
1735    fn current_process(vmi: VmiState<'_, Self>) -> Result<Self::Process<'_>, VmiError> {
1736        Self::current_thread(vmi)?.current_process()
1737    }
1738
1739    /// Returns the system process.
1740    ///
1741    /// The system process is the first process created by the Windows kernel
1742    /// during system initialization. It is the parent process of all other
1743    /// processes in the system.
1744    fn system_process(vmi: VmiState<'_, Self>) -> Result<Self::Process<'_>, VmiError> {
1745        let PsInitialSystemProcess =
1746            Self::kernel_image_base(vmi)? + symbol!(vmi, PsInitialSystemProcess);
1747
1748        let process = vmi.read_va_native(PsInitialSystemProcess)?;
1749        Ok(WindowsProcess::new(vmi, ProcessObject(process)))
1750    }
1751
1752    fn thread(vmi: VmiState<'_, Self>, thread: ThreadObject) -> Result<Self::Thread<'_>, VmiError> {
1753        Ok(WindowsThread::new(vmi, thread))
1754    }
1755
1756    /// Returns the current thread.
1757    fn current_thread(vmi: VmiState<'_, Self>) -> Result<Self::Thread<'_>, VmiError> {
1758        Self::current_kprcb(vmi)?.current_thread()
1759    }
1760
1761    fn image(vmi: VmiState<'_, Self>, image_base: Va) -> Result<Self::Image<'_>, VmiError> {
1762        Ok(WindowsImage::new(vmi, image_base))
1763    }
1764
1765    fn module(vmi: VmiState<'_, Self>, module: Va) -> Result<Self::Module<'_>, VmiError> {
1766        Ok(WindowsModule::new(vmi, module))
1767    }
1768
1769    fn user_module(
1770        vmi: VmiState<'_, Self>,
1771        module: Va,
1772        root: Pa,
1773    ) -> Result<Self::UserModule<'_>, VmiError> {
1774        Ok(WindowsUserModule::new(vmi, module, root))
1775    }
1776
1777    fn region(vmi: VmiState<'_, Self>, region: Va) -> Result<Self::Region<'_>, VmiError> {
1778        Ok(WindowsRegion::new(vmi, region))
1779    }
1780
1781    fn syscall_argument(vmi: VmiState<Self>, index: u64) -> Result<u64, VmiError> {
1782        Driver::Architecture::syscall_argument(vmi, index)
1783    }
1784
1785    fn function_argument(vmi: VmiState<Self>, index: u64) -> Result<u64, VmiError> {
1786        Driver::Architecture::function_argument(vmi, index)
1787    }
1788
1789    fn function_return_value(vmi: VmiState<Self>) -> Result<u64, VmiError> {
1790        Driver::Architecture::function_return_value(vmi)
1791    }
1792
1793    fn last_error(vmi: VmiState<Self>) -> Result<Option<u32>, VmiError> {
1794        let KTHREAD = offset!(vmi, _KTHREAD);
1795        let TEB = offset!(vmi, _TEB);
1796
1797        let current_thread = Self::current_thread(vmi)?.object()?;
1798        let teb = vmi.read_va_native(current_thread.0 + KTHREAD.Teb.offset())?;
1799
1800        if teb.is_null() {
1801            return Ok(None);
1802        }
1803
1804        let result = vmi.read_u32(teb + TEB.LastErrorValue.offset())?;
1805
1806        Ok(Some(result))
1807    }
1808}