vmi_os_windows/comps/object/thread.rs
1use vmi_core::{
2 Architecture, Va, VcpuId, VmiError, VmiState, VmiVa,
3 driver::VmiRead,
4 os::{ProcessObject, ThreadId, ThreadObject, VmiOsProcess as _, VmiOsThread},
5};
6
7use super::{
8 super::{WindowsProcessorMode, WindowsTeb, WindowsTrapFrame, WindowsWow64Kind},
9 FromWindowsObject, WindowsObject, WindowsObjectTypeKind, WindowsProcess, WindowsToken,
10};
11use crate::{ArchAdapter, WindowsOs, WindowsOsExt as _, offset};
12
13/// Windows kernel thread state (`KTHREAD_STATE`).
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum WindowsThreadState {
16 /// Thread has been initialized but not yet started.
17 Initialized,
18 /// Thread is ready to run.
19 Ready,
20 /// Thread is currently running.
21 Running,
22 /// Thread is selected to run next on a processor.
23 Standby,
24 /// Thread has terminated.
25 Terminated,
26 /// Thread is waiting for an event.
27 Waiting,
28 /// Thread is transitioning between states.
29 Transition,
30 /// Thread is ready to run but deferred.
31 DeferredReady,
32 /// Obsolete gate wait state.
33 GateWaitObsolete,
34 /// Thread is waiting for process in swap.
35 WaitingForProcessInSwap,
36 /// Unknown state value not covered by known variants.
37 Unknown(u8),
38}
39
40impl From<u8> for WindowsThreadState {
41 fn from(value: u8) -> Self {
42 match value {
43 0 => Self::Initialized,
44 1 => Self::Ready,
45 2 => Self::Running,
46 3 => Self::Standby,
47 4 => Self::Terminated,
48 5 => Self::Waiting,
49 6 => Self::Transition,
50 7 => Self::DeferredReady,
51 8 => Self::GateWaitObsolete,
52 9 => Self::WaitingForProcessInSwap,
53 other => Self::Unknown(other),
54 }
55 }
56}
57
58/// Windows thread wait reason (`KWAIT_REASON`).
59///
60/// The unprefixed variants `Executive`..`UserRequest` are the kernel's own
61/// waits. Their `Wr`-prefixed duals `WrExecutive`..`WrUserRequest` are the
62/// same waits performed on behalf of user mode, set when
63/// `KeWaitForSingleObject` is called with `WaitMode = UserMode`. Variants past
64/// `WrUserRequest` identify specific subsystems such as LPC, MM, scheduler,
65/// and locks.
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum WindowsThreadWaitReason {
68 /// Kernel-initiated generic synchronization wait on an executive object.
69 Executive,
70 /// Waiting for a free page in the zero/free page lists.
71 FreePage,
72 /// Waiting for an in-progress hard-fault page read to complete.
73 PageIn,
74 /// Waiting for paged or nonpaged pool to have memory available.
75 PoolAllocation,
76 /// Self-initiated sleep via `KeDelayExecutionThread`.
77 DelayExecution,
78 /// Thread is suspended via `NtSuspendThread` or an APC.
79 Suspended,
80 /// User-mode wait issued by a routine such as `WaitForSingleObject`.
81 UserRequest,
82
83 /// Waiting on an executive object on behalf of user mode.
84 WrExecutive,
85 /// User-mode free-page wait, typically for paging.
86 WrFreePage,
87 /// Hard page-in wait on behalf of user mode.
88 WrPageIn,
89 /// Pool-allocation wait on behalf of user mode.
90 WrPoolAllocation,
91 /// User-mode delayed execution via `NtDelayExecution`.
92 WrDelayExecution,
93 /// User-mode thread suspension.
94 WrSuspended,
95 /// Alertable user-mode wait on an object.
96 WrUserRequest,
97 /// Waiting on an LPC event pair for a paired client/server handshake.
98 WrEventPair,
99 /// Waiting for a `KQUEUE` entry used by I/O completion and worker
100 /// threads.
101 WrQueue,
102 /// LPC server waiting to receive a message from a client.
103 WrLpcReceive,
104 /// LPC client waiting for a reply from the server.
105 WrLpcReply,
106 /// Waiting on a virtual-memory operation that mutates the address space.
107 WrVirtualMemory,
108 /// Waiting for modified-page writer to complete a page-out.
109 WrPageOut,
110 /// Waiting at a rendezvous point for another thread/processor.
111 WrRendezvous,
112 /// Waiting on a keyed event, used by critical sections.
113 WrKeyedEvent,
114 /// Thread has terminated. Wait is for teardown bookkeeping.
115 WrTerminated,
116 /// Waiting for the process to be swapped back into memory.
117 WrProcessInSwap,
118 /// Blocked by CPU rate-control / job CPU throttling.
119 WrCpuRateControl,
120 /// Waiting on a user-mode stack-switch callout.
121 WrCalloutStack,
122 /// Generic in-kernel wait not covered by a specific reason.
123 WrKernel,
124 /// Contending for an `ERESOURCE` executive resource.
125 WrResource,
126 /// Contending for an `EX_PUSH_LOCK`.
127 WrPushLock,
128 /// Waiting to acquire a `KMUTEX` / `KMUTANT`.
129 WrMutex,
130 /// Quantum has ended. Rescheduled pending context switch.
131 WrQuantumEnd,
132 /// Awaiting a dispatch interrupt to run the scheduler.
133 WrDispatchInt,
134 /// Preempted by a higher-priority thread.
135 WrPreempted,
136 /// Voluntarily yielded CPU via `NtYieldExecution`.
137 WrYieldExecution,
138 /// Contending for a `FAST_MUTEX`.
139 WrFastMutex,
140 /// Contending for a `KGUARDED_MUTEX`.
141 WrGuardedMutex,
142 /// Blocked on an `EX_RUNDOWN_REF` rundown-protection drain.
143 WrRundown,
144 /// Blocked on `NtWaitForAlertByThreadId` / thread-ID alert.
145 WrAlertByThreadId,
146 /// Preemption deferred pending a scheduler decision.
147 WrDeferredPreempt,
148 /// Waiting to service a hardware/physical memory fault.
149 WrPhysicalFault,
150 /// Blocked on an I/O ring submission/completion.
151 WrIoRing,
152 /// Waiting for an MDL cache slot to become available.
153 WrMdlCache,
154 /// Blocked inside an RCU grace period.
155 WrRcu,
156
157 /// Unknown wait reason value not covered by known variants.
158 Unknown(u8),
159}
160
161impl From<u8> for WindowsThreadWaitReason {
162 fn from(value: u8) -> Self {
163 match value {
164 0 => Self::Executive,
165 1 => Self::FreePage,
166 2 => Self::PageIn,
167 3 => Self::PoolAllocation,
168 4 => Self::DelayExecution,
169 5 => Self::Suspended,
170 6 => Self::UserRequest,
171 7 => Self::WrExecutive,
172 8 => Self::WrFreePage,
173 9 => Self::WrPageIn,
174 10 => Self::WrPoolAllocation,
175 11 => Self::WrDelayExecution,
176 12 => Self::WrSuspended,
177 13 => Self::WrUserRequest,
178 14 => Self::WrEventPair, // WrSpare0 since Windows 8.1
179 15 => Self::WrQueue,
180 16 => Self::WrLpcReceive,
181 17 => Self::WrLpcReply,
182 18 => Self::WrVirtualMemory,
183 19 => Self::WrPageOut,
184 20 => Self::WrRendezvous,
185 21 => Self::WrKeyedEvent,
186 22 => Self::WrTerminated,
187 23 => Self::WrProcessInSwap,
188 24 => Self::WrCpuRateControl,
189 25 => Self::WrCalloutStack,
190 26 => Self::WrKernel,
191 27 => Self::WrResource,
192 28 => Self::WrPushLock,
193 29 => Self::WrMutex,
194 30 => Self::WrQuantumEnd,
195 31 => Self::WrDispatchInt,
196 32 => Self::WrPreempted,
197 33 => Self::WrYieldExecution,
198 34 => Self::WrFastMutex,
199 35 => Self::WrGuardedMutex,
200 36 => Self::WrRundown,
201 37 => Self::WrAlertByThreadId,
202 38 => Self::WrDeferredPreempt,
203 39 => Self::WrPhysicalFault,
204 40 => Self::WrIoRing,
205 41 => Self::WrMdlCache,
206 42 => Self::WrRcu,
207 other => Self::Unknown(other),
208 }
209 }
210}
211
212/// A Windows thread.
213///
214/// A thread in Windows is represented by the `_ETHREAD` structure,
215/// which contains metadata about its execution state, context, and scheduling.
216///
217/// # Implementation Details
218///
219/// Corresponds to `_ETHREAD`.
220pub struct WindowsThread<'a, Driver>
221where
222 Driver: VmiRead,
223 Driver::Architecture: ArchAdapter<Driver>,
224{
225 /// The VMI state.
226 vmi: VmiState<'a, WindowsOs<Driver>>,
227
228 /// Address of the `_ETHREAD` structure.
229 va: Va,
230}
231
232impl<'a, Driver> From<WindowsThread<'a, Driver>> for WindowsObject<'a, Driver>
233where
234 Driver: VmiRead,
235 Driver::Architecture: ArchAdapter<Driver>,
236{
237 fn from(value: WindowsThread<'a, Driver>) -> Self {
238 Self::new(value.vmi, value.va)
239 }
240}
241
242impl<'a, Driver> FromWindowsObject<'a, Driver> for WindowsThread<'a, Driver>
243where
244 Driver: VmiRead,
245 Driver::Architecture: ArchAdapter<Driver>,
246{
247 fn from_object(object: WindowsObject<'a, Driver>) -> Result<Option<Self>, VmiError> {
248 match object.type_kind()? {
249 Some(WindowsObjectTypeKind::Thread) => {
250 Ok(Some(Self::new(object.vmi, ThreadObject(object.va))))
251 }
252 _ => Ok(None),
253 }
254 }
255}
256
257impl<Driver> VmiVa for WindowsThread<'_, Driver>
258where
259 Driver: VmiRead,
260 Driver::Architecture: ArchAdapter<Driver>,
261{
262 fn va(&self) -> Va {
263 self.va
264 }
265}
266
267impl<'a, Driver> WindowsThread<'a, Driver>
268where
269 Driver: VmiRead,
270 Driver::Architecture: ArchAdapter<Driver>,
271{
272 /// Creates a new Windows thread.
273 pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, thread: ThreadObject) -> Self {
274 Self { vmi, va: thread.0 }
275 }
276
277 /// Returns the process object associated with the thread.
278 ///
279 /// # Implementation Details
280 ///
281 /// Corresponds to `_KTHREAD.Process`.
282 pub fn process(&self) -> Result<WindowsProcess<'a, Driver>, VmiError> {
283 let KTHREAD = offset!(self.vmi, _KTHREAD);
284
285 let process = self
286 .vmi
287 .read_va_native(self.va + KTHREAD.Process.offset())?;
288
289 Ok(WindowsProcess::new(self.vmi, ProcessObject(process)))
290 }
291
292 /// Returns the index into `KTHREAD.ApcStatePointer` selecting the
293 /// thread's currently-active APC environment.
294 ///
295 /// * `0` (`OriginalApcEnvironment`): thread is running in its original
296 /// process.
297 /// * `1` (`AttachedApcEnvironment`): thread is temporarily attached
298 /// to a foreign process via `KeStackAttachProcess` / `KeAttachProcess`.
299 ///
300 /// The original `KTHREAD.ApcState` is preserved in `KTHREAD.SavedApcState`.
301 ///
302 /// # Implementation Details
303 ///
304 /// Corresponds to `_KTHREAD.ApcStateIndex`.
305 pub fn apc_state_index(&self) -> Result<u8, VmiError> {
306 let KTHREAD = offset!(self.vmi, _KTHREAD);
307
308 self.vmi.read_u8(self.va + KTHREAD.ApcStateIndex.offset())
309 }
310
311 /// Checks if the thread is currently attached to foreign process context.
312 ///
313 /// # Implementation Details
314 ///
315 /// Corresponds to `_KTHREAD.ApcStateIndex != 0`.
316 pub fn is_attached(&self) -> Result<bool, VmiError> {
317 Ok(self.apc_state_index()? != 0)
318 }
319
320 /// Returns the process whose address space the thread is currently executing in.
321 ///
322 /// # Implementation Details
323 ///
324 /// Corresponds to `_KTHREAD.ApcState.Process`.
325 pub fn current_process(&self) -> Result<WindowsProcess<'a, Driver>, VmiError> {
326 let KTHREAD = offset!(self.vmi, _KTHREAD);
327 let KAPC_STATE = offset!(self.vmi, _KAPC_STATE);
328
329 let process = self
330 .vmi
331 .read_va_native(self.va + KTHREAD.ApcState.offset() + KAPC_STATE.Process.offset())?;
332
333 Ok(WindowsProcess::new(self.vmi, ProcessObject(process)))
334 }
335
336 /// Returns the thread's saved home process, or NULL if the thread is not attached.
337 ///
338 /// # Implementation Details
339 ///
340 /// Corresponds to `_KTHREAD.SavedApcState.Process`.
341 pub fn saved_process(&self) -> Result<Option<WindowsProcess<'a, Driver>>, VmiError> {
342 let KTHREAD = offset!(self.vmi, _KTHREAD);
343 let KAPC_STATE = offset!(self.vmi, _KAPC_STATE);
344
345 let process = self.vmi.read_va_native(
346 self.va + KTHREAD.SavedApcState.offset() + KAPC_STATE.Process.offset(),
347 )?;
348
349 if process.is_null() {
350 return Ok(None);
351 }
352
353 Ok(Some(WindowsProcess::new(self.vmi, ProcessObject(process))))
354 }
355
356 /// Returns the thread's impersonation token, or `None` when the
357 /// thread is not currently impersonating.
358 ///
359 /// # Implementation Details
360 ///
361 /// Corresponds to `_ETHREAD.ClientSecurity.ImpersonationToken`, gated
362 /// on `_ETHREAD.ActiveImpersonationInfo`.
363 pub fn impersonation_token(&self) -> Result<Option<WindowsToken<'a, Driver>>, VmiError> {
364 let ETHREAD = offset!(self.vmi, _ETHREAD);
365 let PS_CLIENT_SECURITY_CONTEXT = offset!(self.vmi, _PS_CLIENT_SECURITY_CONTEXT);
366
367 let active = self
368 .vmi
369 .read_field(self.va, ÐREAD.ActiveImpersonationInfo)?;
370
371 if ETHREAD.ActiveImpersonationInfo.extract(active) == 0 {
372 return Ok(None);
373 }
374
375 let token = self.vmi.os().read_fast_ref(
376 self.va
377 + ETHREAD.ClientSecurity.offset()
378 + PS_CLIENT_SECURITY_CONTEXT.ImpersonationToken.offset(),
379 )?;
380
381 Ok(Some(WindowsToken::new(self.vmi, token)))
382 }
383
384 /// Returns the ID of the processor the thread is bound to.
385 ///
386 /// For a [`Running`] thread this is the CPU currently executing it.
387 /// For a [`Ready`] or [`Standby`] thread this is the CPU the scheduler
388 /// has selected for its next run.
389 ///
390 /// # Implementation Details
391 ///
392 /// Corresponds to `_KTHREAD.NextProcessor`.
393 ///
394 /// [`Running`]: WindowsThreadState::Running
395 /// [`Ready`]: WindowsThreadState::Ready
396 /// [`Standby`]: WindowsThreadState::Standby
397 pub fn next_processor(&self) -> Result<VcpuId, VmiError> {
398 let KTHREAD = offset!(self.vmi, _KTHREAD);
399
400 let next_processor = self
401 .vmi
402 .read_u32(self.va + KTHREAD.NextProcessor.offset())?;
403
404 // In newer Windows versions, the `NextProcessor` field is a union:
405 //
406 // union {
407 // volatile ULONG NextProcessor;
408 //
409 // struct {
410 // ULONG NextProcessorNumber : 31;
411 // ULONG SharedReadyQueue : 1;
412 // };
413 // };
414
415 // Mask out the `SharedReadyQueue` bit.
416 let next_processor = next_processor & 0x7FFFFFFF;
417
418 Ok(VcpuId(next_processor as u16))
419 }
420
421 /// Returns whether the thread is currently alertable.
422 ///
423 /// # Notes
424 ///
425 /// Usually only trustworthy when `_KTHREAD.State == Waiting`.
426 ///
427 /// # Implementation Details
428 ///
429 /// Corresponds to `_KTHREAD.Alertable`.
430 pub fn alertable(&self) -> Result<bool, VmiError> {
431 let KTHREAD = offset!(self.vmi, _KTHREAD);
432
433 let alertable = self.vmi.read_field(self.va, &KTHREAD.Alertable)?;
434
435 Ok(KTHREAD.Alertable.extract(alertable) != 0)
436 }
437
438 /// Returns the thread's wait mode.
439 ///
440 /// # Notes
441 ///
442 /// Usually only trustworthy when `_KTHREAD.State == Waiting`.
443 ///
444 /// # Implementation Details
445 ///
446 /// Corresponds to `_KTHREAD.WaitMode`.
447 pub fn wait_mode(&self) -> Result<WindowsProcessorMode, VmiError> {
448 let KTHREAD = offset!(self.vmi, _KTHREAD);
449
450 let value = self.vmi.read_u8(self.va + KTHREAD.WaitMode.offset())?;
451 Ok(WindowsProcessorMode::from(value))
452 }
453
454 /// Returns the thread's wait reason.
455 ///
456 /// # Notes
457 ///
458 /// Usually only trustworthy when `_KTHREAD.State == Waiting`.
459 ///
460 /// # Implementation Details
461 ///
462 /// Corresponds to `_KTHREAD.WaitReason`.
463 pub fn wait_reason(&self) -> Result<WindowsThreadWaitReason, VmiError> {
464 let KTHREAD = offset!(self.vmi, _KTHREAD);
465
466 let value = self.vmi.read_u8(self.va + KTHREAD.WaitReason.offset())?;
467 Ok(WindowsThreadWaitReason::from(value))
468 }
469
470 /// Returns the thread's TEB.
471 ///
472 /// # Implementation Details
473 ///
474 /// Corresponds to `_KTHREAD.Teb` for the native TEB, and
475 /// `Teb64 + ROUND_TO_PAGES(sizeof(TEB))` for the WoW64 TEB.
476 pub fn teb(&self) -> Result<Option<WindowsTeb<'a, Driver>>, VmiError> {
477 let TEB = offset!(self.vmi, _TEB);
478
479 let teb = match self.native_teb()? {
480 Some(teb) => teb,
481 None => return Ok(None),
482 };
483
484 let owning_process = self.process()?;
485 if !owning_process.is_wow64()? {
486 return Ok(Some(teb));
487 }
488
489 // #define WOW64_GET_TEB32_SAFE(teb64) \
490 // ((PTEB32) ((ULONGLONG)teb64 + WOW64_ROUND_TO_PAGES (sizeof (TEB))))
491
492 let va = teb.va() + Driver::Architecture::va_align_up(Va(TEB.len() as u64));
493 let root = owning_process.translation_root()?;
494
495 Ok(Some(WindowsTeb::with_kind(
496 self.vmi,
497 va,
498 root,
499 WindowsWow64Kind::X86,
500 )))
501 }
502
503 /// Returns the thread's native TEB.
504 ///
505 /// # Implementation Details
506 ///
507 /// Corresponds to `_KTHREAD.Teb`.
508 pub fn native_teb(&self) -> Result<Option<WindowsTeb<'a, Driver>>, VmiError> {
509 let KTHREAD = offset!(self.vmi, _KTHREAD);
510
511 let va = self.vmi.read_va_native(self.va + KTHREAD.Teb.offset())?;
512
513 if va.is_null() {
514 return Ok(None);
515 }
516
517 let root = self.process()?.translation_root()?;
518
519 Ok(Some(WindowsTeb::with_kind(
520 self.vmi,
521 va,
522 root,
523 WindowsWow64Kind::Native,
524 )))
525 }
526
527 /// Returns the thread's trap frame.
528 ///
529 /// Points to the most recent user-to-kernel transition trap frame for the thread.
530 /// It records the user-mode register state that was captured when the thread
531 /// entered the kernel via a syscall, interrupt, or exception.
532 ///
533 /// Can be NULL when the thread is executing purely in kernel mode and has not
534 /// entered via a user-mode trap.
535 ///
536 /// # Implementation Details
537 ///
538 /// Corresponds to `_KTHREAD.TrapFrame`.
539 pub fn trap_frame(&self) -> Result<Option<WindowsTrapFrame<'a, Driver>>, VmiError> {
540 let KTHREAD = offset!(self.vmi, _KTHREAD);
541
542 let va = self
543 .vmi
544 .read_va_native(self.va + KTHREAD.TrapFrame.offset())?;
545
546 if va.is_null() {
547 return Ok(None);
548 }
549
550 Ok(Some(WindowsTrapFrame::new(self.vmi, va)))
551 }
552
553 /// Returns the thread's scheduling state.
554 ///
555 /// # Implementation Details
556 ///
557 /// Corresponds to `_KTHREAD.State`.
558 pub fn state(&self) -> Result<WindowsThreadState, VmiError> {
559 let KTHREAD = offset!(self.vmi, _KTHREAD);
560
561 let value = self.vmi.read_u8(self.va + KTHREAD.State.offset())?;
562 Ok(WindowsThreadState::from(value))
563 }
564
565 /// Returns the saved kernel stack pointer for this thread.
566 ///
567 /// For threads that are not currently running, this is the stack pointer
568 /// value saved during the last context switch (KiSwapContext).
569 ///
570 /// # Implementation Details
571 ///
572 /// Corresponds to `_KTHREAD.KernelStack`.
573 pub fn kernel_stack(&self) -> Result<Va, VmiError> {
574 let KTHREAD = offset!(self.vmi, _KTHREAD);
575
576 self.vmi
577 .read_va_native(self.va + KTHREAD.KernelStack.offset())
578 }
579}
580
581impl<'a, Driver> VmiOsThread<'a, Driver> for WindowsThread<'a, Driver>
582where
583 Driver: VmiRead,
584 Driver::Architecture: ArchAdapter<Driver>,
585{
586 type Os = WindowsOs<Driver>;
587
588 /// Returns the thread ID.
589 ///
590 /// # Implementation Details
591 ///
592 /// Corresponds to `_ETHREAD.Cid.UniqueThread`.
593 fn id(&self) -> Result<ThreadId, VmiError> {
594 let ETHREAD = offset!(self.vmi, _ETHREAD);
595 let CLIENT_ID = offset!(self.vmi, _CLIENT_ID);
596
597 let result = self
598 .vmi
599 .read_u32(self.va + ETHREAD.Cid.offset() + CLIENT_ID.UniqueThread.offset())?;
600
601 Ok(ThreadId(result))
602 }
603
604 /// Returns the thread object.
605 fn object(&self) -> Result<ThreadObject, VmiError> {
606 Ok(ThreadObject(self.va))
607 }
608}