vmi_os_windows/comps/kprcb.rs
1use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead, os::ThreadObject};
2
3use crate::{
4 ArchAdapter, CONTEXT_AMD64, KSPECIAL_REGISTERS_AMD64, WindowsContext, WindowsError, WindowsOs,
5 WindowsSpecialRegisters, WindowsThread, offset,
6};
7
8/// A Windows kernel processor control block (KPRCB).
9///
10/// The KPRCB is an opaque, per-processor structure embedded within
11/// the KPCR. While the KPCR (`_KPCR`) is the top-level per-processor
12/// region (anchored at `gs:[0]`), the KPRCB is its main body, holding
13/// the current/next/idle thread pointers, saved processor context,
14/// and scheduling state.
15///
16/// # Implementation Details
17///
18/// Corresponds to `_KPRCB`.
19pub struct WindowsKernelProcessorBlock<'a, Driver>
20where
21 Driver: VmiRead,
22 Driver::Architecture: ArchAdapter<Driver>,
23{
24 /// The VMI state.
25 vmi: VmiState<'a, WindowsOs<Driver>>,
26
27 /// Address of the `_KPRCB` structure.
28 va: Va,
29}
30
31impl<Driver> VmiVa for WindowsKernelProcessorBlock<'_, Driver>
32where
33 Driver: VmiRead,
34 Driver::Architecture: ArchAdapter<Driver>,
35{
36 fn va(&self) -> Va {
37 self.va
38 }
39}
40
41impl<'a, Driver> WindowsKernelProcessorBlock<'a, Driver>
42where
43 Driver: VmiRead,
44 Driver::Architecture: ArchAdapter<Driver>,
45{
46 /// Creates a new kernel processor control block.
47 pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
48 Self { vmi, va }
49 }
50
51 /// Returns the thread currently executing on this processor.
52 ///
53 /// # Implementation Details
54 ///
55 /// Corresponds to `_KPRCB.CurrentThread`.
56 pub fn current_thread(&self) -> Result<WindowsThread<'a, Driver>, VmiError> {
57 let KPRCB = offset!(self.vmi, _KPRCB);
58
59 let result = self
60 .vmi
61 .read_va_native(self.va + KPRCB.CurrentThread.offset())?;
62
63 if result.is_null() {
64 return Err(WindowsError::CorruptedStruct("KPRCB.CurrentThread").into());
65 }
66
67 Ok(WindowsThread::new(self.vmi, ThreadObject(result)))
68 }
69
70 /// Returns the next thread scheduled to execute on this processor, if any.
71 ///
72 /// # Implementation Details
73 ///
74 /// Corresponds to `_KPRCB.NextThread`.
75 pub fn next_thread(&self) -> Result<Option<WindowsThread<'a, Driver>>, VmiError> {
76 let KPRCB = offset!(self.vmi, _KPRCB);
77
78 let result = self
79 .vmi
80 .read_va_native(self.va + KPRCB.NextThread.offset())?;
81
82 if result.is_null() {
83 // NextThread can be NULL.
84 return Ok(None);
85 }
86
87 Ok(Some(WindowsThread::new(self.vmi, ThreadObject(result))))
88 }
89
90 /// Returns the idle thread for this processor.
91 ///
92 /// # Implementation Details
93 ///
94 /// Corresponds to `_KPRCB.IdleThread`.
95 pub fn idle_thread(&self) -> Result<WindowsThread<'a, Driver>, VmiError> {
96 let KPRCB = offset!(self.vmi, _KPRCB);
97
98 let result = self
99 .vmi
100 .read_va_native(self.va + KPRCB.IdleThread.offset())?;
101
102 if result.is_null() {
103 return Err(WindowsError::CorruptedStruct("KPRCB.IdleThread").into());
104 }
105
106 Ok(WindowsThread::new(self.vmi, ThreadObject(result)))
107 }
108
109 /// Returns the processor's special registers.
110 ///
111 /// # Implementation Details
112 ///
113 /// Corresponds to `_KPRCB.ProcessorState.SpecialRegisters`.
114 pub fn processor_special_registers(&self) -> Result<impl WindowsSpecialRegisters, VmiError> {
115 let KPRCB = offset!(self.vmi, _KPRCB);
116 let KPROCESSOR_STATE = offset!(self.vmi, _KPROCESSOR_STATE);
117
118 self.vmi.read_struct::<KSPECIAL_REGISTERS_AMD64>(
119 self.va + KPRCB.ProcessorState.offset() + KPROCESSOR_STATE.SpecialRegisters.offset(),
120 )
121 }
122
123 /// Returns the processor's saved thread context.
124 ///
125 /// On Windows 7 and later, reads the context via `_KPRCB.Context` pointer,
126 /// which may reference a dynamically-allocated buffer for extended state
127 /// (XSAVE/AVX). Falls back to the embedded `ProcessorState.ContextFrame`
128 /// when the pointer is NULL (pre-Win7 or early boot).
129 ///
130 /// # Implementation Details
131 ///
132 /// Corresponds to `_KPRCB.Context` if non-NULL,
133 /// otherwise `_KPRCB.ProcessorState.ContextFrame`.
134 pub fn processor_context(&self) -> Result<impl WindowsContext, VmiError> {
135 let KPRCB = offset!(self.vmi, _KPRCB);
136 let KPROCESSOR_STATE = offset!(self.vmi, _KPROCESSOR_STATE);
137
138 // `KPRCB::Context` is present since Windows 7. It is a pointer that
139 // normally points to `ProcessorState.ContextFrame`, but may point to a
140 // larger dynamically-allocated buffer when the CPU supports extended
141 // state (XSAVE/AVX).
142 //
143 // On pre-Win7 systems the field doesn't exist, and during very early
144 // boot (before `KiInitializePcr` runs) it may be NULL.
145 //
146 // In either case, fall back to reading `ProcessorState.ContextFrame`
147 // directly.
148
149 let addr = self.vmi.read_va_native(self.va + KPRCB.Context.offset())?;
150
151 if addr.is_null() {
152 self.vmi.read_struct::<CONTEXT_AMD64>(
153 self.va + KPRCB.ProcessorState.offset() + KPROCESSOR_STATE.ContextFrame.offset(),
154 )
155 }
156 else {
157 self.vmi.read_struct::<CONTEXT_AMD64>(addr)
158 }
159 }
160
161 /// Returns the processor's saved thread context, read directly from the
162 /// embedded `ProcessorState.ContextFrame`.
163 ///
164 /// Useful when the `_KPRCB.Context` is unpopulated. Crash dumps from older
165 /// Windows builds (Windows 7) leave `_KPRCB.Context` zero on the crashing
166 /// CPU and write the registers into the embedded frame instead.
167 ///
168 /// # Implementation Details
169 ///
170 /// Corresponds to `_KPRCB.ProcessorState.ContextFrame`.
171 pub fn processor_context_frame(&self) -> Result<impl WindowsContext, VmiError> {
172 let KPRCB = offset!(self.vmi, _KPRCB);
173 let KPROCESSOR_STATE = offset!(self.vmi, _KPROCESSOR_STATE);
174
175 self.vmi.read_struct::<CONTEXT_AMD64>(
176 self.va + KPRCB.ProcessorState.offset() + KPROCESSOR_STATE.ContextFrame.offset(),
177 )
178 }
179}