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