vmi_os_windows/arch/
amd64.rs1use vmi_arch_amd64::{
2 Amd64, Cr3, ExceptionVector, Interrupt, InterruptType, PageTableEntry, PageTableLevel,
3 Registers,
4};
5use vmi_core::{
6 Architecture as _, Pa, Va, VmiCore, VmiError, VmiSession, VmiState, driver::VmiRead, os::NoOS,
7};
8
9use super::ArchAdapter;
10use crate::{WindowsImage, WindowsKernelInformation, WindowsOs};
11
12pub trait WindowsPageTableEntry {
15 fn windows_prototype(self) -> bool;
17
18 fn windows_transition(self) -> bool;
20}
21
22impl WindowsPageTableEntry for PageTableEntry {
23 fn windows_prototype(self) -> bool {
24 (self.0 >> 10) & 1 != 0
25 }
26
27 fn windows_transition(self) -> bool {
28 (self.0 >> 11) & 1 != 0
29 }
30}
31
32pub trait WindowsExceptionVector {
35 const Apc: Self;
37
38 const Dpc: Self;
40}
41
42impl WindowsExceptionVector for ExceptionVector {
43 const Apc: Self = Self(31); const Dpc: Self = Self(47); }
46
47pub trait WindowsInterrupt {
50 fn apc() -> Self;
52
53 fn dpc() -> Self;
55}
56
57impl WindowsInterrupt for Interrupt {
58 fn apc() -> Self {
59 Self {
60 vector: ExceptionVector::Apc,
61 typ: InterruptType::ExternalInterrupt,
62 error_code: 0xffff_ffff,
63 instruction_length: 0,
64 extra: 0,
65 }
66 }
67
68 fn dpc() -> Self {
69 Self {
70 vector: ExceptionVector::Dpc,
71 typ: InterruptType::ExternalInterrupt,
72 error_code: 0xffff_ffff,
73 instruction_length: 0,
74 extra: 0,
75 }
76 }
77}
78
79impl<Driver> ArchAdapter<Driver> for Amd64
80where
81 Driver: VmiRead<Architecture = Self>,
82{
83 fn find_kernel(
84 vmi: &VmiCore<Driver>,
85 registers: &Registers,
86 ) -> Result<Option<WindowsKernelInformation>, VmiError> {
87 const MAX_BACKWARD_SEARCH: u64 = 32 * 1024 * 1024;
89
90 let session = VmiSession::new(vmi, const { &NoOS(std::marker::PhantomData) });
91 let vmi = session.with_registers(registers);
92
93 let lstar = registers.msr_lstar & Amd64::PAGE_MASK;
95
96 let mut data = [0u8; Amd64::PAGE_SIZE as usize];
97
98 for base_address in (lstar - MAX_BACKWARD_SEARCH..=lstar)
99 .rev()
100 .step_by(Amd64::PAGE_SIZE as usize)
101 {
102 let base_address = Va(base_address);
103
104 match vmi.read(base_address, &mut data) {
110 Ok(()) => {}
111 Err(VmiError::Translation(_)) => continue,
112 Err(err) => return Err(err),
113 }
114
115 if &data[..2] != b"MZ" {
116 continue;
117 }
118
119 tracing::trace!(%base_address, "found MZ");
120
121 let image = WindowsImage::new_without_os(vmi, base_address);
122 match super::image_codeview(&image) {
123 Ok(Some(result)) => {
124 let name = &result.codeview.name;
125
126 if name.starts_with("nt") {
127 tracing::debug!(%base_address, "found kernel image");
128 return Ok(Some(result));
129 }
130
131 tracing::trace!(%name, "found non-kernel image");
132 }
133 Ok(None) => tracing::trace!("no codeview found"),
134 Err(err) => tracing::trace!(%err, "error parsing PE"),
135 };
136 }
137
138 tracing::trace!(
139 "no codeview found within {} MB",
140 MAX_BACKWARD_SEARCH / 1024 / 1024
141 );
142
143 Ok(None)
144 }
145
146 fn syscall_argument(vmi: VmiState<WindowsOs<Driver>>, index: u64) -> Result<u64, VmiError> {
147 let registers = vmi.registers();
148
149 match index {
150 0 => Ok(registers.r10),
151 1 => Ok(registers.rdx),
152 2 => Ok(registers.r8),
153 3 => Ok(registers.r9),
154 _ => {
155 let index = index + 1;
156 let stack = registers.rsp + index * size_of::<u64>() as u64;
157 vmi.read_u64(stack.into())
158 }
159 }
160 }
161
162 fn function_argument(vmi: VmiState<WindowsOs<Driver>>, index: u64) -> Result<u64, VmiError> {
163 let registers = vmi.registers();
164
165 if registers.cs.access.long_mode() {
166 function_argument_x64(vmi, index)
167 }
168 else {
169 function_argument_x86(vmi, index)
170 }
171 }
172
173 fn function_return_value(vmi: VmiState<WindowsOs<Driver>>) -> Result<u64, VmiError> {
174 let registers = vmi.registers();
175
176 Ok(registers.rax)
177 }
178
179 fn kernel_image_base(vmi: VmiState<WindowsOs<Driver>>) -> Result<Va, VmiError> {
180 vmi.underlying_os()
181 .kernel_image_base
182 .get_or_try_init(|| {
183 let KiSystemCall64 = vmi.underlying_os().symbols.KiSystemCall64;
184
185 let registers = vmi.registers();
186 Ok(Va(registers.msr_lstar - KiSystemCall64))
187 })
188 .copied()
189 }
190
191 fn is_page_present_or_transition(
192 vmi: VmiState<WindowsOs<Driver>>,
193 address: Va,
194 ) -> Result<bool, VmiError> {
195 let registers = vmi.registers();
196
197 let translation = Amd64::translation(vmi.core(), address, registers.cr3.into());
198 if let Some(entry) = translation.entries().last()
199 && entry.level == PageTableLevel::Pt
200 {
201 if entry.entry.present() {
202 return Ok(true);
204 }
205 else if entry.entry.windows_transition() && !entry.entry.windows_prototype() {
206 return Ok(true);
218 }
219 }
220
221 Ok(false)
222 }
223
224 fn current_kpcr(vmi: VmiState<WindowsOs<Driver>>) -> Va {
225 let registers = vmi.registers();
226
227 if registers.cs.selector.request_privilege_level() != 0
228 || (registers.gs.base & (1 << 47)) == 0
229 {
230 registers.shadow_gs.into()
231 }
232 else {
233 registers.gs.base.into()
234 }
235 }
236
237 fn dtb_to_root(value: u64) -> Pa {
238 Pa::from(Cr3(value))
239 }
240}
241
242fn function_argument_x86<Driver>(
285 vmi: VmiState<WindowsOs<Driver>>,
286 index: u64,
287) -> Result<u64, VmiError>
288where
289 Driver: VmiRead<Architecture = Amd64>,
290{
291 let registers = vmi.registers();
292
293 let index = index + 1;
294 let stack = registers.rsp + index * size_of::<u32>() as u64;
295 Ok(vmi.read_u32(stack.into())? as u64)
296}
297
298fn function_argument_x64<Driver>(
299 vmi: VmiState<WindowsOs<Driver>>,
300 index: u64,
301) -> Result<u64, VmiError>
302where
303 Driver: VmiRead<Architecture = Amd64>,
304{
305 let registers = vmi.registers();
306
307 match index {
308 0 => Ok(registers.rcx),
309 1 => Ok(registers.rdx),
310 2 => Ok(registers.r8),
311 3 => Ok(registers.r9),
312 _ => {
313 let index = index + 1;
314 let stack = registers.rsp + index * size_of::<u64>() as u64;
315 vmi.read_u64(stack.into())
316 }
317 }
318}