kdmp_parser/
structs.rs

1// Axel '0vercl0k' Souchet - February 25 2024
2//! This has all the raw structures that makes up Windows kernel crash-dumps.
3use std::collections::BTreeMap;
4use std::fmt::Debug;
5use std::{io, mem, slice};
6
7use crate::error::Result;
8use crate::{Gpa, KdmpParserError, Reader};
9
10/// The different kind of physical pages.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum PageKind {
13    /// A normal 4kb page.
14    Normal,
15    /// A large 2mb page.
16    Large,
17    /// A huge 1gb page.
18    Huge,
19}
20
21impl PageKind {
22    /// Size in bytes of the page.
23    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    /// Extract the page offset of `addr`.
32    pub fn page_offset(&self, addr: u64) -> u64 {
33        let mask = self.size() - 1;
34
35        addr & mask
36    }
37}
38
39/// Types of kernel crash dump.
40#[derive(Debug, Clone, Copy, PartialEq)]
41#[repr(u32)]
42pub enum DumpType {
43    // Old dump types from dbgeng.dll
44    Full = 0x1,
45    Bmp = 0x5,
46    /// Produced by `.dump /m`.
47    // Mini = 0x4,
48    /// (22H2+) Produced by TaskMgr > System > Create live kernel Memory Dump.
49    LiveKernelMemory = 0x6,
50    /// Produced by `.dump /k`.
51    KernelMemory = 0x8,
52    /// Produced by `.dump /ka`.
53    KernelAndUserMemory = 0x9,
54    /// Produced by `.dump /f`.
55    CompleteMemory = 0xa,
56}
57
58/// The physical memory map maps a physical address to a file offset.
59pub 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; // 'EGAP'
90pub const DUMP_HEADER64_EXPECTED_VALID_DUMP: u32 = 0x34_36_55_44; // '46UD'
91
92/// Adjusted C struct for `DUMP_HEADERS64` from MS Rust docs. Padding
93/// adjustment added from reversing `nt!IoFillDumpHeader`.
94// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Diagnostics/Debug/struct.DUMP_HEADER64.html#structfield.DumpType
95#[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; // 'PMDS'
171const BMPHEADER64_EXPECTED_SIGNATURE2: u32 = 0x50_4D_44_46; // 'PMDF'
172const BMPHEADER64_EXPECTED_VALID_DUMP: u32 = 0x50_4D_55_44; // 'PMUD'
173
174#[derive(Debug, Default)]
175#[repr(C)]
176pub struct BmpHeader64 {
177    pub signature: u32,
178    pub valid_dump: u32,
179    // According to rekall there's a gap there:
180    // 'ValidDump': [0x4, ['String', dict(
181    //    length=4,
182    //    term=None,
183    //    )]],
184    // # The offset of the first page in the file.
185    // 'FirstPage': [0x20, ['unsigned long long']],
186    padding1: [u8; 0x20 - (0x4 + mem::size_of::<u32>())],
187    /// The offset of the first page in the file.
188    pub first_page: u64,
189    /// Total number of pages present in the bitmap.
190    pub total_present_pages: u64,
191    /// Total number of pages in image. This dictates the total size of the
192    /// bitmap.This is not the same as the TotalPresentPages which is only
193    /// the sum of the bits set to 1.
194    pub pages: u64,
195    // Bitmap follows
196}
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    /// Calculate a physical address from a run and an index.
215    ///
216    /// The formulae is: (`base_page` + `page_idx`) * `PageKind::Normal.size()`.
217    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    // PHYSMEM_RUN Run[1]; follows
234}
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
386/// Peek for a `T` from the cursor.
387pub 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
399/// Read a `T` from the cursor.
400pub 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; // 'PMDR'
411const RDMP_HEADER64_EXPECTED_VALID_DUMP: u32 = 0x50_4D_55_44; // 'PMUD'
412
413#[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    // Bitmap follows
423}
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    // Bitmap follows
454}
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    // Bitmap follows
465}
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// Copied from `WDBGEXTS.H`.
503#[repr(C)]
504#[derive(Debug, Default)]
505pub struct DbgKdDebugDataHeader64 {
506    /// Link to other blocks
507    pub list: ListEntry<u64>,
508    /// This is a unique tag to identify the owner of the block.
509    /// If your component only uses one pool tag, use it for this, too.
510    pub owner_tag: u32,
511    /// This must be initialized to the size of the data block,
512    /// including this structure.
513    pub size: u32,
514}
515
516// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/um/WDBGEXTS.H#L1206C16-L1206C34
517#[repr(C)]
518#[derive(Debug, Default)]
519pub struct KdDebuggerData64 {
520    pub header: DbgKdDebugDataHeader64,
521    /// Base address of kernel image
522    pub kern_base: u64,
523    /// DbgBreakPointWithStatus is a function which takes an argument
524    /// and hits a breakpoint.  This field contains the address of the
525    /// breakpoint instruction.  When the debugger sees a breakpoint
526    /// at this address, it may retrieve the argument from the first
527    /// argument register, or on x86 the eax register.
528    pub breakpoint_with_status: u64,
529    /// Address of the saved context record during a bugcheck
530    /// N.B. This is an automatic in KeBugcheckEx's frame, and
531    /// is only valid after a bugcheck.
532    pub saved_context: u64,
533    /// The address of the thread structure is provided in the
534    /// WAIT_STATE_CHANGE packet.  This is the offset from the base of
535    /// the thread structure to the pointer to the kernel stack frame
536    /// for the currently active usermode callback.
537    pub th_callback_stack: u16,
538    //// saved pointer to next callback frame
539    pub next_callback: u16,
540    /// saved frame pointer
541    pub frame_pointer: u16,
542    /// pad to a quad boundary
543    pub pae_enabled: u16,
544    /// Address of the kernel callout routine.
545    pub ki_call_user_mode: u64,
546    /// Address of the usermode entry point for callbacks (in ntdll).
547    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    // NT 5.1 Addition
605    pub nt_build_lab: u64,
606    pub ki_normal_system_call: u64,
607    // NT 5.0 hotfix addition
608    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    // NT 5.1 Addition
620    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    // Server 2003 addition
625    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    // Longhorn addition
673    pub vf_crash_data_block: u64,
674    pub mm_bad_pages_detected: u64,
675    pub mm_zeroed_page_single_bit_errors_detected: u64,
676    // Windows 7 addition
677    pub etwp_debugger_data: u64,
678    pub offset_prcb_context: u16,
679    // ...
680}
681
682#[cfg(test)]
683mod tests {
684    use std::mem;
685
686    use crate::structs::{Context, Header64, PhysmemDesc, PhysmemRun};
687
688    /// Ensure that the sizes of key structures are right.
689    #[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}