Skip to main content

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, &ETHREAD.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}