Skip to main content

vmi_os_windows/arch/
mod.rs

1mod amd64;
2mod context;
3
4use vmi_core::{
5    AccessContext, Architecture, Pa, Va, VmiCore, VmiError, VmiState, driver::VmiRead,
6    os::VmiOsImage as _,
7};
8
9pub use self::{
10    amd64::{WindowsExceptionVector, WindowsInterrupt, WindowsPageTableEntry},
11    context::{
12        CONTEXT_AMD64, CONTEXT_X86, FLOATING_SAVE_AREA, KDESCRIPTOR_AMD64, KDESCRIPTOR_X86,
13        KSPECIAL_REGISTERS_AMD64, KSPECIAL_REGISTERS_X86, M128A, MAXIMUM_SUPPORTED_EXTENSION,
14        SIZE_OF_80387_REGISTERS, WindowsContext, WindowsRegistersAdapter, WindowsSpecialRegisters,
15        XSAVE_FORMAT,
16    },
17};
18use crate::{PeImage as _, WindowsImage, WindowsKernelInformation, WindowsOs, WindowsOsExt};
19
20/// Architecture-specific Windows functionality.
21pub trait ArchAdapter<Driver>: Architecture
22where
23    Driver: VmiRead<Architecture = Self>,
24{
25    /// Locates the Windows kernel image by scanning backward from the
26    /// syscall entry point.
27    ///
28    /// Returns the kernel's base address, OS version, and CodeView debug
29    /// information if found.
30    ///
31    /// # Architecture-specific
32    ///
33    /// - **AMD64**: Scans backward from `MSR_LSTAR` (up to 32 MB)
34    fn find_kernel(
35        vmi: &VmiCore<Driver>,
36        registers: &<Driver::Architecture as Architecture>::Registers,
37    ) -> Result<Option<WindowsKernelInformation>, VmiError>;
38
39    /// Reads a syscall argument by index from the current register state.
40    ///
41    /// Index 0 is the first argument.
42    ///
43    /// # Architecture-specific
44    ///
45    /// - **AMD64**: Arguments 0-3 come from registers (`R10`, `RDX`, `R8`,
46    ///   `R9`); subsequent arguments are read from the stack.
47    fn syscall_argument(vmi: VmiState<WindowsOs<Driver>>, index: u64) -> Result<u64, VmiError>;
48
49    /// Reads a function-call argument by index from the current register state.
50    ///
51    /// Unlike [`syscall_argument`](Self::syscall_argument), this follows the
52    /// standard calling convention.
53    ///
54    /// # Architecture-specific
55    ///
56    /// - **AMD64**: Microsoft x64 calling convention (`RCX`, `RDX`, `R8`,
57    ///   `R9`) in long mode, stdcall (stack-based) in compatibility mode.
58    fn function_argument(vmi: VmiState<WindowsOs<Driver>>, index: u64) -> Result<u64, VmiError>;
59
60    /// Reads the return value of the most recent function call.
61    ///
62    /// # Architecture-specific
63    ///
64    /// - **AMD64**: `RAX`
65    fn function_return_value(vmi: VmiState<WindowsOs<Driver>>) -> Result<u64, VmiError>;
66
67    /// Returns the kernel image base address, caching the result for
68    /// subsequent calls.
69    ///
70    /// # Architecture-specific
71    ///
72    /// - **AMD64**: `MSR_LSTAR - KiSystemCall64`
73    fn kernel_image_base(vmi: VmiState<WindowsOs<Driver>>) -> Result<Va, VmiError>;
74
75    /// Checks whether a virtual address maps to a page that is either
76    /// present or in the Windows transition state (soft fault, still
77    /// resident in physical memory).
78    fn is_page_present_or_transition(
79        vmi: VmiState<WindowsOs<Driver>>,
80        address: Va,
81    ) -> Result<bool, VmiError>;
82
83    /// Returns the virtual address of the Kernel Processor Control Region
84    /// (KPCR) for the current CPU.
85    fn current_kpcr(vmi: VmiState<WindowsOs<Driver>>) -> Va;
86
87    /// Converts a Windows `DirectoryTableBase` value to the page-table root
88    /// physical address.
89    ///
90    /// # Architecture-specific
91    ///
92    /// - **AMD64**: masks and shifts the page-frame-number bits of `CR3`.
93    fn dtb_to_root(value: u64) -> Pa;
94}
95
96/// Pointer-width-dependent operations for reading Windows structures.
97///
98/// Windows structures contain pointer-sized fields (`PVOID`, `UNICODE_STRING`,
99/// `LIST_ENTRY`, ...) whose layout differs between 32-bit and 64-bit processes.
100/// This trait abstracts over the pointer width so that structure accessors can
101/// be generic over both layouts.
102pub trait StructLayout {
103    /// The address width (i.e. pointer size) in bytes.
104    const ADDRESS_WIDTH: u64;
105
106    /// Reads a pointer-sized virtual address from guest memory.
107    fn read_va<Driver>(
108        vmi: VmiState<WindowsOs<Driver>>,
109        ctx: impl Into<AccessContext>,
110    ) -> Result<Va, VmiError>
111    where
112        Driver: VmiRead,
113        Driver::Architecture: ArchAdapter<Driver>;
114
115    /// Reads a `UNICODE_STRING` from guest memory.
116    fn read_unicode_string<Driver>(
117        vmi: VmiState<WindowsOs<Driver>>,
118        ctx: impl Into<AccessContext>,
119    ) -> Result<String, VmiError>
120    where
121        Driver: VmiRead,
122        Driver::Architecture: ArchAdapter<Driver>;
123}
124
125/// 32-bit structure layout.
126pub struct StructLayout32;
127
128impl StructLayout for StructLayout32 {
129    const ADDRESS_WIDTH: u64 = 4;
130
131    fn read_va<Driver>(
132        vmi: VmiState<WindowsOs<Driver>>,
133        ctx: impl Into<AccessContext>,
134    ) -> Result<Va, VmiError>
135    where
136        Driver: VmiRead,
137        Driver::Architecture: ArchAdapter<Driver>,
138    {
139        vmi.core().read_va32(ctx)
140    }
141
142    fn read_unicode_string<Driver>(
143        vmi: VmiState<WindowsOs<Driver>>,
144        ctx: impl Into<AccessContext>,
145    ) -> Result<String, VmiError>
146    where
147        Driver: VmiRead,
148        Driver::Architecture: ArchAdapter<Driver>,
149    {
150        vmi.os().read_unicode_string32_in(ctx)
151    }
152}
153
154/// 64-bit structure layout.
155pub struct StructLayout64;
156
157impl StructLayout for StructLayout64 {
158    const ADDRESS_WIDTH: u64 = 8;
159
160    fn read_va<Driver>(
161        vmi: VmiState<WindowsOs<Driver>>,
162        ctx: impl Into<AccessContext>,
163    ) -> Result<Va, VmiError>
164    where
165        Driver: VmiRead,
166        Driver::Architecture: ArchAdapter<Driver>,
167    {
168        vmi.core().read_va64(ctx)
169    }
170
171    fn read_unicode_string<Driver>(
172        vmi: VmiState<WindowsOs<Driver>>,
173        ctx: impl Into<AccessContext>,
174    ) -> Result<String, VmiError>
175    where
176        Driver: VmiRead,
177        Driver::Architecture: ArchAdapter<Driver>,
178    {
179        vmi.os().read_unicode_string64_in(ctx)
180    }
181}
182
183/// Extracts the kernel image base, version, and CodeView debug information
184/// from a mapped PE image.
185pub(crate) fn image_codeview<Driver>(
186    image: &WindowsImage<Driver>,
187) -> Result<Option<WindowsKernelInformation>, VmiError>
188where
189    Driver: VmiRead,
190    Driver::Architecture: ArchAdapter<Driver>,
191{
192    let debug_directory = match image.debug_directory()? {
193        Some(debug_directory) => debug_directory,
194        None => return Ok(None),
195    };
196
197    let codeview = match debug_directory.codeview()? {
198        Some(codeview) => codeview,
199        None => return Ok(None),
200    };
201
202    let nt_headers = image.nt_headers()?;
203
204    Ok(Some(WindowsKernelInformation {
205        base_address: image.base_address(),
206        version_major: nt_headers.optional_header.major_operating_system_version(),
207        version_minor: nt_headers.optional_header.minor_operating_system_version(),
208        codeview,
209    }))
210}