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//! ## Example
27//!
28//! ```no_run
29//! # use vmi::{VmiCore, VmiDriver, os::windows::WindowsOs};
30//! #
31//! # fn example<Driver: VmiDriver>(
32//! # vmi: &VmiCore<Driver>,
33//! # _os: &WindowsOs<Driver>
34//! # ) -> Result<(), Box<dyn std::error::Error>> {
35//! let _guard = vmi.pause_guard()?;
36//! // Perform introspection operations here
37//! // VM automatically resumes when `_guard` goes out of scope
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! Always consider the potential for race conditions and ensure you're
43//! working with a consistent state of the guest OS.
44
45// Allow Windows-specific naming conventions to be used throughout this module.
46#![allow(
47 non_snake_case, // example: AlpcpSendMessage
48 non_upper_case_globals, // example: StandbyPageList
49)]
50
51use std::{cell::RefCell, collections::HashMap};
52
53use isr_core::Profile;
54use once_cell::unsync::OnceCell;
55use vmi_core::{
56 AccessContext, Architecture, Gfn, Hex, Registers as _, Va, VmiCore, VmiDriver, VmiError,
57 VmiState,
58 os::{ProcessObject, ThreadObject, VmiOs, VmiOsThread},
59};
60use vmi_macros::derive_trait_from_impl;
61use zerocopy::{FromBytes, IntoBytes};
62
63mod arch;
64use self::arch::ArchAdapter;
65
66mod error;
67pub use self::error::WindowsError;
68
69mod iter;
70pub use self::iter::{HandleTableEntryIterator, ListEntryIterator, TreeNodeIterator};
71
72pub mod pe;
73pub use self::pe::{CodeView, Pe, PeError};
74
75mod offsets;
76pub use self::offsets::{Offsets, OffsetsExt, Symbols}; // TODO: make private + remove offsets() & symbols() methods
77
78mod comps;
79pub use self::comps::{
80 ParseObjectTypeError, WindowsControlArea, WindowsDirectoryObject, WindowsFileObject,
81 WindowsHandleTable, WindowsHandleTableEntry, WindowsImage, WindowsModule, WindowsObject,
82 WindowsObjectAttributes, WindowsObjectHeaderNameInfo, WindowsObjectType, WindowsObjectTypeKind,
83 WindowsPeb, WindowsProcess, WindowsProcessParameters, WindowsRegion, WindowsSectionObject,
84 WindowsSession, WindowsThread, WindowsWow64Kind,
85};
86
87/// VMI operations for the Windows operating system.
88///
89/// `WindowsOs` provides methods and utilities for introspecting a Windows-based
90/// virtual machine. It encapsulates Windows-specific knowledge and operations,
91/// allowing for high-level interactions with the guest OS structures and processes.
92///
93/// # Usage
94///
95/// Create an instance of [`WindowsOs`] using a [`Profile`] that contains information
96/// about the specific Windows version being introspected:
97///
98/// ```no_run
99/// use isr::cache::{IsrCache, JsonCodec};
100/// use vmi::{VcpuId, VmiCore, VmiDriver, VmiError, os::windows::WindowsOs};
101///
102/// # fn example<Driver: VmiDriver>(
103/// # driver: Driver
104/// # ) -> Result<(), Box<dyn std::error::Error>>
105/// # where
106/// # Driver: VmiDriver<Architecture = vmi_arch_amd64::Amd64>,
107/// # {
108/// // Setup VMI.
109/// let core = VmiCore::new(driver)?;
110///
111/// // Try to find the kernel information.
112/// // This is necessary in order to load the profile.
113/// let kernel_info = {
114/// let _guard = core.pause_guard()?;
115/// let registers = core.registers(VcpuId(0))?;
116///
117/// WindowsOs::find_kernel(&core, ®isters)?.expect("kernel information")
118/// };
119///
120/// // Load the profile using the ISR library.
121/// let isr = IsrCache::<JsonCodec>::new("cache")?;
122/// let entry = isr.entry_from_codeview(kernel_info.codeview)?;
123/// let profile = entry.profile()?;
124///
125/// // Create a new `WindowsOs` instance.
126/// let os = WindowsOs::<Driver>::new(&profile)?;
127/// # Ok(())
128/// # }
129/// ```
130///
131/// # Important Notes
132///
133/// - Many methods of this struct require pausing the VM to ensure consistency.
134/// Use [`pause_guard`] when performing operations that might be affected
135/// by concurrent changes in the guest OS.
136///
137/// - The behavior and accuracy of some methods may vary depending on the
138/// Windows version being introspected. Always ensure your [`Profile`] matches
139/// the guest OS version.
140///
141/// # Examples
142///
143/// Retrieving information about the current process:
144///
145/// ```no_run
146/// # use vmi::{VcpuId, VmiDriver, VmiState, os::windows::WindowsOs};
147/// #
148/// # fn example<Driver: VmiDriver>(
149/// # vmi: &VmiState<Driver, WindowsOs<Driver>>,
150/// # ) -> Result<(), Box<dyn std::error::Error>>
151/// # where
152/// # Driver: VmiDriver<Architecture = vmi_arch_amd64::Amd64>,
153/// # {
154/// let process = vmi.os().current_process()?;
155/// let process_id = process.id()?;
156/// let process_name = process.name()?;
157/// println!("Current process: {} (PID: {})", process_name, process_id);
158/// # Ok(())
159/// # }
160/// ```
161///
162/// Enumerating all processes:
163///
164/// ```no_run
165/// # use vmi::{VcpuId, VmiDriver, VmiState, os::windows::WindowsOs};
166/// #
167/// # fn example<Driver: VmiDriver>(
168/// # vmi: &VmiState<Driver, WindowsOs<Driver>>,
169/// # ) -> Result<(), Box<dyn std::error::Error>>
170/// # where
171/// # Driver: VmiDriver<Architecture = vmi_arch_amd64::Amd64>,
172/// # {
173/// for process in vmi.os().processes()? {
174/// println!("Process: {} (PID: {})", process.name()?, process.id()?);
175/// }
176/// # Ok(())
177/// # }
178/// ```
179///
180/// # Safety
181///
182/// While this struct doesn't use unsafe code directly, many of its methods
183/// interact with raw memory of the guest OS. Incorrect usage can lead to
184/// invalid memory access or misinterpretation of data. Always ensure you're
185/// working with the correct memory regions and OS structures.
186///
187/// [`pause_guard`]: VmiCore::pause_guard
188pub struct WindowsOs<Driver>
189where
190 Driver: VmiDriver,
191{
192 offsets: Offsets,
193 symbols: Symbols,
194
195 kernel_image_base: OnceCell<Va>,
196 highest_user_address: OnceCell<Va>,
197 object_root_directory: OnceCell<Va>, // _OBJECT_DIRECTORY*
198 object_header_cookie: OnceCell<u8>,
199 object_type_cache: RefCell<HashMap<WindowsObjectTypeKind, Va>>,
200 object_type_rcache: RefCell<HashMap<Va, WindowsObjectTypeKind>>,
201 object_type_name_cache: RefCell<HashMap<Va, String>>,
202
203 ki_kva_shadow: OnceCell<bool>,
204 mm_pfn_database: OnceCell<Va>, // _MMPFN*
205 nt_build_lab: OnceCell<String>,
206 nt_build_lab_ex: OnceCell<String>,
207
208 _marker: std::marker::PhantomData<Driver>,
209}
210
211/// Information about the Windows kernel image.
212#[derive(Debug)]
213pub struct WindowsKernelInformation {
214 /// Base virtual address where the kernel image is loaded.
215 pub base_address: Va,
216
217 /// Major version number of the Windows kernel.
218 pub version_major: u16,
219
220 /// Minor version number of the Windows kernel.
221 pub version_minor: u16,
222
223 /// CodeView debugging information for the kernel image.
224 pub codeview: CodeView,
225}
226
227/// Represents a `_EXCEPTION_RECORD` structure.
228#[derive(Debug)]
229pub struct WindowsExceptionRecord {
230 /// The `ExceptionCode` field of the exception record.
231 ///
232 /// The reason the exception occurred. This is the code generated by a
233 /// hardware exception, or the code specified in the `RaiseException`
234 /// function for a software-generated exception.
235 pub code: u32,
236
237 /// The `ExceptionFlags` field of the exception record.
238 ///
239 /// This member contains zero or more exception flags.
240 pub flags: u32,
241
242 /// The `ExceptionRecord` field of the exception record.
243 ///
244 /// A pointer to an associated `EXCEPTION_RECORD` structure.
245 /// Exception records can be chained together to provide additional
246 /// information when nested exceptions occur.
247 pub record: Va,
248
249 /// The `ExceptionAddress` field of the exception record.
250 ///
251 /// The address where the exception occurred.
252 pub address: Va,
253
254 /// The `ExceptionInformation` field of the exception record.
255 ///
256 /// An array of additional arguments that describe the exception.
257 /// The number of elements in the array is determined by the `NumberParameters`
258 /// field of the exception record.
259 pub information: Vec<u64>,
260}
261
262macro_rules! offset {
263 ($vmi:expr, $field:ident) => {
264 &this!($vmi).offsets.$field
265 };
266}
267
268macro_rules! symbol {
269 ($vmi:expr, $field:ident) => {
270 this!($vmi).symbols.$field
271 };
272}
273
274macro_rules! this {
275 ($vmi:expr) => {
276 $vmi.underlying_os()
277 };
278}
279
280#[derive_trait_from_impl(WindowsOsExt)]
281#[expect(non_snake_case, non_upper_case_globals)]
282impl<Driver> WindowsOs<Driver>
283where
284 Driver: VmiDriver,
285 Driver::Architecture: Architecture + ArchAdapter<Driver>,
286{
287 /// 32-bit current process pseudo-handle (-1).
288 pub const NtCurrentProcess32: u64 = 0xffff_ffff;
289
290 /// 64-bit current process pseudo-handle (-1).
291 pub const NtCurrentProcess64: u64 = 0xffff_ffff_ffff_ffff;
292
293 /// 32-bit current thread pseudo-handle (-2).
294 pub const NtCurrentThread32: u64 = 0xffff_fffe;
295
296 /// 64-bit current thread pseudo-handle (-2).
297 pub const NtCurrentThread64: u64 = 0xffff_ffff_ffff_fffe;
298
299 /// Creates a new `WindowsOs` instance.
300 pub fn new(profile: &Profile) -> Result<Self, VmiError> {
301 Self::create(profile, OnceCell::new())
302 }
303
304 /// Creates a new `WindowsOs` instance with a known kernel base address.
305 pub fn with_kernel_base(profile: &Profile, kernel_base: Va) -> Result<Self, VmiError> {
306 Self::create(profile, OnceCell::with_value(kernel_base))
307 }
308
309 fn create(profile: &Profile, kernel_image_base: OnceCell<Va>) -> Result<Self, VmiError> {
310 Ok(Self {
311 offsets: Offsets::new(profile)?,
312 symbols: Symbols::new(profile)?,
313 kernel_image_base,
314 highest_user_address: OnceCell::new(),
315 object_root_directory: OnceCell::new(),
316 object_header_cookie: OnceCell::new(),
317 object_type_cache: RefCell::new(HashMap::new()),
318 object_type_rcache: RefCell::new(HashMap::new()),
319 object_type_name_cache: RefCell::new(HashMap::new()),
320 ki_kva_shadow: OnceCell::new(),
321 mm_pfn_database: OnceCell::new(),
322 nt_build_lab: OnceCell::new(),
323 nt_build_lab_ex: OnceCell::new(),
324 _marker: std::marker::PhantomData,
325 })
326 }
327
328 /// Returns a reference to the Windows-specific memory offsets.
329 pub fn offsets(vmi: VmiState<'_, Driver, Self>) -> &Offsets {
330 &this!(vmi).offsets
331 }
332
333 /// Returns a reference to the Windows-specific symbols.
334 pub fn symbols(vmi: VmiState<'_, Driver, Self>) -> &Symbols {
335 &this!(vmi).symbols
336 }
337
338 /// Locates the Windows kernel in memory based on the CPU registers.
339 /// This function is architecture-specific.
340 ///
341 /// On AMD64, the kernel is located by taking the `MSR_LSTAR` value and
342 /// reading the virtual memory page by page backwards until the `MZ` header
343 /// is found.
344 pub fn find_kernel(
345 vmi: &VmiCore<Driver>,
346 registers: &<Driver::Architecture as Architecture>::Registers,
347 ) -> Result<Option<WindowsKernelInformation>, VmiError> {
348 Driver::Architecture::find_kernel(vmi, registers)
349 }
350
351 /// Returns the kernel information string.
352 ///
353 /// # Notes
354 ///
355 /// The kernel information string is cached after the first read.
356 ///
357 /// # Implementation Details
358 ///
359 /// Corresponds to `NtBuildLab` symbol.
360 pub fn kernel_information_string_ex(
361 vmi: VmiState<Driver, Self>,
362 ) -> Result<Option<String>, VmiError> {
363 let NtBuildLabEx = match symbol!(vmi, NtBuildLabEx) {
364 Some(offset) => offset,
365 None => return Ok(None),
366 };
367
368 Ok(Some(
369 this!(vmi)
370 .nt_build_lab_ex
371 .get_or_try_init(|| {
372 let kernel_image_base = Self::kernel_image_base(vmi)?;
373 vmi.read_string(kernel_image_base + NtBuildLabEx)
374 })
375 .cloned()?,
376 ))
377 }
378
379 /// Checks if the given handle is a kernel handle.
380 ///
381 /// A kernel handle is a handle with the highest bit set.
382 pub fn is_kernel_handle(vmi: VmiState<Driver, Self>, handle: u64) -> Result<bool, VmiError> {
383 const KERNEL_HANDLE_MASK32: u64 = 0x8000_0000;
384 const KERNEL_HANDLE_MASK64: u64 = 0xffff_ffff_8000_0000;
385
386 match vmi.registers().address_width() {
387 4 => Ok(handle & KERNEL_HANDLE_MASK32 == KERNEL_HANDLE_MASK32),
388 8 => Ok(handle & KERNEL_HANDLE_MASK64 == KERNEL_HANDLE_MASK64),
389 _ => panic!("Unsupported address width"),
390 }
391 }
392
393 /// Returns the lowest user-mode address.
394 ///
395 /// This method returns a constant value (0x10000) representing the lowest
396 /// address that can be used by user-mode applications in Windows.
397 ///
398 /// # Notes
399 ///
400 /// * Windows creates a `NO_ACCESS` VAD (Virtual Address Descriptor) for the first 64KB
401 /// of virtual memory. This means the VA range 0-0x10000 is off-limits for usage.
402 /// * This behavior is consistent across all Windows versions from XP through
403 /// recent Windows 11, and applies to x86, x64, and ARM64 architectures.
404 /// * Many Windows APIs leverage this fact to determine whether an input argument
405 /// is a pointer or not. Here are two notable examples:
406 ///
407 /// 1. The `FindResource()` function accepts an `lpName` parameter of type `LPCTSTR`,
408 /// which can be either:
409 /// - A pointer to a valid string
410 /// - A value created by `MAKEINTRESOURCE(ID)`
411 ///
412 /// This allows `FindResource()` to accept `WORD` values (unsigned shorts) with
413 /// a maximum value of 0xFFFF, distinguishing them from valid memory addresses.
414 ///
415 /// 2. The `AddAtom()` function similarly accepts an `lpString` parameter of type `LPCTSTR`.
416 /// This parameter can be:
417 /// - A pointer to a null-terminated string (max 255 bytes)
418 /// - An integer atom converted using the `MAKEINTATOM(ID)` macro
419 ///
420 /// In both cases, the API can distinguish between valid pointers (which will be
421 /// above 0x10000) and integer values (which will be below 0x10000), allowing
422 /// for flexible parameter usage without ambiguity.
423 pub fn lowest_user_address(_vmi: VmiState<Driver, Self>) -> Result<Va, VmiError> {
424 Ok(Va(0x10000))
425 }
426
427 /// Returns the highest user-mode address.
428 ///
429 /// This method reads the highest user-mode address from the Windows kernel.
430 /// The value is cached after the first read for performance.
431 ///
432 /// # Notes
433 ///
434 /// This value is cached after the first read.
435 ///
436 /// # Implementation Details
437 ///
438 /// Corresponds to `MmHighestUserAddress` symbol.
439 pub fn highest_user_address(vmi: VmiState<Driver, Self>) -> Result<Va, VmiError> {
440 this!(vmi)
441 .highest_user_address
442 .get_or_try_init(|| {
443 let MmHighestUserAddress =
444 Self::kernel_image_base(vmi)? + symbol!(vmi, MmHighestUserAddress);
445
446 vmi.read_va_native(MmHighestUserAddress)
447 })
448 .copied()
449 }
450
451 /// Checks if a given address is a valid user-mode address.
452 ///
453 /// This method determines whether the provided address falls within
454 /// the range of valid user-mode addresses in Windows.
455 pub fn is_valid_user_address(
456 vmi: VmiState<Driver, Self>,
457 address: Va,
458 ) -> Result<bool, VmiError> {
459 let lowest_user_address = Self::lowest_user_address(vmi)?;
460 let highest_user_address = Self::highest_user_address(vmi)?;
461
462 Ok(address >= lowest_user_address && address <= highest_user_address)
463 }
464
465 /// Returns the virtual address of the current Kernel Processor Control
466 /// Region (KPCR).
467 ///
468 /// The KPCR is a per-processor data structure in Windows that contains
469 /// critical information about the current processor state. This method
470 /// returns the virtual address of the KPCR for the current processor.
471 pub fn current_kpcr(vmi: VmiState<Driver, Self>) -> Va {
472 Driver::Architecture::current_kpcr(vmi)
473 }
474
475 /// Returns information from an exception record at the specified address.
476 ///
477 /// This method reads and parses an `EXCEPTION_RECORD` structure from
478 /// memory, providing detailed information about an exception that has
479 /// occurred in the system. The returned [`WindowsExceptionRecord`]
480 /// contains data such as the exception code, flags, and related memory
481 /// addresses.
482 pub fn exception_record(
483 vmi: VmiState<Driver, Self>,
484 address: Va,
485 ) -> Result<WindowsExceptionRecord, VmiError> {
486 #[repr(C)]
487 #[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
488 #[allow(non_camel_case_types, non_snake_case)]
489 struct _EXCEPTION_RECORD {
490 ExceptionCode: u32,
491 ExceptionFlags: u32,
492 ExceptionRecord: u64,
493 ExceptionAddress: u64,
494 NumberParameters: u64,
495 ExceptionInformation: [u64; 15],
496 }
497
498 let record = vmi.read_struct::<_EXCEPTION_RECORD>(address)?;
499
500 Ok(WindowsExceptionRecord {
501 code: record.ExceptionCode,
502 flags: record.ExceptionFlags,
503 record: record.ExceptionRecord.into(),
504 address: record.ExceptionAddress.into(),
505 information: record.ExceptionInformation
506 [..u64::min(record.NumberParameters, 15) as usize]
507 .to_vec(),
508 })
509 }
510
511 /// Returns the last status value for the current thread.
512 ///
513 /// In Windows, the last status value is typically used to store error codes
514 /// or success indicators from system calls. This method reads this value
515 /// from the Thread Environment Block (TEB) of the current thread, providing
516 /// insight into the outcome of recent operations performed by the thread.
517 ///
518 /// Returns `None` if the TEB is not available.
519 ///
520 /// # Notes
521 ///
522 /// `LastStatusValue` is a `NTSTATUS` value, whereas `LastError` is a Win32
523 /// error code. The two values are related but not identical. You can obtain
524 /// the Win32 error code by calling
525 /// [`VmiOs::last_error`](crate::VmiOs::last_error).
526 ///
527 /// # Implementation Details
528 ///
529 /// Corresponds to `NtCurrentTeb()->LastStatusValue`.
530 pub fn last_status(vmi: VmiState<Driver, Self>) -> Result<Option<u32>, VmiError> {
531 let KTHREAD = offset!(vmi, _KTHREAD);
532 let TEB = offset!(vmi, _TEB);
533
534 let current_thread = Self::current_thread(vmi)?.object()?;
535 let teb = vmi.read_va_native(current_thread.0 + KTHREAD.Teb.offset())?;
536
537 if teb.is_null() {
538 return Ok(None);
539 }
540
541 let result = vmi.read_u32(teb + TEB.LastStatusValue.offset())?;
542 Ok(Some(result))
543 }
544
545 /// Returns the virtual address of the Page Frame Number (PFN) database.
546 ///
547 /// The PFN database is a critical data structure in Windows memory management,
548 /// containing information about each physical page in the system.
549 ///
550 /// # Notes
551 ///
552 /// This value is cached after the first read.
553 ///
554 /// # Implementation Details
555 ///
556 /// Corresponds to `MmPfnDatabase` symbol.
557 fn pfn_database(vmi: VmiState<Driver, Self>) -> Result<Va, VmiError> {
558 let MmPfnDatabase = symbol!(vmi, MmPfnDatabase);
559
560 this!(vmi)
561 .mm_pfn_database
562 .get_or_try_init(|| {
563 let kernel_image_base = Self::kernel_image_base(vmi)?;
564 vmi.read_va_native(kernel_image_base + MmPfnDatabase)
565 })
566 .copied()
567 }
568
569 fn modify_pfn_reference_count(
570 vmi: VmiState<Driver, Self>,
571 pfn: Gfn,
572 increment: i16,
573 ) -> Result<Option<u16>, VmiError> {
574 let MMPFN = offset!(vmi, _MMPFN);
575
576 // const ZeroedPageList: u16 = 0;
577 // const FreePageList: u16 = 1;
578 const StandbyPageList: u16 = 2; //this list and before make up available pages.
579 const ModifiedPageList: u16 = 3;
580 const ModifiedNoWritePageList: u16 = 4;
581 // const BadPageList: u16 = 5;
582 const ActiveAndValid: u16 = 6;
583 // const TransitionPage: u16 = 7;
584
585 let pfn = Self::pfn_database(vmi)? + u64::from(pfn) * MMPFN.len() as u64;
586
587 //
588 // In the _MMPFN structure, the fields are like this:
589 //
590 // ```c
591 // struct _MMPFN {
592 // ...
593 // union {
594 // USHORT ReferenceCount;
595 // struct {
596 // UCHAR PageLocation : 3;
597 // ...
598 // } e1;
599 // ...
600 // } u3;
601 // };
602 // ```
603 //
604 // On the systems tested (Win7 - Win11), the `PageLocation` is right
605 // after `ReferenceCount`. We can read the value of both fields at once.
606 //
607
608 debug_assert_eq!(MMPFN.ReferenceCount.size(), 2);
609 debug_assert_eq!(
610 MMPFN.ReferenceCount.offset() + MMPFN.ReferenceCount.size(),
611 MMPFN.PageLocation.offset()
612 );
613 debug_assert_eq!(MMPFN.PageLocation.bit_position(), 0);
614 debug_assert_eq!(MMPFN.PageLocation.bit_length(), 3);
615
616 let pfn_value = vmi.read_u32(pfn + MMPFN.ReferenceCount.offset())?;
617 let flags = (pfn_value >> 16) as u16;
618 let ref_count = (pfn_value & 0xFFFF) as u16;
619
620 let page_location = flags & 7;
621
622 tracing::debug!(
623 %pfn,
624 ref_count,
625 flags = %Hex(flags),
626 page_location,
627 increment,
628 "Modifying PFN reference count"
629 );
630
631 //
632 // Make sure the page is good (when coming from hibernate/standby pages
633 // can be in modified state).
634 //
635
636 if !matches!(
637 page_location,
638 StandbyPageList | ModifiedPageList | ModifiedNoWritePageList | ActiveAndValid
639 ) {
640 tracing::warn!(
641 %pfn,
642 ref_count,
643 flags = %Hex(flags),
644 page_location,
645 increment,
646 "Page is not active and valid"
647 );
648 return Ok(None);
649 }
650
651 if ref_count == 0 {
652 tracing::warn!(
653 %pfn,
654 ref_count,
655 flags = %Hex(flags),
656 page_location,
657 increment,
658 "Page is not initialized"
659 );
660 return Ok(None);
661 }
662
663 let new_ref_count = match ref_count.checked_add_signed(increment) {
664 Some(new_ref_count) => new_ref_count,
665 None => {
666 tracing::warn!(
667 %pfn,
668 ref_count,
669 flags = %Hex(flags),
670 page_location,
671 increment,
672 "Page is at maximum reference count"
673 );
674 return Ok(None);
675 }
676 };
677
678 vmi.write_u16(pfn + MMPFN.ReferenceCount.offset(), new_ref_count)?;
679
680 Ok(Some(new_ref_count))
681 }
682
683 /// Increments the reference count of a Page Frame Number (PFN).
684 ///
685 /// This method is used to "lock" a physical page by increasing its
686 /// reference count, preventing it from being paged out or reallocated.
687 ///
688 /// Returns the new reference count if successful, or `None` if the
689 /// operation failed (e.g., if the page is not in a valid state).
690 ///
691 /// # Implementation Details
692 ///
693 /// The method works by:
694 /// 1. Locating the `_MMPFN` structure for the given PFN within the `MmPfnDatabase`.
695 /// 2. Incrementing the `ReferenceCount` member of the `_MMPFN` structure.
696 ///
697 /// # Warning
698 ///
699 /// This function can potentially cause race conditions if the virtual machine
700 /// is not paused during its execution. It is strongly recommended to pause
701 /// the virtual machine before calling this function and resume it afterwards.
702 ///
703 /// Failure to pause the VM may result in inconsistent state or potential
704 /// crashes if the page is concurrently modified by the guest OS.
705 ///
706 /// # Examples:
707 ///
708 /// ```no_run
709 /// # use vmi::{
710 /// # arch::amd64::{Amd64, Registers},
711 /// # os::windows::WindowsOs,
712 /// # Architecture, Gfn, VmiCore, VmiDriver, VmiError,
713 /// # };
714 /// #
715 /// # fn example<Driver>(
716 /// # vmi: &VmiCore<Driver>,
717 /// # os: &WindowsOs<Driver>,
718 /// # registers: &Registers,
719 /// # pfn: Gfn,
720 /// # ) -> Result<(), VmiError>
721 /// # where
722 /// # Driver: VmiDriver<Architecture = Amd64>,
723 /// # {
724 /// let _pause_guard = vmi.pause_guard()?;
725 /// os.lock_pfn(pfn)?;
726 /// // The VM will automatically resume when `_guard` goes out of scope
727 /// # Ok(())
728 /// # }
729 /// ```
730 pub fn lock_pfn(vmi: VmiState<Driver, Self>, pfn: Gfn) -> Result<Option<u16>, VmiError> {
731 Self::modify_pfn_reference_count(vmi, pfn, 1)
732 }
733
734 /// Decrements the reference count of a Page Frame Number (PFN).
735 ///
736 /// This method is used to "unlock" a physical page by decreasing its
737 /// reference count, potentially allowing it to be paged out or reallocated
738 /// if the count reaches zero.
739 ///
740 /// Returns the new reference count if successful, or `None` if the
741 /// operation failed (e.g., if the page is not in a valid state).
742 ///
743 /// # Implementation Details
744 ///
745 /// The method works by:
746 /// 1. Locating the `_MMPFN` structure for the given PFN within the `MmPfnDatabase`.
747 /// 2. Decrementing the `ReferenceCount` member of the `_MMPFN` structure.
748 ///
749 /// # Warning
750 ///
751 /// This function can potentially cause race conditions if the virtual machine
752 /// is not paused during its execution. It is strongly recommended to pause
753 /// the virtual machine before calling this function and resume it afterwards.
754 ///
755 /// Failure to pause the VM may result in inconsistent state or potential
756 /// crashes if the page is concurrently modified by the guest OS.
757 ///
758 /// # Examples
759 ///
760 /// ```no_run
761 /// # use vmi_arch_amd64::{Amd64, Registers};
762 /// # use vmi_core::{Architecture, Gfn, VmiCore, VmiDriver, VmiError, VmiOs};
763 /// # use vmi_os_windows::WindowsOs;
764 /// #
765 /// # fn example<Driver>(
766 /// # vmi: &VmiCore<Driver>,
767 /// # os: &WindowsOs<Driver>,
768 /// # registers: &Registers,
769 /// # pfn: Gfn,
770 /// # ) -> Result<(), VmiError>
771 /// # where
772 /// # Driver: VmiDriver<Architecture = Amd64>,
773 /// # {
774 /// let _pause_guard = vmi.pause_guard()?;
775 /// os.unlock_pfn(pfn)?;
776 /// // The VM will automatically resume when `_guard` goes out of scope
777 /// # Ok(())
778 /// # }
779 /// ```
780 pub fn unlock_pfn(vmi: VmiState<Driver, Self>, pfn: Gfn) -> Result<Option<u16>, VmiError> {
781 Self::modify_pfn_reference_count(vmi, pfn, -1)
782 }
783
784 /// Returns the Windows object.
785 pub fn object<'a>(
786 vmi: VmiState<'a, Driver, Self>,
787 va: Va,
788 ) -> Result<WindowsObject<'a, Driver>, VmiError> {
789 Ok(WindowsObject::new(vmi, va))
790 }
791
792 /// Returns a Windows object type for the given object kind.
793 ///
794 /// # Notes
795 ///
796 /// The object type is cached after the first read.
797 ///
798 /// # Implementation Details
799 ///
800 /// - `File` corresponds to `IoFileObjectType`.
801 /// - `Job` corresponds to `PsJobType`.
802 /// - `Key` corresponds to `CmKeyObjectType`.
803 /// - `Process` corresponds to `PsProcessType`.
804 /// - `Thread` corresponds to `PsThreadType`.
805 /// - `Token` corresponds to `SeTokenObjectType`.
806 /// - Other types are not supported.
807 pub fn object_type<'a>(
808 vmi: VmiState<'a, Driver, Self>,
809 kind: WindowsObjectTypeKind,
810 ) -> Result<WindowsObjectType<'a, Driver>, VmiError> {
811 if let Some(va) = this!(vmi).object_type_cache.borrow().get(&kind).copied() {
812 return Ok(WindowsObjectType::new(vmi, va));
813 }
814
815 let symbol = match kind {
816 WindowsObjectTypeKind::File => symbol!(vmi, IoFileObjectType),
817 WindowsObjectTypeKind::Job => symbol!(vmi, PsJobType),
818 WindowsObjectTypeKind::Key => symbol!(vmi, CmKeyObjectType),
819 WindowsObjectTypeKind::Process => symbol!(vmi, PsProcessType),
820 WindowsObjectTypeKind::Thread => symbol!(vmi, PsThreadType),
821 WindowsObjectTypeKind::Token => symbol!(vmi, SeTokenObjectType),
822 _ => return Err(VmiError::NotSupported),
823 };
824
825 let va = vmi.read_va(Self::kernel_image_base(vmi)? + symbol)?;
826 this!(vmi).object_type_cache.borrow_mut().insert(kind, va);
827
828 Ok(WindowsObjectType::new(vmi, va))
829 }
830
831 /// Returns the root directory object for the Windows kernel.
832 ///
833 /// # Notes
834 ///
835 /// The object root directory is cached after the first read.
836 ///
837 /// # Implementation Details
838 ///
839 /// Corresponds to `ObpRootDirectoryObject` symbol.
840 pub fn object_root_directory<'a>(
841 vmi: VmiState<'a, Driver, Self>,
842 ) -> Result<WindowsDirectoryObject<'a, Driver>, VmiError> {
843 let object_root_directory = this!(vmi)
844 .object_root_directory
845 .get_or_try_init(|| {
846 let ObpRootDirectoryObject =
847 Self::kernel_image_base(vmi)? + symbol!(vmi, ObpRootDirectoryObject);
848
849 vmi.read_va_native(ObpRootDirectoryObject)
850 })
851 .copied()?;
852
853 Ok(WindowsDirectoryObject::new(vmi, object_root_directory))
854 }
855
856 /// Returns the object header cookie used for obfuscating object types.
857 /// Returns `None` if the cookie is not present in the kernel image.
858 ///
859 /// # Notes
860 ///
861 /// Windows 10 introduced a security feature that obfuscates the type
862 /// of kernel objects by XORing the `TypeIndex` field in the object header
863 /// with a random cookie value. This method fetches that cookie, which is
864 /// essential for correctly interpreting object headers in memory.
865 ///
866 /// The cookie is cached after the first read.
867 ///
868 /// # Implementation Details
869 ///
870 /// Corresponds to `ObHeaderCookie` symbol.
871 pub fn object_header_cookie(vmi: VmiState<Driver, Self>) -> Result<Option<u8>, VmiError> {
872 let ObHeaderCookie = match symbol!(vmi, ObHeaderCookie) {
873 Some(cookie) => cookie,
874 None => return Ok(None),
875 };
876
877 Ok(Some(
878 this!(vmi)
879 .object_header_cookie
880 .get_or_try_init(|| {
881 let kernel_image_base = Self::kernel_image_base(vmi)?;
882 vmi.read_u8(kernel_image_base + ObHeaderCookie)
883 })
884 .copied()?,
885 ))
886 }
887
888 /// Returns the Windows object attributes.
889 pub fn object_attributes<'a>(
890 vmi: VmiState<'a, Driver, Self>,
891 object_attributes: Va,
892 ) -> Result<WindowsObjectAttributes<'a, Driver>, VmiError> {
893 Ok(WindowsObjectAttributes::new(vmi, object_attributes))
894 }
895
896 /// Reads string of bytes from an `_ANSI_STRING` structure.
897 ///
898 /// This method reads a native `_ANSI_STRING` structure which contains
899 /// an ASCII/ANSI string. The structure is read according to the current
900 /// OS's architecture (32-bit or 64-bit).
901 pub fn read_ansi_string_bytes(
902 vmi: VmiState<Driver, Self>,
903 va: Va,
904 ) -> Result<Vec<u8>, VmiError> {
905 Self::read_ansi_string_bytes_in(vmi, vmi.access_context(va))
906 }
907
908 /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` structure.
909 ///
910 /// This method is specifically for reading `_ANSI_STRING` structures in
911 /// 32-bit processes or WoW64 processes where pointers are 32 bits.
912 pub fn read_ansi_string32_bytes(
913 vmi: VmiState<Driver, Self>,
914 va: Va,
915 ) -> Result<Vec<u8>, VmiError> {
916 Self::read_ansi_string32_bytes_in(vmi, vmi.access_context(va))
917 }
918
919 /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` structure.
920 ///
921 /// This method is specifically for reading `_ANSI_STRING` structures in
922 /// 64-bit processes where pointers are 64 bits.
923 pub fn read_ansi_string64_bytes(
924 vmi: VmiState<Driver, Self>,
925 va: Va,
926 ) -> Result<Vec<u8>, VmiError> {
927 Self::read_ansi_string64_bytes_in(vmi, vmi.access_context(va))
928 }
929
930 /// Reads string from an `_ANSI_STRING` structure.
931 ///
932 /// This method reads a native `_ANSI_STRING` structure which contains
933 /// an ASCII/ANSI string. The structure is read according to the current
934 /// OS's architecture (32-bit or 64-bit).
935 pub fn read_ansi_string(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
936 Self::read_ansi_string_in(vmi, vmi.access_context(va))
937 }
938
939 /// Reads string from a 32-bit version of `_ANSI_STRING` structure.
940 ///
941 /// This method is specifically for reading `_ANSI_STRING` structures in
942 /// 32-bit processes or WoW64 processes where pointers are 32 bits.
943 pub fn read_ansi_string32(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
944 Self::read_ansi_string32_in(vmi, vmi.access_context(va))
945 }
946
947 /// Reads string from a 64-bit version of `_ANSI_STRING` structure.
948 ///
949 /// This method is specifically for reading `_ANSI_STRING` structures in
950 /// 64-bit processes where pointers are 64 bits.
951 pub fn read_ansi_string64(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
952 Self::read_ansi_string64_in(vmi, vmi.access_context(va))
953 }
954
955 /// Reads string from a `_UNICODE_STRING` structure.
956 ///
957 /// This method reads a native `_UNICODE_STRING` structure which contains
958 /// a UTF-16 string. The structure is read according to the current OS's
959 /// architecture (32-bit or 64-bit).
960 pub fn read_unicode_string_bytes(
961 vmi: VmiState<Driver, Self>,
962 va: Va,
963 ) -> Result<Vec<u16>, VmiError> {
964 Self::read_unicode_string_bytes_in(vmi, vmi.access_context(va))
965 }
966
967 /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
968 ///
969 /// This method is specifically for reading `_UNICODE_STRING` structures
970 /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
971 pub fn read_unicode_string32_bytes(
972 vmi: VmiState<Driver, Self>,
973 va: Va,
974 ) -> Result<Vec<u16>, VmiError> {
975 Self::read_unicode_string32_bytes_in(vmi, vmi.access_context(va))
976 }
977
978 /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
979 ///
980 /// This method is specifically for reading `_UNICODE_STRING` structures
981 /// in 64-bit processes where pointers are 64 bits.
982 pub fn read_unicode_string64_bytes(
983 vmi: VmiState<Driver, Self>,
984 va: Va,
985 ) -> Result<Vec<u16>, VmiError> {
986 Self::read_unicode_string64_bytes_in(vmi, vmi.access_context(va))
987 }
988
989 /// Reads string from a `_UNICODE_STRING` structure.
990 ///
991 /// This method reads a native `_UNICODE_STRING` structure which contains
992 /// a UTF-16 string. The structure is read according to the current OS's
993 /// architecture (32-bit or 64-bit).
994 pub fn read_unicode_string(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
995 Self::read_unicode_string_in(vmi, vmi.access_context(va))
996 }
997
998 /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
999 ///
1000 /// This method is specifically for reading `_UNICODE_STRING` structures
1001 /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1002 pub fn read_unicode_string32(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
1003 Self::read_unicode_string32_in(vmi, vmi.access_context(va))
1004 }
1005
1006 /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1007 ///
1008 /// This method is specifically for reading `_UNICODE_STRING` structures
1009 /// in 64-bit processes where pointers are 64 bits.
1010 pub fn read_unicode_string64(vmi: VmiState<Driver, Self>, va: Va) -> Result<String, VmiError> {
1011 Self::read_unicode_string64_in(vmi, vmi.access_context(va))
1012 }
1013
1014 /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` or
1015 /// `_UNICODE_STRING` structure.
1016 ///
1017 /// This method is specifically for reading `_ANSI_STRING` or
1018 /// `_UNICODE_STRING` structures in 32-bit processes or WoW64 processes
1019 /// where pointers are 32 bits.
1020 fn read_string32_in(
1021 vmi: VmiState<Driver, Self>,
1022 ctx: impl Into<AccessContext>,
1023 ) -> Result<Vec<u8>, VmiError> {
1024 let mut ctx = ctx.into();
1025
1026 let mut buffer = [0u8; 8];
1027 vmi.read_in(ctx, &mut buffer)?;
1028
1029 let string_length = u16::from_le_bytes([buffer[0], buffer[1]]);
1030
1031 if string_length == 0 {
1032 return Ok(Vec::new());
1033 }
1034
1035 // let string_maximum_length = u16::from_le_bytes([buffer[2], buffer[3]]);
1036 let string_buffer = u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
1037
1038 if string_buffer == 0 {
1039 tracing::warn!(
1040 addr = %Hex(ctx.address),
1041 len = string_length,
1042 "String buffer is NULL"
1043 );
1044
1045 return Ok(Vec::new());
1046 }
1047
1048 ctx.address = string_buffer as u64;
1049
1050 let mut buffer = vec![0u8; string_length as usize];
1051 vmi.read_in(ctx, &mut buffer)?;
1052
1053 Ok(buffer)
1054 }
1055
1056 /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` or
1057 /// `_UNICODE_STRING` structure.
1058 ///
1059 /// This method is specifically for reading `_ANSI_STRING` or
1060 /// `_UNICODE_STRING` structures in 64-bit processes where pointers
1061 /// are 64 bits.
1062 fn read_string64_in(
1063 vmi: VmiState<Driver, Self>,
1064 ctx: impl Into<AccessContext>,
1065 ) -> Result<Vec<u8>, VmiError> {
1066 let mut ctx = ctx.into();
1067
1068 let mut buffer = [0u8; 16];
1069 vmi.read_in(ctx, &mut buffer)?;
1070
1071 let string_length = u16::from_le_bytes([buffer[0], buffer[1]]);
1072
1073 if string_length == 0 {
1074 return Ok(Vec::new());
1075 }
1076
1077 // let string_maximum_length = u16::from_le_bytes([buffer[2], buffer[3]]);
1078 let string_buffer = u64::from_le_bytes([
1079 buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
1080 buffer[15],
1081 ]);
1082
1083 if string_buffer == 0 {
1084 tracing::warn!(
1085 addr = %Hex(ctx.address),
1086 len = string_length,
1087 "String buffer is NULL"
1088 );
1089
1090 return Ok(Vec::new());
1091 }
1092
1093 ctx.address = string_buffer;
1094
1095 let mut buffer = vec![0u8; string_length as usize];
1096 vmi.read_in(ctx, &mut buffer)?;
1097
1098 Ok(buffer)
1099 }
1100
1101 /// Reads string of bytes from an `_ANSI_STRING` structure.
1102 ///
1103 /// This method reads a native `_ANSI_STRING` structure which contains
1104 /// an ASCII/ANSI string. The structure is read according to the current
1105 /// OS's architecture (32-bit or 64-bit).
1106 pub fn read_ansi_string_bytes_in(
1107 vmi: VmiState<Driver, Self>,
1108 ctx: impl Into<AccessContext>,
1109 ) -> Result<Vec<u8>, VmiError> {
1110 match vmi.registers().address_width() {
1111 4 => Self::read_ansi_string32_bytes_in(vmi, ctx),
1112 8 => Self::read_ansi_string64_bytes_in(vmi, ctx),
1113 _ => panic!("Unsupported address width"),
1114 }
1115 }
1116
1117 /// Reads string of bytes from a 32-bit version of `_ANSI_STRING` structure.
1118 ///
1119 /// This method is specifically for reading `_ANSI_STRING` structures in
1120 /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1121 pub fn read_ansi_string32_bytes_in(
1122 vmi: VmiState<Driver, Self>,
1123 ctx: impl Into<AccessContext>,
1124 ) -> Result<Vec<u8>, VmiError> {
1125 Self::read_string32_in(vmi, ctx)
1126 }
1127
1128 /// Reads string of bytes from a 64-bit version of `_ANSI_STRING` structure.
1129 ///
1130 /// This method is specifically for reading `_ANSI_STRING` structures in
1131 /// 64-bit processes where pointers are 64 bits.
1132 pub fn read_ansi_string64_bytes_in(
1133 vmi: VmiState<Driver, Self>,
1134 ctx: impl Into<AccessContext>,
1135 ) -> Result<Vec<u8>, VmiError> {
1136 Self::read_string64_in(vmi, ctx)
1137 }
1138
1139 /// Reads string from an `_ANSI_STRING` structure.
1140 ///
1141 /// This method reads a native `_ANSI_STRING` structure which contains
1142 /// an ASCII/ANSI string. The structure is read according to the current
1143 /// OS's architecture (32-bit or 64-bit).
1144 pub fn read_ansi_string_in(
1145 vmi: VmiState<Driver, Self>,
1146 ctx: impl Into<AccessContext>,
1147 ) -> Result<String, VmiError> {
1148 match vmi.registers().address_width() {
1149 4 => Self::read_ansi_string32_in(vmi, ctx),
1150 8 => Self::read_ansi_string64_in(vmi, ctx),
1151 _ => panic!("Unsupported address width"),
1152 }
1153 }
1154
1155 /// Reads string from a 32-bit version of `_ANSI_STRING` structure.
1156 ///
1157 /// This method is specifically for reading `_ANSI_STRING` structures in
1158 /// 32-bit processes or WoW64 processes where pointers are 32 bits.
1159 pub fn read_ansi_string32_in(
1160 vmi: VmiState<Driver, Self>,
1161 ctx: impl Into<AccessContext>,
1162 ) -> Result<String, VmiError> {
1163 Ok(String::from_utf8_lossy(&Self::read_ansi_string32_bytes_in(vmi, ctx)?).into())
1164 }
1165
1166 /// Reads string from a 64-bit version of `_ANSI_STRING` structure.
1167 ///
1168 /// This method is specifically for reading `_ANSI_STRING` structures in
1169 /// 64-bit processes where pointers are 64 bits.
1170 pub fn read_ansi_string64_in(
1171 vmi: VmiState<Driver, Self>,
1172 ctx: impl Into<AccessContext>,
1173 ) -> Result<String, VmiError> {
1174 Ok(String::from_utf8_lossy(&Self::read_ansi_string64_bytes_in(vmi, ctx)?).into())
1175 }
1176
1177 /// Reads string from a `_UNICODE_STRING` structure.
1178 ///
1179 /// This method reads a native `_UNICODE_STRING` structure which contains
1180 /// a UTF-16 string. The structure is read according to the current OS's
1181 /// architecture (32-bit or 64-bit).
1182 pub fn read_unicode_string_bytes_in(
1183 vmi: VmiState<Driver, Self>,
1184 ctx: impl Into<AccessContext>,
1185 ) -> Result<Vec<u16>, VmiError> {
1186 match vmi.registers().address_width() {
1187 4 => Self::read_unicode_string32_bytes_in(vmi, ctx),
1188 8 => Self::read_unicode_string64_bytes_in(vmi, ctx),
1189 _ => panic!("Unsupported address width"),
1190 }
1191 }
1192
1193 /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1194 ///
1195 /// This method is specifically for reading `_UNICODE_STRING` structures
1196 /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1197 pub fn read_unicode_string32_bytes_in(
1198 vmi: VmiState<Driver, Self>,
1199 ctx: impl Into<AccessContext>,
1200 ) -> Result<Vec<u16>, VmiError> {
1201 let buffer = Self::read_string32_in(vmi, ctx)?;
1202
1203 Ok(buffer
1204 .chunks_exact(2)
1205 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
1206 .collect::<Vec<_>>())
1207 }
1208
1209 /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1210 ///
1211 /// This method is specifically for reading `_UNICODE_STRING` structures
1212 /// in 64-bit processes where pointers are 64 bits.
1213 pub fn read_unicode_string64_bytes_in(
1214 vmi: VmiState<Driver, Self>,
1215 ctx: impl Into<AccessContext>,
1216 ) -> Result<Vec<u16>, VmiError> {
1217 let buffer = Self::read_string64_in(vmi, ctx)?;
1218
1219 Ok(buffer
1220 .chunks_exact(2)
1221 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
1222 .collect::<Vec<_>>())
1223 }
1224
1225 /// Reads string from a `_UNICODE_STRING` structure.
1226 ///
1227 /// This method reads a native `_UNICODE_STRING` structure which contains
1228 /// a UTF-16 string. The structure is read according to the current OS's
1229 /// architecture (32-bit or 64-bit).
1230 pub fn read_unicode_string_in(
1231 vmi: VmiState<Driver, Self>,
1232 ctx: impl Into<AccessContext>,
1233 ) -> Result<String, VmiError> {
1234 match vmi.registers().address_width() {
1235 4 => Self::read_unicode_string32_in(vmi, ctx),
1236 8 => Self::read_unicode_string64_in(vmi, ctx),
1237 _ => panic!("Unsupported address width"),
1238 }
1239 }
1240
1241 /// Reads string from a 32-bit version of `_UNICODE_STRING` structure.
1242 ///
1243 /// This method is specifically for reading `_UNICODE_STRING` structures
1244 /// in 32-bit processes or WoW64 processes where pointers are 32 bits.
1245 pub fn read_unicode_string32_in(
1246 vmi: VmiState<Driver, Self>,
1247 ctx: impl Into<AccessContext>,
1248 ) -> Result<String, VmiError> {
1249 Ok(String::from_utf16_lossy(
1250 &Self::read_unicode_string32_bytes_in(vmi, ctx)?,
1251 ))
1252 }
1253
1254 /// Reads string from a 64-bit version of `_UNICODE_STRING` structure.
1255 ///
1256 /// This method is specifically for reading `_UNICODE_STRING` structures
1257 /// in 64-bit processes where pointers are 64 bits.
1258 pub fn read_unicode_string64_in(
1259 vmi: VmiState<Driver, Self>,
1260 ctx: impl Into<AccessContext>,
1261 ) -> Result<String, VmiError> {
1262 Ok(String::from_utf16_lossy(
1263 &Self::read_unicode_string64_bytes_in(vmi, ctx)?,
1264 ))
1265 }
1266
1267 /// Returns an iterator over a doubly-linked list of `LIST_ENTRY` structures.
1268 ///
1269 /// This method is used to iterate over a doubly-linked list of `LIST_ENTRY`
1270 /// structures in memory. It returns an iterator that yields the virtual
1271 /// addresses of each `LIST_ENTRY` structure in the list.
1272 pub fn linked_list<'a>(
1273 vmi: VmiState<'a, Driver, Self>,
1274 list_head: Va,
1275 offset: u64,
1276 ) -> Result<impl Iterator<Item = Result<Va, VmiError>> + 'a, VmiError> {
1277 Ok(ListEntryIterator::new(vmi, list_head, offset))
1278 }
1279}
1280
1281#[expect(non_snake_case)]
1282impl<Driver> VmiOs<Driver> for WindowsOs<Driver>
1283where
1284 Driver: VmiDriver,
1285 Driver::Architecture: Architecture + ArchAdapter<Driver>,
1286{
1287 type Process<'a> = WindowsProcess<'a, Driver>;
1288 type Thread<'a> = WindowsThread<'a, Driver>;
1289 type Image<'a> = WindowsImage<'a, Driver>;
1290 type Module<'a> = WindowsModule<'a, Driver>;
1291 type Region<'a> = WindowsRegion<'a, Driver>;
1292 type Mapped<'a> = WindowsControlArea<'a, Driver>;
1293
1294 fn kernel_image_base(vmi: VmiState<Driver, Self>) -> Result<Va, VmiError> {
1295 Driver::Architecture::kernel_image_base(vmi)
1296 }
1297
1298 fn kernel_information_string(vmi: VmiState<Driver, Self>) -> Result<String, VmiError> {
1299 this!(vmi)
1300 .nt_build_lab
1301 .get_or_try_init(|| {
1302 let NtBuildLab = symbol!(vmi, NtBuildLab);
1303
1304 let kernel_image_base = Self::kernel_image_base(vmi)?;
1305 vmi.read_string(kernel_image_base + NtBuildLab)
1306 })
1307 .cloned()
1308 }
1309
1310 /// Checks if Kernel Virtual Address Shadow (KVA Shadow) is enabled.
1311 ///
1312 /// KVA Shadow is a security feature introduced in Windows 10 that
1313 /// mitigates Meltdown and Spectre vulnerabilities by isolating
1314 /// kernel memory from user-mode processes.
1315 ///
1316 /// # Notes
1317 ///
1318 /// This value is cached after the first read.
1319 ///
1320 /// # Implementation Details
1321 ///
1322 /// Corresponds to `KiKvaShadow` symbol.
1323 fn kpti_enabled(vmi: VmiState<Driver, Self>) -> Result<bool, VmiError> {
1324 this!(vmi)
1325 .ki_kva_shadow
1326 .get_or_try_init(|| {
1327 let KiKvaShadow = symbol!(vmi, KiKvaShadow);
1328
1329 let KiKvaShadow = match KiKvaShadow {
1330 Some(KiKvaShadow) => KiKvaShadow,
1331 None => return Ok(false),
1332 };
1333
1334 let kernel_image_base = Self::kernel_image_base(vmi)?;
1335 Ok(vmi.read_u8(kernel_image_base + KiKvaShadow)? != 0)
1336 })
1337 .copied()
1338 }
1339
1340 /// Returns an iterator over all loaded Windows Driver modules.
1341 ///
1342 /// This method returns an iterator over all loaded Windows Driver modules.
1343 /// It reads the `PsLoadedModuleList` symbol from the kernel image and
1344 /// iterates over the linked list of `KLDR_DATA_TABLE_ENTRY` structures
1345 /// representing each loaded module.
1346 fn modules(
1347 vmi: VmiState<'_, Driver, Self>,
1348 ) -> Result<impl Iterator<Item = Result<Self::Module<'_>, VmiError>> + '_, VmiError> {
1349 let PsLoadedModuleList = Self::kernel_image_base(vmi)? + symbol!(vmi, PsLoadedModuleList);
1350 let KLDR_DATA_TABLE_ENTRY = offset!(vmi, _KLDR_DATA_TABLE_ENTRY);
1351
1352 Ok(ListEntryIterator::new(
1353 vmi,
1354 PsLoadedModuleList,
1355 KLDR_DATA_TABLE_ENTRY.InLoadOrderLinks.offset(),
1356 )
1357 .map(move |result| result.map(|entry| WindowsModule::new(vmi, entry))))
1358 }
1359
1360 /// Returns an iterator over all Windows processes.
1361 ///
1362 /// This method returns an iterator over all Windows processes. It reads the
1363 /// `PsActiveProcessHead` symbol from the kernel image and iterates over the
1364 /// linked list of `EPROCESS` structures representing each process.
1365 fn processes(
1366 vmi: VmiState<'_, Driver, Self>,
1367 ) -> Result<impl Iterator<Item = Result<Self::Process<'_>, VmiError>> + '_, VmiError> {
1368 let PsActiveProcessHead = Self::kernel_image_base(vmi)? + symbol!(vmi, PsActiveProcessHead);
1369 let EPROCESS = offset!(vmi, _EPROCESS);
1370
1371 Ok(ListEntryIterator::new(
1372 vmi,
1373 PsActiveProcessHead,
1374 EPROCESS.ActiveProcessLinks.offset(),
1375 )
1376 .map(move |result| result.map(|entry| WindowsProcess::new(vmi, ProcessObject(entry)))))
1377 }
1378
1379 fn process(
1380 vmi: VmiState<'_, Driver, Self>,
1381 process: ProcessObject,
1382 ) -> Result<Self::Process<'_>, VmiError> {
1383 Ok(WindowsProcess::new(vmi, process))
1384 }
1385
1386 /// Returns the current process.
1387 fn current_process(vmi: VmiState<'_, Driver, Self>) -> Result<Self::Process<'_>, VmiError> {
1388 Self::current_thread(vmi)?.attached_process()
1389 }
1390
1391 /// Returns the system process.
1392 ///
1393 /// The system process is the first process created by the Windows kernel
1394 /// during system initialization. It is the parent process of all other
1395 /// processes in the system.
1396 fn system_process(vmi: VmiState<'_, Driver, Self>) -> Result<Self::Process<'_>, VmiError> {
1397 let PsInitialSystemProcess =
1398 Self::kernel_image_base(vmi)? + symbol!(vmi, PsInitialSystemProcess);
1399
1400 let process = vmi.read_va_native(PsInitialSystemProcess)?;
1401 Ok(WindowsProcess::new(vmi, ProcessObject(process)))
1402 }
1403
1404 fn thread(
1405 vmi: VmiState<'_, Driver, Self>,
1406 thread: ThreadObject,
1407 ) -> Result<Self::Thread<'_>, VmiError> {
1408 Ok(WindowsThread::new(vmi, thread))
1409 }
1410
1411 /// Returns the current thread.
1412 fn current_thread(vmi: VmiState<'_, Driver, Self>) -> Result<Self::Thread<'_>, VmiError> {
1413 let KPCR = offset!(vmi, _KPCR);
1414 let KPRCB = offset!(vmi, _KPRCB);
1415
1416 let kpcr = Self::current_kpcr(vmi);
1417
1418 if kpcr.is_null() {
1419 return Err(WindowsError::CorruptedStruct("KPCR").into());
1420 }
1421
1422 let addr = kpcr + KPCR.Prcb.offset() + KPRCB.CurrentThread.offset();
1423 let result = vmi.read_va_native(addr)?;
1424
1425 if result.is_null() {
1426 return Err(WindowsError::CorruptedStruct("KPCR.Prcb.CurrentThread").into());
1427 }
1428
1429 Ok(WindowsThread::new(vmi, ThreadObject(result)))
1430 }
1431
1432 fn image(vmi: VmiState<'_, Driver, Self>, image_base: Va) -> Result<Self::Image<'_>, VmiError> {
1433 Ok(WindowsImage::new(vmi, image_base))
1434 }
1435
1436 fn module(vmi: VmiState<'_, Driver, Self>, module: Va) -> Result<Self::Module<'_>, VmiError> {
1437 Ok(WindowsModule::new(vmi, module))
1438 }
1439
1440 fn region(vmi: VmiState<'_, Driver, Self>, region: Va) -> Result<Self::Region<'_>, VmiError> {
1441 Ok(WindowsRegion::new(vmi, region))
1442 }
1443
1444 fn syscall_argument(vmi: VmiState<Driver, Self>, index: u64) -> Result<u64, VmiError> {
1445 Driver::Architecture::syscall_argument(vmi, index)
1446 }
1447
1448 fn function_argument(vmi: VmiState<Driver, Self>, index: u64) -> Result<u64, VmiError> {
1449 Driver::Architecture::function_argument(vmi, index)
1450 }
1451
1452 fn function_return_value(vmi: VmiState<Driver, Self>) -> Result<u64, VmiError> {
1453 Driver::Architecture::function_return_value(vmi)
1454 }
1455
1456 fn last_error(vmi: VmiState<Driver, Self>) -> Result<Option<u32>, VmiError> {
1457 let KTHREAD = offset!(vmi, _KTHREAD);
1458 let TEB = offset!(vmi, _TEB);
1459
1460 let current_thread = Self::current_thread(vmi)?.object()?;
1461 let teb = vmi.read_va_native(current_thread.0 + KTHREAD.Teb.offset())?;
1462
1463 if teb.is_null() {
1464 return Ok(None);
1465 }
1466
1467 let result = vmi.read_u32(teb + TEB.LastErrorValue.offset())?;
1468
1469 Ok(Some(result))
1470 }
1471}