Skip to main content

dll_syringe/process/
owned.rs

1use std::{
2    hash::{Hash, Hasher},
3    io,
4    os::windows::{
5        prelude::{
6            AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle,
7            RawHandle,
8        },
9        raw::HANDLE,
10    },
11    path::Path,
12    process::Child,
13    time::Duration,
14};
15
16use winapi::{shared::minwindef::FALSE, um::processthreadsapi::OpenProcess};
17
18use crate::process::{BorrowedProcess, OwnedProcessModule, Process, PROCESS_INJECTION_ACCESS};
19
20/// A struct representing a running process.
21/// This struct owns the underlying process handle (see also [`BorrowedProcess`] for a borrowed version).
22///
23/// # Note
24/// The underlying handle has the following [privileges](https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights):
25///  - `PROCESS_CREATE_THREAD`
26///  - `PROCESS_QUERY_INFORMATION`
27///  - `PROCESS_VM_OPERATION`
28///  - `PROCESS_VM_WRITE`
29///  - `PROCESS_VM_READ`
30#[repr(transparent)]
31#[derive(Debug)]
32pub struct OwnedProcess(OwnedHandle);
33
34unsafe impl Send for OwnedProcess {}
35unsafe impl Sync for OwnedProcess {}
36
37impl AsRawHandle for OwnedProcess {
38    fn as_raw_handle(&self) -> HANDLE {
39        self.0.as_raw_handle()
40    }
41}
42
43impl AsHandle for OwnedProcess {
44    fn as_handle(&self) -> BorrowedHandle<'_> {
45        self.0.as_handle()
46    }
47}
48
49impl IntoRawHandle for OwnedProcess {
50    fn into_raw_handle(self) -> RawHandle {
51        self.0.into_raw_handle()
52    }
53}
54
55impl FromRawHandle for OwnedProcess {
56    unsafe fn from_raw_handle(handle: HANDLE) -> Self {
57        Self(unsafe { OwnedHandle::from_raw_handle(handle) })
58    }
59}
60
61impl From<Child> for OwnedProcess {
62    fn from(child: Child) -> Self {
63        Self::from_child(child)
64    }
65}
66
67impl TryFrom<BorrowedProcess<'_>> for OwnedProcess {
68    type Error = io::Error;
69
70    fn try_from(process: BorrowedProcess<'_>) -> Result<Self, Self::Error> {
71        process.try_to_owned()
72    }
73}
74
75impl PartialEq for OwnedProcess {
76    fn eq(&self, other: &Self) -> bool {
77        self.borrowed() == other.borrowed()
78    }
79}
80
81impl Eq for OwnedProcess {}
82
83impl Hash for OwnedProcess {
84    fn hash<H: Hasher>(&self, state: &mut H) {
85        self.borrowed().hash(state);
86    }
87}
88
89impl Process for OwnedProcess {
90    type Handle = OwnedHandle;
91
92    fn borrowed(&self) -> BorrowedProcess<'_> {
93        unsafe { BorrowedProcess::from_handle_unchecked(self.as_handle()) }
94    }
95
96    fn try_clone(&self) -> Result<Self, io::Error> {
97        self.borrowed().try_to_owned()
98    }
99
100    fn into_handle(self) -> Self::Handle {
101        self.0
102    }
103
104    unsafe fn from_handle_unchecked(handle: Self::Handle) -> Self {
105        Self(handle)
106    }
107
108    fn current_handle() -> Self::Handle {
109        unsafe { OwnedHandle::from_raw_handle(Self::raw_current_handle()) }
110    }
111
112    fn find_module_by_name(
113        &self,
114        module_name: impl AsRef<Path>,
115    ) -> Result<Option<OwnedProcessModule>, io::Error> {
116        if let Some(module) = self.borrowed().find_module_by_name(module_name)? {
117            Ok(Some(module.try_to_owned()?))
118        } else {
119            Ok(None)
120        }
121    }
122
123    fn find_module_by_path(
124        &self,
125        module_path: impl AsRef<Path>,
126    ) -> Result<Option<OwnedProcessModule>, io::Error> {
127        if let Some(module) = self.borrowed().find_module_by_path(module_path)? {
128            Ok(Some(module.try_to_owned()?))
129        } else {
130            Ok(None)
131        }
132    }
133
134    fn wait_for_module_by_name(
135        &self,
136        module_name: impl AsRef<Path>,
137        timeout: Duration,
138    ) -> Result<Option<OwnedProcessModule>, io::Error> {
139        if let Some(module) = self
140            .borrowed()
141            .wait_for_module_by_name(module_name, timeout)?
142        {
143            Ok(Some(module.try_to_owned()?))
144        } else {
145            Ok(None)
146        }
147    }
148
149    fn wait_for_module_by_path(
150        &self,
151        module_path: impl AsRef<Path>,
152        timeout: Duration,
153    ) -> Result<Option<OwnedProcessModule>, io::Error> {
154        if let Some(module) = self
155            .borrowed()
156            .wait_for_module_by_path(module_path, timeout)?
157        {
158            Ok(Some(module.try_to_owned()?))
159        } else {
160            Ok(None)
161        }
162    }
163}
164
165impl OwnedProcess {
166    /// Creates a new instance from the given pid.
167    pub fn from_pid(pid: u32) -> Result<OwnedProcess, io::Error> {
168        let handle = unsafe {
169            OpenProcess(
170                // access required for performing dll injection
171                PROCESS_INJECTION_ACCESS,
172                FALSE,
173                pid,
174            )
175        };
176
177        if handle.is_null() {
178            return Err(io::Error::last_os_error());
179        }
180
181        Ok(unsafe { OwnedProcess::from_raw_handle(handle.cast()) })
182    }
183
184    /// Returns a list of all currently running processes.
185    #[must_use]
186    pub fn all() -> Vec<OwnedProcess> {
187        // TODO: avoid using sysinfo for this
188        // TODO: deduplicate code
189        let mut system = sysinfo::System::new();
190        system.refresh_processes_specifics(
191            sysinfo::ProcessesToUpdate::All,
192            true,
193            sysinfo::ProcessRefreshKind::nothing(),
194        );
195        system
196            .processes()
197            .values()
198            .map(|process| process.pid())
199            .filter_map(|pid| OwnedProcess::from_pid(pid.as_u32()).ok())
200            .collect()
201    }
202
203    /// Finds all processes whose name contains the given string.
204    #[must_use]
205    pub fn find_all_by_name(name: impl AsRef<str>) -> Vec<OwnedProcess> {
206        // TODO: avoid using sysinfo for this
207        // TODO: deduplicate code
208        let mut system = sysinfo::System::new();
209        system.refresh_processes_specifics(
210            sysinfo::ProcessesToUpdate::All,
211            true,
212            sysinfo::ProcessRefreshKind::nothing(),
213        );
214        system
215            .processes()
216            .values()
217            .filter(move |process| process.name().to_string_lossy().contains(name.as_ref()))
218            .map(|process| process.pid())
219            .filter_map(|pid| OwnedProcess::from_pid(pid.as_u32()).ok())
220            .collect()
221    }
222
223    /// Finds the first process whose name contains the given string.
224    #[must_use]
225    pub fn find_first_by_name(name: impl AsRef<str>) -> Option<OwnedProcess> {
226        // TODO: avoid using sysinfo for this
227        // TODO: deduplicate code
228        let mut system = sysinfo::System::new();
229        system.refresh_processes_specifics(
230            sysinfo::ProcessesToUpdate::All,
231            true,
232            sysinfo::ProcessRefreshKind::nothing(),
233        );
234        system
235            .processes()
236            .values()
237            .filter(move |process| process.name().to_string_lossy().contains(name.as_ref()))
238            .map(|process| process.pid())
239            .find_map(|pid| OwnedProcess::from_pid(pid.as_u32()).ok())
240    }
241
242    /// Creates a new instance from the given child process.
243    #[must_use]
244    pub fn from_child(child: Child) -> OwnedProcess {
245        unsafe { OwnedProcess::from_raw_handle(child.into_raw_handle()) }
246    }
247
248    /// Returns a borrowed instance of this process that lives for `'static`.
249    ///
250    /// # Safety
251    /// - This method is unsafe as the returned instance can outlive the owned instance,
252    ///   thus the caller must guarantee that the owned instance outlives the returned instance.
253    #[must_use]
254    pub unsafe fn borrowed_static(&self) -> BorrowedProcess<'static> {
255        unsafe {
256            BorrowedProcess::from_handle_unchecked(BorrowedHandle::borrow_raw(self.as_raw_handle()))
257        }
258    }
259
260    /// Creates a new owning [`Process`] instance for this process by duplicating the underlying handle.
261    pub fn try_clone(&self) -> Result<Self, io::Error> {
262        self.borrowed().try_to_owned()
263    }
264
265    /// Leaks the underlying handle and return it as a non-owning [`BorrowedProcess`] instance.
266    #[allow(clippy::must_use_candidate)]
267    pub fn leak(self) -> BorrowedProcess<'static> {
268        unsafe { self.borrowed_static() }
269    }
270
271    /// Returns a [`ProcessKillGuard`] wrapping this process that will automatically kill this process when dropped.
272    #[must_use]
273    pub const fn kill_on_drop(self) -> ProcessKillGuard {
274        ProcessKillGuard(self)
275    }
276}
277
278#[derive(Debug, shrinkwraprs::Shrinkwrap)]
279#[shrinkwrap(mutable)]
280/// A guard wrapping a [`OwnedProcess`] that will be automatically killed on drop.
281pub struct ProcessKillGuard(pub OwnedProcess);
282
283impl Drop for ProcessKillGuard {
284    fn drop(&mut self) {
285        let _ = self.0.kill();
286    }
287}