wraith/structures/
peb.rs

1//! PEB (Process Environment Block) structure
2
3use super::ldr::PebLdrData;
4use super::offsets::PebOffsets;
5use crate::arch::{segment, NativePtr};
6use crate::error::{Result, WraithError};
7use crate::version::WindowsVersion;
8use core::ptr::NonNull;
9
10/// safe wrapper around PEB access
11pub struct Peb {
12    ptr: NonNull<u8>,
13    offsets: &'static PebOffsets,
14}
15
16impl Peb {
17    /// get PEB for current process
18    pub fn current() -> Result<Self> {
19        // SAFETY: segment::get_peb always returns valid PEB for current process
20        let ptr = unsafe { segment::get_peb() };
21        let ptr = NonNull::new(ptr).ok_or(WraithError::InvalidPebAccess)?;
22
23        let version = WindowsVersion::current()?;
24        let offsets = PebOffsets::for_version(&version)?;
25
26        Ok(Self { ptr, offsets })
27    }
28
29    /// raw PEB pointer
30    pub fn as_ptr(&self) -> *mut u8 {
31        self.ptr.as_ptr()
32    }
33
34    /// check BeingDebugged flag
35    pub fn being_debugged(&self) -> bool {
36        // SAFETY: ptr is valid, offset is correct for this version
37        unsafe {
38            let addr = self.ptr.as_ptr().add(self.offsets.being_debugged);
39            *(addr as *const u8) != 0
40        }
41    }
42
43    /// set BeingDebugged flag
44    ///
45    /// # Safety
46    /// modifies process state
47    pub unsafe fn set_being_debugged(&mut self, value: bool) {
48        unsafe {
49            let addr = self.ptr.as_ptr().add(self.offsets.being_debugged);
50            *(addr as *mut u8) = if value { 1 } else { 0 };
51        }
52    }
53
54    /// get NtGlobalFlag value
55    pub fn nt_global_flag(&self) -> u32 {
56        unsafe {
57            let addr = self.ptr.as_ptr().add(self.offsets.nt_global_flag);
58            *(addr as *const u32)
59        }
60    }
61
62    /// set NtGlobalFlag value
63    ///
64    /// # Safety
65    /// modifies process state
66    pub unsafe fn set_nt_global_flag(&mut self, value: u32) {
67        unsafe {
68            let addr = self.ptr.as_ptr().add(self.offsets.nt_global_flag);
69            *(addr as *mut u32) = value;
70        }
71    }
72
73    /// get pointer to PEB_LDR_DATA
74    pub fn ldr(&self) -> Option<&PebLdrData> {
75        unsafe {
76            let addr = self.ptr.as_ptr().add(self.offsets.ldr);
77            let ldr_ptr = *(addr as *const *const PebLdrData);
78            if ldr_ptr.is_null() {
79                None
80            } else {
81                Some(&*ldr_ptr)
82            }
83        }
84    }
85
86    /// get mutable pointer to PEB_LDR_DATA
87    ///
88    /// # Safety
89    /// caller must ensure exclusive access
90    pub unsafe fn ldr_mut(&mut self) -> Option<&mut PebLdrData> {
91        unsafe {
92            let addr = self.ptr.as_ptr().add(self.offsets.ldr);
93            let ldr_ptr = *(addr as *mut *mut PebLdrData);
94            if ldr_ptr.is_null() {
95                None
96            } else {
97                Some(&mut *ldr_ptr)
98            }
99        }
100    }
101
102    /// get image base address
103    pub fn image_base(&self) -> NativePtr {
104        unsafe {
105            let addr = self.ptr.as_ptr().add(self.offsets.image_base);
106            *(addr as *const NativePtr)
107        }
108    }
109
110    /// get process heap pointer
111    pub fn process_heap(&self) -> NativePtr {
112        unsafe {
113            let addr = self.ptr.as_ptr().add(self.offsets.process_heap);
114            *(addr as *const NativePtr)
115        }
116    }
117
118    /// get number of processors
119    pub fn number_of_processors(&self) -> u32 {
120        unsafe {
121            let addr = self.ptr.as_ptr().add(self.offsets.number_of_processors);
122            *(addr as *const u32)
123        }
124    }
125}
126
127// manual Send/Sync - PEB is process-wide but our wrapper is safe
128unsafe impl Send for Peb {}
129unsafe impl Sync for Peb {}