Skip to main content

dll_syringe/process/
borrowed.rs

1use std::{
2    borrow::Cow,
3    cmp,
4    hash::{Hash, Hasher},
5    io,
6    mem::{self, MaybeUninit},
7    os::windows::{
8        prelude::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle},
9        raw::HANDLE,
10    },
11    path::Path,
12    time::Duration,
13};
14
15use winapi::{
16    shared::{
17        minwindef::{FALSE, HMODULE},
18        winerror::ERROR_PARTIAL_COPY,
19    },
20    um::{
21        handleapi::DuplicateHandle,
22        processthreadsapi::GetCurrentProcess,
23        psapi::{EnumProcessModulesEx, LIST_MODULES_ALL},
24        winnt::DUPLICATE_SAME_ACCESS,
25    },
26};
27
28use crate::{
29    process::{ModuleHandle, OwnedProcess, Process, ProcessModule},
30    utils::{retry_faillable_until_some_with_timeout, ArrayOrVecBuf},
31};
32
33/// A struct representing a running process.
34/// This struct does **NOT** own the underlying process handle (see also [`OwnedProcess`] for an owned version).
35///
36/// # Note
37/// The underlying handle has the following [privileges](https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights):
38///  - `PROCESS_CREATE_THREAD`
39///  - `PROCESS_QUERY_INFORMATION`
40///  - `PROCESS_VM_OPERATION`
41///  - `PROCESS_VM_WRITE`
42///  - `PROCESS_VM_READ`
43#[repr(transparent)]
44#[derive(Debug, Clone, Copy)]
45pub struct BorrowedProcess<'a>(BorrowedHandle<'a>);
46
47unsafe impl Send for BorrowedProcess<'_> {}
48unsafe impl Sync for BorrowedProcess<'_> {}
49
50impl AsRawHandle for BorrowedProcess<'_> {
51    fn as_raw_handle(&self) -> HANDLE {
52        self.0.as_raw_handle()
53    }
54}
55
56impl AsHandle for BorrowedProcess<'_> {
57    fn as_handle(&self) -> BorrowedHandle<'_> {
58        self.0.as_handle()
59    }
60}
61
62impl<'a, 'b> PartialEq<BorrowedProcess<'a>> for BorrowedProcess<'b> {
63    fn eq(&self, other: &BorrowedProcess<'a>) -> bool {
64        // TODO: (unsafe { CompareObjectHandles(self.handle(), other.handle()) }) != FALSE
65
66        self.as_raw_handle() == other.as_raw_handle()
67            || self.pid().map_or(0, |v| v.get()) == other.pid().map_or(0, |v| v.get())
68    }
69}
70
71impl PartialEq<OwnedProcess> for BorrowedProcess<'_> {
72    fn eq(&self, other: &OwnedProcess) -> bool {
73        self == &other.borrowed()
74    }
75}
76
77impl PartialEq<BorrowedProcess<'_>> for OwnedProcess {
78    fn eq(&self, other: &BorrowedProcess<'_>) -> bool {
79        &self.borrowed() == other
80    }
81}
82
83impl Eq for BorrowedProcess<'_> {}
84
85impl Hash for BorrowedProcess<'_> {
86    fn hash<H: Hasher>(&self, state: &mut H) {
87        self.as_raw_handle().hash(state);
88    }
89}
90
91impl<'a> From<&'a OwnedProcess> for BorrowedProcess<'a> {
92    fn from(process: &'a OwnedProcess) -> Self {
93        process.borrowed()
94    }
95}
96
97impl<'a> Process for BorrowedProcess<'a> {
98    type Handle = BorrowedHandle<'a>;
99
100    fn borrowed(&self) -> BorrowedProcess<'a> {
101        *self
102    }
103
104    fn into_handle(self) -> Self::Handle {
105        self.0
106    }
107
108    fn try_clone(&self) -> Result<Self, io::Error> {
109        Ok(*self)
110    }
111
112    unsafe fn from_handle_unchecked(handle: Self::Handle) -> Self {
113        Self(handle)
114    }
115
116    fn current_handle() -> Self::Handle {
117        unsafe { BorrowedHandle::borrow_raw(Self::raw_current_handle()) }
118    }
119
120    fn find_module_by_name(
121        &self,
122        module_name: impl AsRef<Path>,
123    ) -> Result<Option<ProcessModule<BorrowedProcess<'a>>>, io::Error> {
124        let target_module_name = module_name.as_ref();
125
126        // add default file extension if missing
127        let target_module_name = if target_module_name.extension().is_none() {
128            Cow::Owned(target_module_name.with_extension("dll").into_os_string())
129        } else {
130            Cow::Borrowed(target_module_name.as_os_str())
131        };
132
133        let modules = self.module_handles()?;
134
135        for module_handle in modules {
136            let module = unsafe { ProcessModule::new_unchecked(module_handle, *self) };
137            let module_name = module.base_name()?;
138
139            if module_name.eq_ignore_ascii_case(&target_module_name) {
140                return Ok(Some(module));
141            }
142        }
143
144        Ok(None)
145    }
146
147    fn find_module_by_path(
148        &self,
149        module_path: impl AsRef<Path>,
150    ) -> Result<Option<ProcessModule<BorrowedProcess<'a>>>, io::Error> {
151        let target_module_path = module_path.as_ref();
152
153        // add default file extension if missing
154        let target_module_path = if target_module_path.extension().is_none() {
155            Cow::Owned(target_module_path.with_extension("dll").into_os_string())
156        } else {
157            Cow::Borrowed(target_module_path.as_os_str())
158        };
159
160        let target_module_handle = same_file::Handle::from_path(&target_module_path)?;
161
162        let modules = self.module_handles()?;
163
164        for module_handle in modules {
165            let module = unsafe { ProcessModule::new_unchecked(module_handle, *self) };
166            let module_path = module.path()?.into_os_string();
167
168            match same_file::Handle::from_path(&module_path) {
169                Ok(module_handle) => {
170                    if module_handle == target_module_handle {
171                        return Ok(Some(module));
172                    }
173                }
174                Err(_) => {
175                    if target_module_path.eq_ignore_ascii_case(&module_path) {
176                        return Ok(Some(module));
177                    }
178                }
179            }
180        }
181
182        Ok(None)
183    }
184
185    fn wait_for_module_by_name(
186        &self,
187        module_name: impl AsRef<Path>,
188        timeout: Duration,
189    ) -> Result<Option<ProcessModule<BorrowedProcess<'a>>>, io::Error> {
190        retry_faillable_until_some_with_timeout(
191            || self.find_module_by_name(module_name.as_ref()),
192            timeout,
193        )
194    }
195
196    fn wait_for_module_by_path(
197        &self,
198        module_path: impl AsRef<Path>,
199        timeout: Duration,
200    ) -> Result<Option<ProcessModule<BorrowedProcess<'a>>>, io::Error> {
201        retry_faillable_until_some_with_timeout(
202            || self.find_module_by_path(module_path.as_ref()),
203            timeout,
204        )
205    }
206}
207
208impl<'a> BorrowedProcess<'a> {
209    /// Tries to create a new [`OwnedProcess`] instance for this process.
210    pub fn try_to_owned(&self) -> Result<OwnedProcess, io::Error> {
211        let raw_handle = self.as_raw_handle();
212        let process = unsafe { GetCurrentProcess() };
213        let mut new_handle = MaybeUninit::uninit();
214        let result = unsafe {
215            DuplicateHandle(
216                process,
217                raw_handle.cast(),
218                process,
219                new_handle.as_mut_ptr(),
220                0,
221                FALSE,
222                DUPLICATE_SAME_ACCESS,
223            )
224        };
225        if result == 0 {
226            return Err(io::Error::last_os_error());
227        }
228        Ok(unsafe { OwnedProcess::from_raw_handle(new_handle.assume_init().cast()) })
229    }
230
231    /// Returns a snapshot of the handles of the modules currently loaded in this process.
232    ///
233    /// # Note
234    /// If the process is currently starting up and has not yet loaded all its modules, the returned list may be incomplete.
235    /// This can be worked around by repeatedly calling this method.
236    pub fn module_handles(&self) -> Result<impl ExactSizeIterator<Item = ModuleHandle>, io::Error> {
237        let mut module_buf = ArrayOrVecBuf::<ModuleHandle, 1024>::new_uninit_array();
238        const HANDLE_SIZE: u32 = mem::size_of::<HMODULE>() as _;
239        let mut module_buf_byte_size = HANDLE_SIZE * module_buf.capacity() as u32;
240        let mut bytes_needed_new = MaybeUninit::uninit();
241        loop {
242            let result = unsafe {
243                EnumProcessModulesEx(
244                    self.as_raw_handle().cast(),
245                    module_buf.as_mut_ptr(),
246                    module_buf_byte_size,
247                    bytes_needed_new.as_mut_ptr(),
248                    LIST_MODULES_ALL,
249                )
250            };
251            if result == 0 {
252                let err = io::Error::last_os_error();
253                if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _) && self.is_alive() {
254                    continue;
255                }
256                return Err(err);
257            }
258
259            break;
260        }
261
262        let mut bytes_needed = unsafe { bytes_needed_new.assume_init() };
263
264        let modules = if bytes_needed <= module_buf_byte_size {
265            // buffer size was sufficient
266            let module_buf_len = (bytes_needed / HANDLE_SIZE) as usize;
267            unsafe { module_buf.set_len(module_buf_len) };
268            module_buf
269        } else {
270            // buffer size was not sufficient
271            let mut module_buf_vec = Vec::new();
272
273            // we loop here trying to find a buffer size that fits all handles
274            // this needs to be a loop as the returned bytes_needed is only valid for the modules loaded when
275            // the function run, if more modules have loaded in the meantime we need to resize the buffer again.
276            // This can happen often if the process is currently starting up.
277            loop {
278                module_buf_byte_size =
279                    cmp::max(bytes_needed, module_buf_byte_size.saturating_mul(2));
280                let mut module_buf_len = (module_buf_byte_size / HANDLE_SIZE) as usize;
281                if module_buf_len > module_buf_vec.capacity() {
282                    module_buf_vec.reserve(module_buf_len - module_buf_vec.capacity());
283                }
284
285                let mut bytes_needed_new = MaybeUninit::uninit();
286                let result = unsafe {
287                    EnumProcessModulesEx(
288                        self.as_raw_handle().cast(),
289                        module_buf_vec.as_mut_ptr(),
290                        module_buf_byte_size,
291                        bytes_needed_new.as_mut_ptr(),
292                        LIST_MODULES_ALL,
293                    )
294                };
295                if result == 0 {
296                    return Err(io::Error::last_os_error());
297                }
298                bytes_needed = unsafe { bytes_needed_new.assume_init() };
299
300                if bytes_needed <= module_buf_byte_size {
301                    module_buf_len = (bytes_needed / HANDLE_SIZE) as usize;
302                    unsafe { module_buf_vec.set_len(module_buf_len) };
303                    break ArrayOrVecBuf::from_vec(module_buf_vec);
304                }
305            }
306        };
307
308        debug_assert!(modules.iter().all(|module| !module.is_null()));
309
310        Ok(modules.into_iter())
311    }
312}