1use std::collections::BTreeMap;
4use std::fmt::Debug;
5use std::{io, mem, slice};
6
7use crate::error::Result;
8use crate::{Gpa, KdmpParserError, Reader};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum PageKind {
13 Normal,
15 Large,
17 Huge,
19}
20
21impl PageKind {
22 pub fn size(&self) -> u64 {
24 match self {
25 Self::Normal => 4 * 1_024,
26 Self::Large => 2 * 1_024 * 1_024,
27 Self::Huge => 1_024 * 1_024 * 1_024,
28 }
29 }
30
31 pub fn page_offset(&self, addr: u64) -> u64 {
33 let mask = self.size() - 1;
34
35 addr & mask
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq)]
41#[repr(u32)]
42pub enum DumpType {
43 Full = 0x1,
45 Bmp = 0x5,
46 LiveKernelMemory = 0x6,
50 KernelMemory = 0x8,
52 KernelAndUserMemory = 0x9,
54 CompleteMemory = 0xa,
56}
57
58pub type PhysmemMap = BTreeMap<Gpa, u64>;
60
61impl TryFrom<u32> for DumpType {
62 type Error = KdmpParserError;
63
64 fn try_from(value: u32) -> Result<Self> {
65 match value {
66 x if x == DumpType::Full as u32 => Ok(DumpType::Full),
67 x if x == DumpType::Bmp as u32 => Ok(DumpType::Bmp),
68 x if x == DumpType::KernelMemory as u32 => Ok(DumpType::KernelMemory),
69 x if x == DumpType::KernelAndUserMemory as u32 => Ok(DumpType::KernelAndUserMemory),
70 x if x == DumpType::CompleteMemory as u32 => Ok(DumpType::CompleteMemory),
71 x if x == DumpType::LiveKernelMemory as u32 => Ok(DumpType::LiveKernelMemory),
72 _ => Err(KdmpParserError::UnknownDumpType(value)),
73 }
74 }
75}
76
77#[repr(C)]
78#[derive(Debug, Default)]
79pub struct ExceptionRecord64 {
80 pub exception_code: u32,
81 pub exception_flags: u32,
82 pub exception_record: u64,
83 pub exception_address: u64,
84 pub number_parameters: u32,
85 unused_alignment1: u32,
86 pub exception_information: [u64; 15],
87}
88
89pub const DUMP_HEADER64_EXPECTED_SIGNATURE: u32 = 0x45_47_41_50; pub const DUMP_HEADER64_EXPECTED_VALID_DUMP: u32 = 0x34_36_55_44; #[repr(C)]
96pub struct Header64 {
97 pub signature: u32,
98 pub valid_dump: u32,
99 pub major_version: u32,
100 pub minor_version: u32,
101 pub directory_table_base: u64,
102 pub pfn_database: u64,
103 pub ps_loaded_module_list: u64,
104 pub ps_active_process_head: u64,
105 pub machine_image_type: u32,
106 pub number_processors: u32,
107 pub bug_check_code: u32,
108 padding1: u32,
109 pub bug_check_code_parameters: [u64; 4],
110 pub version_user: [u8; 32],
111 pub kd_debugger_data_block: u64,
112 pub physical_memory_block_buffer: [u8; 700],
113 padding2: u32,
114 pub context_record_buffer: [u8; 3_000],
115 pub exception: ExceptionRecord64,
116 pub dump_type: u32,
117 padding3: u32,
118 pub required_dump_space: i64,
119 pub system_time: i64,
120 pub comment: [u8; 128],
121 pub system_up_time: i64,
122 pub minidump_fields: u32,
123 pub secondary_data_state: u32,
124 pub product_type: u32,
125 pub suite_mask: u32,
126 pub writer_status: u32,
127 unused1: u8,
128 pub kd_secondary_version: u8,
129 unused2: [u8; 2],
130 pub attributes: u32,
131 pub boot_id: u32,
132 reserved1: [u8; 4008],
133}
134
135impl Debug for Header64 {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 f.debug_struct("Header64")
138 .field("signature", &self.signature)
139 .field("valid_dump", &self.valid_dump)
140 .field("major_version", &self.major_version)
141 .field("minor_version", &self.minor_version)
142 .field("directory_table_base", &self.directory_table_base)
143 .field("pfn_database", &self.pfn_database)
144 .field("ps_loaded_module_list", &self.ps_loaded_module_list)
145 .field("ps_active_process_head", &self.ps_active_process_head)
146 .field("machine_image_type", &self.machine_image_type)
147 .field("number_processors", &self.number_processors)
148 .field("bug_check_code", &self.bug_check_code)
149 .field("bug_check_code_parameters", &self.bug_check_code_parameters)
150 .field("version_user", &self.version_user)
151 .field("kd_debugger_data_block", &self.kd_debugger_data_block)
152 .field("exception", &self.exception)
153 .field("dump_type", &self.dump_type)
154 .field("required_dump_space", &self.required_dump_space)
155 .field("system_time", &self.system_time)
156 .field("comment", &self.comment)
157 .field("system_up_time", &self.system_up_time)
158 .field("minidump_fields", &self.minidump_fields)
159 .field("secondary_data_state", &self.secondary_data_state)
160 .field("product_type", &self.product_type)
161 .field("suite_mask", &self.suite_mask)
162 .field("writer_status", &self.writer_status)
163 .field("kd_secondary_version", &self.kd_secondary_version)
164 .field("attributes", &self.attributes)
165 .field("boot_id", &self.boot_id)
166 .finish()
167 }
168}
169
170const BMPHEADER64_EXPECTED_SIGNATURE: u32 = 0x50_4D_44_53; const BMPHEADER64_EXPECTED_SIGNATURE2: u32 = 0x50_4D_44_46; const BMPHEADER64_EXPECTED_VALID_DUMP: u32 = 0x50_4D_55_44; #[derive(Debug, Default)]
175#[repr(C)]
176pub struct BmpHeader64 {
177 pub signature: u32,
178 pub valid_dump: u32,
179 padding1: [u8; 0x20 - (0x4 + mem::size_of::<u32>())],
187 pub first_page: u64,
189 pub total_present_pages: u64,
191 pub pages: u64,
195 }
197
198impl BmpHeader64 {
199 pub fn looks_good(&self) -> bool {
200 (self.signature == BMPHEADER64_EXPECTED_SIGNATURE
201 || self.signature == BMPHEADER64_EXPECTED_SIGNATURE2)
202 && self.valid_dump == BMPHEADER64_EXPECTED_VALID_DUMP
203 }
204}
205
206#[derive(Debug, Default)]
207#[repr(C)]
208pub struct PhysmemRun {
209 pub base_page: u64,
210 pub page_count: u64,
211}
212
213impl PhysmemRun {
214 pub fn phys_addr(&self, page_idx: u64) -> Option<Gpa> {
218 debug_assert!(page_idx < self.page_count);
219
220 self.base_page
221 .checked_add(page_idx)?
222 .checked_mul(PageKind::Normal.size())
223 .map(Gpa::new)
224 }
225}
226
227#[derive(Debug, Default)]
228#[repr(C)]
229pub struct PhysmemDesc {
230 pub number_of_runs: u32,
231 padding1: u32,
232 pub number_of_pages: u64,
233 }
235
236impl TryFrom<&[u8]> for PhysmemDesc {
237 type Error = KdmpParserError;
238
239 fn try_from(slice: &[u8]) -> Result<Self> {
240 let expected_len = mem::size_of::<Self>();
241 if slice.len() < expected_len {
242 return Err(KdmpParserError::InvalidData("physmem desc is too small"));
243 }
244
245 let number_of_runs = u32::from_le_bytes((&slice[0..4]).try_into().unwrap());
246 let number_of_pages = u64::from_le_bytes((&slice[4..12]).try_into().unwrap());
247
248 Ok(Self {
249 number_of_runs,
250 number_of_pages,
251 ..Default::default()
252 })
253 }
254}
255
256#[derive(PartialEq)]
257#[repr(C)]
258pub struct Context {
259 pub p1_home: u64,
260 pub p2_home: u64,
261 pub p3_home: u64,
262 pub p4_home: u64,
263 pub p5_home: u64,
264 pub p6_home: u64,
265 pub context_flags: u32,
266 pub mxcsr: u32,
267 pub seg_cs: u16,
268 pub seg_ds: u16,
269 pub seg_es: u16,
270 pub seg_fs: u16,
271 pub seg_gs: u16,
272 pub seg_ss: u16,
273 pub eflags: u32,
274 pub dr0: u64,
275 pub dr1: u64,
276 pub dr2: u64,
277 pub dr3: u64,
278 pub dr6: u64,
279 pub dr7: u64,
280 pub rax: u64,
281 pub rcx: u64,
282 pub rdx: u64,
283 pub rbx: u64,
284 pub rsp: u64,
285 pub rbp: u64,
286 pub rsi: u64,
287 pub rdi: u64,
288 pub r8: u64,
289 pub r9: u64,
290 pub r10: u64,
291 pub r11: u64,
292 pub r12: u64,
293 pub r13: u64,
294 pub r14: u64,
295 pub r15: u64,
296 pub rip: u64,
297 pub control_word: u16,
298 pub status_word: u16,
299 pub tag_word: u8,
300 reserved1: u8,
301 pub error_opcode: u16,
302 pub error_offset: u32,
303 pub error_selector: u16,
304 reserved2: u16,
305 pub data_offset: u32,
306 pub data_selector: u16,
307 reserved3: u16,
308 pub mxcsr2: u32,
309 pub mxcsr_mask: u32,
310 pub float_registers: [u128; 8],
311 pub xmm_registers: [u128; 16],
312 reserved4: [u8; 96],
313 pub vector_register: [u128; 26],
314 pub vector_control: u64,
315 pub debug_control: u64,
316 pub last_branch_to_rip: u64,
317 pub last_branch_from_rip: u64,
318 pub last_exception_to_rip: u64,
319 pub last_exception_from_rip: u64,
320}
321
322impl Debug for Context {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 f.debug_struct("Context")
325 .field("p1_home", &self.p1_home)
326 .field("p2_home", &self.p2_home)
327 .field("p3_home", &self.p3_home)
328 .field("p4_home", &self.p4_home)
329 .field("p5_home", &self.p5_home)
330 .field("p6_home", &self.p6_home)
331 .field("context_flags", &self.context_flags)
332 .field("mxcsr", &self.mxcsr)
333 .field("seg_cs", &self.seg_cs)
334 .field("seg_ds", &self.seg_ds)
335 .field("seg_es", &self.seg_es)
336 .field("seg_fs", &self.seg_fs)
337 .field("seg_gs", &self.seg_gs)
338 .field("seg_ss", &self.seg_ss)
339 .field("eflags", &self.eflags)
340 .field("dr0", &self.dr0)
341 .field("dr1", &self.dr1)
342 .field("dr2", &self.dr2)
343 .field("dr3", &self.dr3)
344 .field("dr6", &self.dr6)
345 .field("dr7", &self.dr7)
346 .field("rax", &self.rax)
347 .field("rcx", &self.rcx)
348 .field("rdx", &self.rdx)
349 .field("rbx", &self.rbx)
350 .field("rsp", &self.rsp)
351 .field("rbp", &self.rbp)
352 .field("rsi", &self.rsi)
353 .field("rdi", &self.rdi)
354 .field("r8", &self.r8)
355 .field("r9", &self.r9)
356 .field("r10", &self.r10)
357 .field("r11", &self.r11)
358 .field("r12", &self.r12)
359 .field("r13", &self.r13)
360 .field("r14", &self.r14)
361 .field("r15", &self.r15)
362 .field("rip", &self.rip)
363 .field("control_word", &self.control_word)
364 .field("status_word", &self.status_word)
365 .field("tag_word", &self.tag_word)
366 .field("error_opcode", &self.error_opcode)
367 .field("error_offset", &self.error_offset)
368 .field("error_selector", &self.error_selector)
369 .field("data_offset", &self.data_offset)
370 .field("data_selector", &self.data_selector)
371 .field("mxcsr2", &self.mxcsr2)
372 .field("mxcsr_mask", &self.mxcsr_mask)
373 .field("float_registers", &self.float_registers)
374 .field("xmm_registers", &self.xmm_registers)
375 .field("vector_register", &self.vector_register)
376 .field("vector_control", &self.vector_control)
377 .field("debug_control", &self.debug_control)
378 .field("last_branch_to_rip", &self.last_branch_to_rip)
379 .field("last_branch_from_rip", &self.last_branch_from_rip)
380 .field("last_exception_to_rip", &self.last_exception_to_rip)
381 .field("last_exception_from_rip", &self.last_exception_from_rip)
382 .finish()
383 }
384}
385
386pub fn peek_struct<T>(reader: &mut impl Reader) -> Result<T> {
388 let mut s = mem::MaybeUninit::uninit();
389 let size_of_s = mem::size_of_val(&s);
390 let slice_over_s = unsafe { slice::from_raw_parts_mut(s.as_mut_ptr() as *mut u8, size_of_s) };
391
392 let pos = reader.stream_position()?;
393 reader.read_exact(slice_over_s)?;
394 reader.seek(io::SeekFrom::Start(pos))?;
395
396 Ok(unsafe { s.assume_init() })
397}
398
399pub fn read_struct<T>(reader: &mut impl Reader) -> Result<T> {
401 let s = peek_struct(reader)?;
402 let size_of_s = mem::size_of_val(&s);
403
404 reader.seek(io::SeekFrom::Current(size_of_s.try_into().unwrap()))?;
405
406 Ok(s)
407}
408
409const RDMP_HEADER64_EXPECTED_MARKER: u32 = 0x40;
410const RDMP_HEADER64_EXPECTED_SIGNATURE: u32 = 0x50_4D_44_52; const RDMP_HEADER64_EXPECTED_VALID_DUMP: u32 = 0x50_4D_55_44; #[repr(C)]
414#[derive(Debug, Default)]
415pub struct RdmpHeader64 {
416 pub marker: u32,
417 pub signature: u32,
418 pub valid_dump: u32,
419 reserved1: u32,
420 pub metadata_size: u64,
421 pub first_page_offset: u64,
422 }
424
425impl RdmpHeader64 {
426 pub fn looks_good(&self) -> bool {
427 if self.marker != RDMP_HEADER64_EXPECTED_MARKER {
428 return false;
429 }
430
431 if self.signature != RDMP_HEADER64_EXPECTED_SIGNATURE {
432 return false;
433 }
434
435 if self.valid_dump != RDMP_HEADER64_EXPECTED_VALID_DUMP {
436 return false;
437 }
438
439 if self.metadata_size - 0x20 != self.first_page_offset - 0x20_40 {
440 return false;
441 }
442
443 true
444 }
445}
446
447#[repr(C)]
448#[derive(Debug, Default)]
449pub struct KernelRdmpHeader64 {
450 pub hdr: RdmpHeader64,
451 unknown1: u64,
452 unknown2: u64,
453 }
455
456#[repr(C)]
457#[derive(Debug, Default)]
458pub struct FullRdmpHeader64 {
459 pub hdr: RdmpHeader64,
460 pub number_of_ranges: u32,
461 reserved1: u16,
462 reserved2: u16,
463 pub total_number_of_pages: u64,
464 }
466
467#[repr(C)]
468#[derive(Debug, Default)]
469pub struct PfnRange {
470 pub page_file_number: u64,
471 pub number_of_pages: u64,
472}
473
474#[repr(C)]
475#[derive(Debug, Default)]
476pub struct ListEntry<P> {
477 pub flink: P,
478 pub blink: P,
479}
480
481#[repr(C)]
482#[derive(Debug, Default)]
483pub struct UnicodeString<P> {
484 pub length: u16,
485 pub maximum_length: u16,
486 pub buffer: P,
487}
488
489#[derive(Debug, Default)]
490#[repr(C)]
491pub struct LdrDataTableEntry<P> {
492 pub in_load_order_links: ListEntry<P>,
493 pub in_memory_order_links: ListEntry<P>,
494 pub in_initialization_order_links: ListEntry<P>,
495 pub dll_base: P,
496 pub entry_point: P,
497 pub size_of_image: u32,
498 pub full_dll_name: UnicodeString<P>,
499 pub base_dll_name: UnicodeString<P>,
500}
501
502#[repr(C)]
504#[derive(Debug, Default)]
505pub struct DbgKdDebugDataHeader64 {
506 pub list: ListEntry<u64>,
508 pub owner_tag: u32,
511 pub size: u32,
514}
515
516#[repr(C)]
518#[derive(Debug, Default)]
519pub struct KdDebuggerData64 {
520 pub header: DbgKdDebugDataHeader64,
521 pub kern_base: u64,
523 pub breakpoint_with_status: u64,
529 pub saved_context: u64,
533 pub th_callback_stack: u16,
538 pub next_callback: u16,
540 pub frame_pointer: u16,
542 pub pae_enabled: u16,
544 pub ki_call_user_mode: u64,
546 pub ke_user_callback_dispatcher: u64,
548 pub ps_loaded_module_list: u64,
549 pub ps_active_process_head: u64,
550 pub psp_cid_table: u64,
551 pub exp_system_resources_list: u64,
552 pub exp_paged_pool_descriptor: u64,
553 pub exp_number_of_paged_pools: u64,
554 pub ke_time_increment: u64,
555 pub ke_bug_check_callback_list_head: u64,
556 pub ki_bugcheck_data: u64,
557 pub iop_error_log_list_head: u64,
558 pub obp_root_directory_object: u64,
559 pub obp_type_object_type: u64,
560 pub mm_system_cache_start: u64,
561 pub mm_system_cache_end: u64,
562 pub mm_system_cache_ws: u64,
563 pub mm_pfn_database: u64,
564 pub mm_system_ptes_start: u64,
565 pub mm_system_ptes_end: u64,
566 pub mm_subsection_base: u64,
567 pub mm_number_of_paging_files: u64,
568 pub mm_lowest_physical_page: u64,
569 pub mm_highest_physical_page: u64,
570 pub mm_number_of_physical_pages: u64,
571 pub mm_maximum_non_paged_pool_in_bytes: u64,
572 pub mm_non_paged_system_start: u64,
573 pub mm_non_paged_pool_start: u64,
574 pub mm_non_paged_pool_end: u64,
575 pub mm_paged_pool_start: u64,
576 pub mm_paged_pool_end: u64,
577 pub mm_paged_pool_information: u64,
578 pub mm_page_size: u64,
579 pub mm_size_of_paged_pool_in_bytes: u64,
580 pub mm_total_commit_limit: u64,
581 pub mm_total_committed_pages: u64,
582 pub mm_shared_commit: u64,
583 pub mm_driver_commit: u64,
584 pub mm_process_commit: u64,
585 pub mm_paged_pool_commit: u64,
586 pub mm_extended_commit: u64,
587 pub mm_zeroed_page_list_head: u64,
588 pub mm_free_page_list_head: u64,
589 pub mm_standby_page_list_head: u64,
590 pub mm_modified_page_list_head: u64,
591 pub mm_modified_no_write_page_list_head: u64,
592 pub mm_available_pages: u64,
593 pub mm_resident_available_pages: u64,
594 pub pool_track_table: u64,
595 pub non_paged_pool_descriptor: u64,
596 pub mm_highest_user_address: u64,
597 pub mm_system_range_start: u64,
598 pub mm_user_probe_address: u64,
599 pub kd_print_circular_buffer: u64,
600 pub kd_print_circular_buffer_end: u64,
601 pub kd_print_write_pointer: u64,
602 pub kd_print_rollover_count: u64,
603 pub mm_loaded_user_image_list: u64,
604 pub nt_build_lab: u64,
606 pub ki_normal_system_call: u64,
607 pub ki_processor_block: u64,
609 pub mm_unloaded_drivers: u64,
610 pub mm_last_unloaded_driver: u64,
611 pub mm_triage_action_taken: u64,
612 pub mm_special_pool_tag: u64,
613 pub kernel_verifier: u64,
614 pub mm_verifier_data: u64,
615 pub mm_allocated_non_paged_pool: u64,
616 pub mm_peak_commitment: u64,
617 pub mm_total_commit_limit_maximum: u64,
618 pub cm_nt_csd_version: u64,
619 pub mm_physical_memory_block: u64,
621 pub mm_session_base: u64,
622 pub mm_session_size: u64,
623 pub mm_system_parent_table_page: u64,
624 pub mm_virtual_translation_base: u64,
626 pub offset_kthread_next_processor: u16,
627 pub offset_kthread_teb: u16,
628 pub offset_kthread_kernel_stack: u16,
629 pub offset_kthread_initial_stack: u16,
630 pub offset_kthread_apc_process: u16,
631 pub offset_kthread_state: u16,
632 pub offset_kthread_b_store: u16,
633 pub offset_kthread_b_store_limit: u16,
634 pub size_eprocess: u16,
635 pub offset_eprocess_peb: u16,
636 pub offset_eprocess_parent_cid: u16,
637 pub offset_eprocess_directory_table_base: u16,
638 pub size_prcb: u16,
639 pub offset_prcb_dpc_routine: u16,
640 pub offset_prcb_current_thread: u16,
641 pub offset_prcb_mhz: u16,
642 pub offset_prcb_cpu_type: u16,
643 pub offset_prcb_vendor_string: u16,
644 pub offset_prcb_proc_state_context: u16,
645 pub offset_prcb_number: u16,
646 pub size_ethread: u16,
647 pub kd_print_circular_buffer_ptr: u64,
648 pub kd_print_buffer_size: u64,
649 pub ke_loader_block: u64,
650 pub size_pcr: u16,
651 pub offset_pcr_self_pcr: u16,
652 pub offset_pcr_current_prcb: u16,
653 pub offset_pcr_contained_prcb: u16,
654 pub offset_pcr_initial_b_store: u16,
655 pub offset_pcr_b_store_limit: u16,
656 pub offset_pcr_initial_stack: u16,
657 pub offset_pcr_stack_limit: u16,
658 pub offset_prcb_pcr_page: u16,
659 pub offset_prcb_proc_state_special_reg: u16,
660 pub gdt_r0_code: u16,
661 pub gdt_r0_data: u16,
662 pub gdt_r0_pcr: u16,
663 pub gdt_r3_code: u16,
664 pub gdt_r3_data: u16,
665 pub gdt_r3_teb: u16,
666 pub gdt_ldt: u16,
667 pub gdt_tss: u16,
668 pub gdt64_r3_cm_code: u16,
669 pub gdt64_r3_cm_teb: u16,
670 pub iop_num_triage_dump_data_blocks: u64,
671 pub iop_triage_dump_data_blocks: u64,
672 pub vf_crash_data_block: u64,
674 pub mm_bad_pages_detected: u64,
675 pub mm_zeroed_page_single_bit_errors_detected: u64,
676 pub etwp_debugger_data: u64,
678 pub offset_prcb_context: u16,
679 }
681
682#[cfg(test)]
683mod tests {
684 use std::mem;
685
686 use crate::structs::{Context, Header64, PhysmemDesc, PhysmemRun};
687
688 #[test]
690 fn layout() {
691 assert_eq!(mem::size_of::<PhysmemDesc>(), 0x10);
692 assert_eq!(mem::size_of::<PhysmemRun>(), 0x10);
693 assert_eq!(mem::size_of::<Header64>(), 0x2_000);
694 assert_eq!(mem::size_of::<Context>(), 0x4d0);
695 }
696}