Skip to main content

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}