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, ®isters)?.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}