1#![cfg(windows)]
6#![warn(
7 missing_docs,
8 missing_copy_implementations,
9 missing_debug_implementations
10)]
11
12use widestring::U16CString;
13use winapi::shared::minwindef::{BOOL, HMODULE, LPCVOID};
14use winapi::um::{
15 handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
16 tlhelp32::*,
17 winnt::HANDLE,
18};
19
20use std::{
21 fmt,
22 io::{Error, Result},
23 iter::{FusedIterator, Iterator},
24 mem,
25};
26
27type Tl32helpFunc<T> = unsafe extern "system" fn(HANDLE, *mut T) -> BOOL;
28
29macro_rules! to_u16cstring {
30 ($ident:expr) => {
31 U16CString::from_vec_with_nul(Box::new($ident) as Box<[u16]>).unwrap_or_default()
32 };
33}
34
35pub fn read_process_memory(
38 process_id: u32,
39 base_address: LPCVOID,
40 buffer: &mut [u8],
41) -> Result<usize> {
42 let mut num_bytes_read = 0;
43 if unsafe {
44 Toolhelp32ReadProcessMemory(
45 process_id,
46 base_address,
47 buffer.as_mut_ptr() as *mut _,
48 buffer.len(),
49 &mut num_bytes_read,
50 )
51 } == 0
52 {
53 Err(Error::last_os_error())
54 } else {
55 Ok(num_bytes_read)
56 }
57}
58
59pub trait TagTl32: private::Sealed {
61 type Raw: Copy;
63 const FLAGS: u32;
65 const ITER_FIRST: Tl32helpFunc<Self::Raw>;
67 const ITER_NEXT: Tl32helpFunc<Self::Raw>;
69
70 fn init_raw() -> Self::Raw;
72
73 fn from_raw(raw: Self::Raw) -> Self;
75}
76
77mod private {
78 pub trait Sealed {}
79 impl Sealed for super::ProcessEntry {}
80 impl Sealed for super::HeapList {}
81 impl Sealed for super::ModuleEntry {}
82 impl Sealed for super::ThreadEntry {}
83}
84
85#[allow(missing_docs)]
88#[derive(Clone)]
89pub struct ProcessEntry {
90 pub process_id: u32,
91 pub cnt_threads: u32,
92 pub parent_process_id: u32,
93 pub pc_pri_class_base: i32,
94 pub sz_exe_file: U16CString,
95}
96
97impl TagTl32 for ProcessEntry {
98 type Raw = PROCESSENTRY32W;
99 const FLAGS: u32 = TH32CS_SNAPPROCESS;
100 const ITER_FIRST: Tl32helpFunc<Self::Raw> = Process32FirstW;
101 const ITER_NEXT: Tl32helpFunc<Self::Raw> = Process32NextW;
102
103 #[inline]
104 fn init_raw() -> Self::Raw {
105 Self::Raw {
106 dwSize: mem::size_of::<Self::Raw>() as u32,
107 ..unsafe { mem::uninitialized() }
108 }
109 }
110
111 #[inline]
112 fn from_raw(raw: Self::Raw) -> Self {
113 ProcessEntry {
114 process_id: raw.th32ProcessID,
115 cnt_threads: raw.cntThreads,
116 parent_process_id: raw.th32ParentProcessID,
117 pc_pri_class_base: raw.pcPriClassBase,
118 sz_exe_file: to_u16cstring!(raw.szExeFile),
119 }
120 }
121}
122
123impl fmt::Debug for ProcessEntry {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 f.debug_struct("ProcessEntry")
126 .field("process_id", &self.process_id)
127 .field("cnt_threads", &self.cnt_threads)
128 .field("parent_process_id", &self.parent_process_id)
129 .field("pc_pri_class_base", &self.pc_pri_class_base)
130 .field(
131 "sz_exe_file",
132 &self.sz_exe_file.to_string().unwrap_or_default(),
133 )
134 .finish()
135 }
136}
137
138#[allow(missing_docs)]
141#[derive(Clone)]
142pub struct ModuleEntry {
143 pub process_id: u32,
144 pub base_addr: *mut u8,
145 pub base_size: u32,
146 pub h_module: HMODULE,
147 pub sz_module: U16CString,
148 pub sz_exe_path: U16CString,
149}
150
151impl TagTl32 for ModuleEntry {
152 type Raw = MODULEENTRY32W;
153 const FLAGS: u32 = TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32;
154 const ITER_FIRST: Tl32helpFunc<Self::Raw> = Module32FirstW;
155 const ITER_NEXT: Tl32helpFunc<Self::Raw> = Module32NextW;
156
157 #[inline]
158 fn init_raw() -> Self::Raw {
159 Self::Raw {
160 dwSize: mem::size_of::<Self::Raw>() as u32,
161 ..unsafe { mem::uninitialized() }
162 }
163 }
164
165 #[inline]
166 fn from_raw(raw: Self::Raw) -> Self {
167 ModuleEntry {
168 process_id: raw.th32ProcessID,
169 base_addr: raw.modBaseAddr,
170 base_size: raw.modBaseSize,
171 h_module: raw.hModule,
172 sz_module: to_u16cstring!(raw.szModule),
173 sz_exe_path: to_u16cstring!(raw.szExePath),
174 }
175 }
176}
177
178impl fmt::Debug for ModuleEntry {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 f.debug_struct("ProcessEntry")
181 .field("process_id", &self.process_id)
182 .field("base_addr", &self.base_addr)
183 .field("base_size", &self.base_size)
184 .field("h_module", &self.h_module)
185 .field("sz_module", &self.sz_module.to_string().unwrap_or_default())
186 .field(
187 "sz_exe_file",
188 &self.sz_exe_path.to_string().unwrap_or_default(),
189 )
190 .finish()
191 }
192}
193
194#[allow(missing_docs, missing_copy_implementations)]
197pub struct HeapList {
198 pub process_id: u32,
199 pub heap_id: usize,
200 pub flags: u32,
201 current: Option<HEAPENTRY32>,
202}
203
204impl TagTl32 for HeapList {
205 type Raw = HEAPLIST32;
206 const FLAGS: u32 = TH32CS_SNAPHEAPLIST;
207 const ITER_FIRST: Tl32helpFunc<Self::Raw> = Heap32ListFirst;
208 const ITER_NEXT: Tl32helpFunc<Self::Raw> = Heap32ListNext;
209
210 #[inline]
211 fn init_raw() -> Self::Raw {
212 Self::Raw {
213 dwSize: mem::size_of::<Self::Raw>(),
214 ..unsafe { mem::uninitialized() }
215 }
216 }
217
218 #[inline]
219 fn from_raw(raw: Self::Raw) -> Self {
220 let mut entry = HEAPENTRY32 {
221 dwSize: mem::size_of::<HEAPENTRY32>(),
222 ..unsafe { mem::uninitialized() }
223 };
224 let current = if unsafe { Heap32First(&mut entry, raw.th32ProcessID, raw.th32HeapID) == 0 }
225 {
226 None
227 } else {
228 Some(entry)
229 };
230 HeapList {
231 process_id: raw.th32ProcessID,
232 heap_id: raw.th32HeapID,
233 flags: raw.dwFlags,
234 current,
235 }
236 }
237}
238
239impl Iterator for HeapList {
240 type Item = HeapEntry;
241 fn next(&mut self) -> Option<Self::Item> {
242 let val = HeapEntry::from_raw(self.current?);
243 if unsafe { Heap32Next(self.current.as_mut().unwrap()) == 0 } {
244 self.current = None
245 }
246 Some(val)
247 }
248}
249
250impl fmt::Debug for HeapList {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 f.debug_struct("HeapList")
253 .field("process_id", &self.process_id)
254 .field("heap_id", &self.heap_id)
255 .field("flags", &self.flags)
256 .field("exhausted", &self.current.is_none())
257 .finish()
258 }
259}
260
261#[allow(missing_docs)]
264#[derive(Clone, Copy, Debug)]
265pub struct HeapEntry {
266 pub handle: HANDLE,
267 pub address: usize,
268 pub block_size: usize,
269 pub flags: u32,
270 pub process_id: u32,
271 pub heap_id: usize,
272}
273
274impl HeapEntry {
275 fn from_raw(raw: HEAPENTRY32) -> Self {
276 HeapEntry {
277 handle: raw.hHandle,
278 address: raw.dwAddress,
279 block_size: raw.dwBlockSize,
280 flags: raw.dwFlags,
281 process_id: raw.th32ProcessID,
282 heap_id: raw.th32HeapID,
283 }
284 }
285}
286
287#[allow(missing_docs)]
290#[derive(Clone, Copy, Debug)]
291pub struct ThreadEntry {
292 pub thread_id: u32,
293 pub owner_process_id: u32,
294 pub base_pri: i32,
295}
296
297impl TagTl32 for ThreadEntry {
298 type Raw = THREADENTRY32;
299 const FLAGS: u32 = TH32CS_SNAPTHREAD;
300 const ITER_FIRST: Tl32helpFunc<Self::Raw> = Thread32First;
301 const ITER_NEXT: Tl32helpFunc<Self::Raw> = Thread32Next;
302
303 #[inline]
304 fn init_raw() -> Self::Raw {
305 Self::Raw {
306 dwSize: mem::size_of::<Self::Raw>() as u32,
307 ..unsafe { mem::uninitialized() }
308 }
309 }
310
311 #[inline]
312 fn from_raw(raw: Self::Raw) -> Self {
313 ThreadEntry {
314 thread_id: raw.th32ThreadID,
315 owner_process_id: raw.th32OwnerProcessID,
316 base_pri: raw.tpBasePri,
317 }
318 }
319}
320
321#[derive(Debug)]
324pub struct Snapshot<T: TagTl32> {
325 snapshot: HANDLE,
326 current: Option<T::Raw>,
327}
328
329impl<T: TagTl32> Snapshot<T> {
330 #[inline]
331 fn new(pid: u32) -> Result<Self> {
332 unsafe { Self::from_handle(CreateToolhelp32Snapshot(T::FLAGS, pid)) }
333 }
334
335 pub unsafe fn from_handle(snapshot: HANDLE) -> Result<Self> {
340 match snapshot {
341 INVALID_HANDLE_VALUE => Err(Error::last_os_error()),
342 snapshot => {
343 let mut entry = T::init_raw();
344 let current = if T::ITER_FIRST(snapshot, &mut entry) == 0 {
345 None
346 } else {
347 Some(entry)
348 };
349 Ok(Snapshot { snapshot, current })
350 }
351 }
352 }
353
354 pub fn handle(&self) -> HANDLE {
356 self.snapshot
357 }
358}
359
360impl Snapshot<ProcessEntry> {
361 pub fn new_process() -> Result<Self> {
373 Self::new(0)
374 }
375}
376
377impl Snapshot<HeapList> {
378 pub fn new_heap_list(pid: u32) -> Result<Self> {
391 Self::new(pid)
392 }
393}
394
395impl Snapshot<ModuleEntry> {
396 pub fn new_module(pid: u32) -> Result<Self> {
408 Self::new(pid)
409 }
410}
411
412impl Snapshot<ThreadEntry> {
413 pub fn new_thread() -> Result<Self> {
425 Self::new(0)
426 }
427}
428
429impl<T: TagTl32> Iterator for Snapshot<T> {
430 type Item = T;
431 fn next(&mut self) -> Option<Self::Item> {
432 let val = T::from_raw(self.current?);
433 if unsafe { T::ITER_NEXT(self.snapshot, self.current.as_mut().unwrap()) == 0 } {
434 self.current = None
435 }
436 Some(val)
437 }
438}
439
440impl<T: TagTl32> FusedIterator for Snapshot<T> {}
441
442impl<T: TagTl32> Drop for Snapshot<T> {
443 fn drop(&mut self) {
444 unsafe { CloseHandle(self.snapshot) };
445 }
446}
447
448unsafe impl Send for ModuleEntry {}
449unsafe impl Sync for ModuleEntry {}
450unsafe impl Send for HeapList {}
451unsafe impl Send for HeapEntry {}
452unsafe impl Sync for HeapEntry {}