1use std::{
2 collections::{HashMap, HashSet},
3 path::{Path, PathBuf},
4};
5
6use anyhow::{anyhow, bail, Result};
7use pdb::{FallibleIterator, SymbolData};
8use simics::{debug, get_attribute, get_object, info, ConfObject};
9use vergilius::bindings::*;
10use windows_sys::Win32::System::{
11 Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64},
12 Kernel::LIST_ENTRY,
13 SystemServices::{
14 IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_SIGNATURE,
15 },
16};
17
18use crate::os::{
19 windows::{
20 debug_info::DebugInfo,
21 idt::IdtEntry64,
22 util::{read_nul_terminated_string, read_unicode_string, read_virtual},
23 },
24 DebugInfoConfig,
25};
26
27use super::{
28 debug_info::{Module, Process},
29 structs::{WindowsEProcess, WindowsKThread, WindowsKpcr, WindowsKprcb},
30};
31
32const KUSER_SHARED_DATA_ADDRESS_X86_64: u64 = 0xFFFFF78000000000;
33
34pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result<bool> {
36 const OPTIONAL_HEADER_SIGNATURE_PE32: u16 = 0x10b;
37 const OPTIONAL_HEADER_SIGNATURE_PE32_PLUS: u16 = 0x20b;
38
39 let dos_header = read_virtual::<IMAGE_DOS_HEADER>(processor, address)?;
40
41 if dos_header.e_magic != IMAGE_DOS_SIGNATURE {
42 return Ok(false);
43 }
44
45 let nt_header =
46 read_virtual::<IMAGE_NT_HEADERS64>(processor, address + dos_header.e_lfanew as u64)?;
47
48 if nt_header.Signature != IMAGE_NT_SIGNATURE {
49 debug!(
50 "NT Signature {:#x} != {:#x}",
51 nt_header.Signature, IMAGE_NT_SIGNATURE
52 );
53 return Ok(false);
54 }
55
56 debug!(
57 "Found NT signature at {:#x}",
58 address + dos_header.e_lfanew as u64
59 );
60
61 if nt_header.FileHeader.SizeOfOptionalHeader == 0 {
62 debug!(get_object("tsffs")?, "Optional header size was 0");
63 return Ok(false);
64 }
65
66 if ![
67 OPTIONAL_HEADER_SIGNATURE_PE32,
68 OPTIONAL_HEADER_SIGNATURE_PE32_PLUS,
69 ]
70 .contains(&nt_header.OptionalHeader.Magic)
71 {
72 debug!(
73 "Optional header magic {:#x} unrecognized",
74 nt_header.OptionalHeader.Magic
75 );
76 return Ok(false);
77 }
78
79 let image_size = nt_header.OptionalHeader.SizeOfImage as u64;
80
81 debug!(get_object("tsffs")?, "Image size is {:#x}", image_size);
82
83 let export_header_offset = nt_header.OptionalHeader.DataDirectory
84 [IMAGE_DIRECTORY_ENTRY_EXPORT as usize]
85 .VirtualAddress as u64;
86 let export_header_size =
87 nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize].Size as u64;
88
89 if export_header_offset == 0 || export_header_offset >= image_size {
90 debug!(
91 "Export header offset {:#x} invalid for image sized {:#x}",
92 export_header_offset, image_size
93 );
94 return Ok(false);
95 }
96
97 if export_header_size == 0 || export_header_offset + export_header_size > image_size {
98 debug!(
99 "Export header size {:#x} and offset {:#x} invalid for image sized {:#x}",
100 export_header_size, export_header_offset, image_size
101 );
102 return Ok(false);
103 }
104
105 debug!(
106 "Export header offset {:#x} size {:#x}",
107 export_header_offset, export_header_size
108 );
109
110 let export_directory =
111 read_virtual::<IMAGE_EXPORT_DIRECTORY>(processor, address + export_header_offset)?;
112
113 let name = read_nul_terminated_string(processor, address + export_directory.Name as u64)?;
114
115 debug!(get_object("tsffs")?, "Read image name {}", name);
116
117 if name == "ntoskrnl.exe" {
118 return Ok(true);
119 }
120
121 Ok(false)
122}
123
124pub fn find_kernel(processor: *mut ConfObject, start: u64, step: u64) -> Result<u64> {
126 let mut scan_address = start & !(step - 1);
127 let stop_address = start & !(0x1000000000000 - 1);
128
129 debug!(
130 "Scanning for kernel from {:#x}->{:#x}",
131 stop_address, scan_address
132 );
133
134 while scan_address >= stop_address {
135 if page_is_kernel(processor, scan_address)? {
136 return Ok(scan_address);
137 }
138
139 scan_address -= step;
140 }
141
142 bail!("Kernel not found");
143}
144
145pub fn find_kernel_with_idt(processor: *mut ConfObject, build: u32) -> Result<u64> {
147 let sim_idtr_base: u64 = get_attribute(processor, "idtr_base")?.try_into()?;
148
149 for i in 0..256 {
150 let idtr_entry0 = read_virtual::<IdtEntry64>(
152 processor,
153 sim_idtr_base + (i * std::mem::size_of::<IdtEntry64>() as u64),
154 )?;
155 if !idtr_entry0.present() {
156 debug!(get_object("tsffs")?, "Entry {} not present, skipping", i);
157 continue;
158 }
159 let idtr_entry0_offset = idtr_entry0.offset();
160 debug!(
161 get_object("tsffs")?,
162 "Got valid IDT entry with offset {:#x}", idtr_entry0_offset
163 );
164 return find_kernel(
165 processor,
166 idtr_entry0_offset,
167 if build >= 19000 { 0x200000 } else { 0x1000 },
168 );
169 }
170
171 bail!("Kernel not found");
172}
173
174#[derive(Debug)]
175pub struct KernelInfo {
177 pub base: u64,
179 pub major: u32,
181 pub minor: u32,
183 pub build: u32,
185 pub debug_info: DebugInfo<'static>,
187}
188
189impl KernelInfo {
190 pub fn new<P>(
192 processor: *mut ConfObject,
193 name: &str,
194 base: u64,
195 download_directory: P,
196 not_found_full_name_cache: &mut HashSet<String>,
197 user_debug_info: &mut DebugInfoConfig,
198 ) -> Result<Self>
199 where
200 P: AsRef<Path>,
201 {
202 let debug_info = DebugInfo::new(
203 processor,
204 name,
205 base,
206 download_directory,
207 not_found_full_name_cache,
208 &DebugInfoConfig {
211 system: true,
212 user_debug_info: user_debug_info.user_debug_info,
213 coverage: user_debug_info.coverage,
214 },
215 )?
216 .ok_or_else(|| anyhow!("Failed to get debug info for kernel"))?;
217
218 let kuser_shared_data = read_virtual::<windows_10_0_22631_2428_x64::_KUSER_SHARED_DATA>(
219 processor,
220 KUSER_SHARED_DATA_ADDRESS_X86_64,
221 )?;
222
223 Ok(Self {
224 base,
225 major: kuser_shared_data.NtMajorVersion,
226 minor: kuser_shared_data.NtMinorVersion,
227 build: kuser_shared_data.NtBuildNumber,
228 debug_info,
229 })
230 }
231
232 fn find_ps_loaded_module_list_address(&mut self) -> Result<u64> {
233 let pdb_symbols = self.debug_info.pdb.global_symbols()?;
236 let pdb_address_map = self.debug_info.pdb.address_map()?;
237 if let Ok(Some(ps_loaded_module_list_symbol)) =
238 pdb_symbols.iter().find_map(|symbol| match symbol.parse() {
239 Ok(symbol) => {
240 if let SymbolData::Public(public_symbol) = symbol {
241 if public_symbol.name.to_string() == "PsLoadedModuleList" {
242 Ok(Some(
243 public_symbol
244 .offset
245 .to_rva(&pdb_address_map)
246 .ok_or(pdb::Error::AddressMapNotFound)?
247 .0 as u64
248 + self.base,
249 ))
250 } else {
251 Ok(None)
252 }
253 } else {
254 Ok(None)
255 }
256 }
257 Err(e) => Err(e),
258 })
259 {
260 Ok(ps_loaded_module_list_symbol)
261 } else {
262 self.debug_info
263 .exports()?
264 .iter()
265 .find(|export| {
266 export
267 .name
268 .as_ref()
269 .is_some_and(|name| name == "PsLoadedModuleList")
270 })
271 .map(|export| export.rva as u64 + self.base)
272 .ok_or_else(|| anyhow!("PsLoadedModuleList not found"))
273 }
274 }
275
276 pub fn loaded_module_list<P>(
278 &mut self,
279 processor: *mut ConfObject,
280 download_directory: P,
281 not_found_full_name_cache: &mut HashSet<String>,
282 user_debug_info: &DebugInfoConfig,
283 ) -> Result<Vec<Module>>
284 where
285 P: AsRef<Path>,
286 {
287 let list_address = self.find_ps_loaded_module_list_address()?;
288 debug!(
289 get_object("tsffs")?,
290 "PsLoadedModuleList: {:#x}", list_address
291 );
292 let list = read_virtual::<LIST_ENTRY>(processor, list_address)?;
293 let mut modules = Vec::new();
294
295 let mut current = list.Flink;
296
297 while !std::ptr::eq(current, list_address as *mut LIST_ENTRY) {
298 let ldr_data_table_entry = read_virtual::<
309 windows_10_0_22631_2428_x64::_KLDR_DATA_TABLE_ENTRY,
310 >(processor, current as u64)?;
311
312 let base = ldr_data_table_entry.DllBase as u64;
313 let entry = ldr_data_table_entry.EntryPoint as u64;
314 let size = ldr_data_table_entry.SizeOfImage as u64;
315 let full_name = read_unicode_string(
316 processor,
317 ldr_data_table_entry.FullDllName.Length as usize,
318 ldr_data_table_entry.FullDllName.Buffer,
319 )?;
320 let base_name = read_unicode_string(
321 processor,
322 ldr_data_table_entry.BaseDllName.Length as usize,
323 ldr_data_table_entry.BaseDllName.Buffer,
324 )?;
325 let debug_info = full_name
326 .split('\\')
327 .next_back()
328 .ok_or_else(|| anyhow!("Failed to get file name"))
329 .and_then(|fname| {
330 DebugInfo::new(
332 processor,
333 fname,
334 base,
335 download_directory.as_ref(),
336 not_found_full_name_cache,
337 user_debug_info,
338 )
339 })
340 .ok()
341 .flatten();
342
343 modules.push(Module {
344 base,
345 entry,
346 size,
347 full_name,
348 base_name,
349 debug_info,
350 });
351
352 current = ldr_data_table_entry.InLoadOrderLinks.Flink as *mut _;
353 }
354
355 Ok(modules)
356 }
357
358 fn find_ps_initial_system_process_address(&mut self) -> Result<u64> {
359 let pdb_symbols = self.debug_info.pdb.global_symbols()?;
362 let pdb_address_map = self.debug_info.pdb.address_map()?;
363 if let Ok(Some(ps_initial_system_process_symbol)) =
364 pdb_symbols.iter().find_map(|symbol| match symbol.parse() {
365 Ok(symbol) => {
366 if let SymbolData::Public(public_symbol) = symbol {
367 if public_symbol.name.to_string() == "PsInitialSystemProcess" {
368 Ok(Some(
369 public_symbol
370 .offset
371 .to_rva(&pdb_address_map)
372 .ok_or(pdb::Error::AddressMapNotFound)?
373 .0 as u64
374 + self.base,
375 ))
376 } else {
377 Ok(None)
378 }
379 } else {
380 Ok(None)
381 }
382 }
383 Err(e) => Err(e),
384 })
385 {
386 Ok(ps_initial_system_process_symbol)
387 } else {
388 self.debug_info
389 .exports()?
390 .iter()
391 .find(|export| {
392 export
393 .name
394 .as_ref()
395 .is_some_and(|name| name == "PsInitialSystemProcess")
396 })
397 .map(|export| export.rva as u64 + self.base)
398 .ok_or_else(|| anyhow!("PsInitialSystemProcess not found"))
399 }
400 }
401
402 pub fn current_process<P>(
404 &mut self,
405 processor: *mut ConfObject,
406 download_directory: P,
407 not_found_full_name_cache: &mut HashSet<String>,
408 user_debug_info: &DebugInfoConfig,
409 ) -> Result<Process>
410 where
411 P: AsRef<Path>,
412 {
413 let kpcr = WindowsKpcr::new(processor, self.major, self.minor, self.build)?;
414 let kprcb = WindowsKprcb::new(
415 processor,
416 self.major,
417 self.minor,
418 self.build,
419 kpcr.kpcrb_address(),
420 )?;
421 let kthread = WindowsKThread::new(
422 processor,
423 self.major,
424 self.minor,
425 self.build,
426 kprcb.current_thread(),
427 )?;
428 let eprocess = kthread.process(processor, self.major, self.minor, self.build)?;
429
430 Ok(Process {
431 pid: eprocess.pid(),
432 file_name: eprocess.file_name(processor)?,
433 base_address: eprocess.base_address(processor, self.major, self.minor, self.build)?,
434 modules: eprocess
435 .modules(
436 processor,
437 self.major,
438 self.minor,
439 self.build,
440 download_directory.as_ref(),
441 not_found_full_name_cache,
442 user_debug_info,
443 )
444 .unwrap_or_default(),
445 })
446 }
447
448 pub fn process_list<P>(
450 &mut self,
451 processor: *mut ConfObject,
452 download_directory: P,
453 not_found_full_name_cache: &mut HashSet<String>,
454 user_debug_info: &DebugInfoConfig,
455 ) -> Result<Vec<Process>>
456 where
457 P: AsRef<Path>,
458 {
459 let kpcr = WindowsKpcr::new(processor, self.major, self.minor, self.build)?;
460 let kprcb = WindowsKprcb::new(
461 processor,
462 self.major,
463 self.minor,
464 self.build,
465 kpcr.kpcrb_address(),
466 )?;
467 let kthread = WindowsKThread::new(
468 processor,
469 self.major,
470 self.minor,
471 self.build,
472 kprcb.current_thread(),
473 )?;
474 let eprocess = kthread.process(processor, self.major, self.minor, self.build)?;
475 let mut processes = Vec::new();
478
479 let mut list_entry = eprocess.active_process_links();
480 let last_entry = list_entry.Blink;
481
482 while !list_entry.Flink.is_null() {
483 let eprocess = WindowsEProcess::new_from_active_process_links_address(
484 processor,
485 self.major,
486 self.minor,
487 self.build,
488 list_entry.Flink as u64,
489 )?;
490 let pid = eprocess.pid();
491 let file_name = eprocess.file_name(processor)?;
492 let base_address =
493 eprocess.base_address(processor, self.major, self.minor, self.build)?;
494 debug!(
495 get_object("tsffs")?,
496 "Found process {} at {:#x}", file_name, base_address
497 );
498 let modules = eprocess
499 .modules(
500 processor,
501 self.major,
502 self.minor,
503 self.build,
504 download_directory.as_ref(),
505 not_found_full_name_cache,
506 user_debug_info,
507 )
508 .unwrap_or_default();
509 debug!(get_object("tsffs")?, "Found {} modules", modules.len());
510
511 processes.push(Process {
512 pid,
513 file_name,
514 base_address,
515 modules,
516 });
517
518 list_entry = eprocess.active_process_links();
519
520 if std::ptr::eq(list_entry.Flink, last_entry) {
521 break;
522 }
523 }
524
525 Ok(processes)
526 }
527}