tlhelp32/
lib.rs

1//! An abstraction over the windows tlhelp32 api.
2//! It offers a generic [`Snapshot`] struct which acts as an iterator to easily iterate over the
3//! returned entries.
4
5#![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
35/// Copies memory allocated to another process at the specified address into a supplied slice.
36/// The number of bytes to copy is the length of the supplied slice.
37pub 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
59/// A trait for the different [`Snapshot`] types. You shouldn't need to work with this directly.
60pub trait TagTl32: private::Sealed {
61    /// The raw windows counterpart of the implementing struct
62    type Raw: Copy;
63    /// The corresponding Snapshot flags
64    const FLAGS: u32;
65    /// The `*32First` windows function
66    const ITER_FIRST: Tl32helpFunc<Self::Raw>;
67    /// The `*32Next` windows function
68    const ITER_NEXT: Tl32helpFunc<Self::Raw>;
69
70    /// Creates a new instance of this raw representation and initializes its `dwSize` field.
71    fn init_raw() -> Self::Raw;
72
73    /// Creates a new instance of `Self` from its windows counterpart.
74    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/// A process entry taken from a [`Snapshot`].
86/// For more information on the fields meanings visit the [`microsoft docs`](https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagprocessentry32)
87#[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/// A module entry taken from a [`Snapshot`].
139/// For more information on the fields meanings visit the [`microsoft docs`](https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagmoduleentry32)
140#[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/// A heap list taken from a [`Snapshot`]. This struct is an iterator over the heap entries of its heap.
195/// For more information on the fields meanings visit the [`microsoft docs`](https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagheaplist32)
196#[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/// A heap entry taken from a [`HeapList`].
262/// For more information on the fields meanings visit the [`microsoft docs`](https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagheapentry32)
263#[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/// A thread entry taken from a [`Snapshot`].
288/// For more information on the fields meanings visit the [`microsoft docs`](https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagthreadentry32)
289#[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/// An iterator for the Toolhelp32Snapshot Windows API.
322/// You create them by calling the appropriate `new_*` methods.
323#[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    /// Creates a snapshot from a given handle. Avoid using this unless you have a specific reason to.
336    /// # Safety
337    /// This function does not check whether the generic type and the flags belong together.
338    /// If used incorrectly this will produce an iterator that returns [`None`] from the very beginning.
339    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    /// Retrieves the windows snapshot handle
355    pub fn handle(&self) -> HANDLE {
356        self.snapshot
357    }
358}
359
360impl Snapshot<ProcessEntry> {
361    /// Creates a new [`ProcessEntry`] [`Snapshot`]. This is equal to creating a snapshot with the `TH32CS_SNAPPROCESS` flag.
362    /// # Errors
363    /// This function fails and returns the appropriate os error if it is unable to create a [`Snapshot`]
364    ///
365    /// # Usage
366    ///
367    /// ```rust,no_run
368    /// for entry in tlhelp32::Snapshot::new_process()? {
369    ///     println!("{:?}", entry);
370    /// }
371    /// ```
372    pub fn new_process() -> Result<Self> {
373        Self::new(0)
374    }
375}
376
377impl Snapshot<HeapList> {
378    /// Creates a new [`HeapList`] [`Snapshot`]. This is equal to creating a snapshot with the `TH32CS_SNAPHEAPLIST` flag.
379    /// # Errors
380    /// This function fails and returns the appropriate os error if it is unable to create a [`Snapshot`]
381    /// # Usage
382    ///
383    /// ```rust,no_run
384    /// for heap_list in tlhelp32::Snapshot::new_heap_list(pid)? {
385    ///     for heap_entry in heap_list {
386    ///         println!("{:?}", heap_entry);
387    ///     }
388    /// }
389    /// ```
390    pub fn new_heap_list(pid: u32) -> Result<Self> {
391        Self::new(pid)
392    }
393}
394
395impl Snapshot<ModuleEntry> {
396    /// Creates a new [`ModuleEntry`] [`Snapshot`]. This is equal to creating a snapshot with the `TH32CS_SNAPMODULE` and `TH32CS_SNAPMODULE32` flags.
397    /// # Errors
398    /// This function fails and returns the appropriate os error if it is unable to create a [`Snapshot`]
399    ///
400    /// # Usage
401    ///
402    /// ```rust,no_run
403    /// for mod_entry in tlhelp32::Snapshot::new_module(entry.process_id)? {
404    ///     println!("{:?}", mod_entry);
405    /// }
406    /// ```
407    pub fn new_module(pid: u32) -> Result<Self> {
408        Self::new(pid)
409    }
410}
411
412impl Snapshot<ThreadEntry> {
413    /// Creates a new [`ThreadEntry`] [`Snapshot`]. This is equal to creating a snapshot with the `TH32CS_SNAPTHREAD` flag.
414    /// # Errors
415    /// This function fails and returns the appropriate os error if it is unable to create a [`Snapshot`]
416    ///
417    /// # Usage
418    ///
419    /// ```rust,no_run
420    /// for thread_entry in tlhelp32::Snapshot::new_thread()? {
421    ///     println!("{:?}", mod_entry);
422    /// }
423    /// ```
424    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 {}