1use std::{
2 ffi::OsString,
3 io,
4 mem::{self, MaybeUninit},
5 num::NonZeroU32,
6 os::windows::prelude::{AsHandle, AsRawHandle, FromRawHandle, OwnedHandle},
7 path::{Path, PathBuf},
8 ptr,
9 time::Duration,
10};
11
12use winapi::{
13 shared::{
14 minwindef::{DWORD, FALSE},
15 winerror::{ERROR_CALL_NOT_IMPLEMENTED, ERROR_INSUFFICIENT_BUFFER},
16 },
17 um::{
18 minwinbase::STILL_ACTIVE,
19 processthreadsapi::{
20 CreateRemoteThread, GetCurrentProcess, GetExitCodeProcess, GetExitCodeThread,
21 GetProcessId, TerminateProcess,
22 },
23 synchapi::WaitForSingleObject,
24 winbase::{QueryFullProcessImageNameW, INFINITE, WAIT_FAILED},
25 winnt::{
26 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION,
27 PROCESS_VM_READ, PROCESS_VM_WRITE,
28 },
29 wow64apiset::{GetSystemWow64DirectoryA, IsWow64Process},
30 },
31};
32
33use crate::{
34 process::{BorrowedProcess, ProcessModule},
35 utils::{win_fill_path_buf_helper, FillPathBufResult},
36};
37
38pub type ProcessHandle = std::os::windows::raw::HANDLE;
40
41pub const PROCESS_INJECTION_ACCESS: DWORD = PROCESS_CREATE_THREAD
43 | PROCESS_QUERY_INFORMATION
44 | PROCESS_VM_OPERATION
45 | PROCESS_VM_READ
46 | PROCESS_VM_WRITE;
47
48pub trait Process: AsHandle + AsRawHandle {
58 type Handle;
60
61 fn borrowed(&self) -> BorrowedProcess<'_>;
63
64 fn try_clone(&self) -> Result<Self, io::Error>
66 where
67 Self: Sized;
68
69 #[must_use]
71 fn into_handle(self) -> Self::Handle;
72
73 #[must_use]
78 unsafe fn from_handle_unchecked(handle: Self::Handle) -> Self;
79
80 #[must_use]
82 fn raw_current_handle() -> ProcessHandle {
83 unsafe { GetCurrentProcess() }.cast()
84 }
85
86 #[must_use]
88 fn current_handle() -> Self::Handle;
89
90 #[must_use]
92 fn current() -> Self
93 where
94 Self: Sized,
95 {
96 unsafe { Self::from_handle_unchecked(Self::current_handle()) }
97 }
98
99 #[must_use]
101 fn is_current(&self) -> bool {
102 self.borrowed() == BorrowedProcess::current()
103 }
104
105 #[must_use]
110 fn is_alive(&self) -> bool {
111 if self.is_current() {
112 return true;
113 }
114
115 let mut exit_code = MaybeUninit::uninit();
116 let result =
117 unsafe { GetExitCodeProcess(self.as_raw_handle().cast(), exit_code.as_mut_ptr()) };
118 result != FALSE && unsafe { exit_code.assume_init() } == STILL_ACTIVE
119 }
120
121 fn pid(&self) -> Result<NonZeroU32, io::Error> {
123 let result = unsafe { GetProcessId(self.as_raw_handle().cast()) };
124 NonZeroU32::new(result).ok_or_else(io::Error::last_os_error)
125 }
126
127 fn runs_under_wow64(&self) -> Result<bool, io::Error> {
133 let mut is_wow64 = MaybeUninit::uninit();
134 let result = unsafe { IsWow64Process(self.as_raw_handle().cast(), is_wow64.as_mut_ptr()) };
135 if result == 0 {
136 return Err(io::Error::last_os_error());
137 }
138 Ok(unsafe { is_wow64.assume_init() } != FALSE)
139 }
140
141 fn is_x64(&self) -> Result<bool, io::Error> {
143 Ok(is_x64_windows()? && !self.runs_under_wow64()?)
144 }
145
146 fn is_x86(&self) -> Result<bool, io::Error> {
148 Ok(is_x32_windows()? || is_x64_windows()? && self.runs_under_wow64()?)
149 }
150
151 fn path(&self) -> Result<PathBuf, io::Error> {
153 win_fill_path_buf_helper(|buf_ptr, buf_size| {
154 let mut buf_size = buf_size as u32;
155 let result = unsafe {
156 QueryFullProcessImageNameW(self.as_raw_handle().cast(), 0, buf_ptr, &mut buf_size)
157 };
158 if result == 0 {
159 let err = io::Error::last_os_error();
160 if err.raw_os_error().unwrap() == ERROR_INSUFFICIENT_BUFFER as i32 {
161 FillPathBufResult::BufTooSmall {
162 size_hint: Some(buf_size as usize),
163 }
164 } else {
165 FillPathBufResult::Error(err)
166 }
167 } else {
168 FillPathBufResult::Success {
169 actual_len: buf_size as usize,
170 }
171 }
172 })
173 }
174
175 fn base_name(&self) -> Result<OsString, io::Error> {
177 self.path()
178 .map(|path| path.file_name().unwrap().to_os_string())
179 }
180
181 fn kill(&self) -> Result<(), io::Error> {
183 self.kill_with_exit_code(1)
184 }
185
186 fn kill_with_exit_code(&self, exit_code: u32) -> Result<(), io::Error> {
188 let result = unsafe { TerminateProcess(self.as_raw_handle().cast(), exit_code) };
189 if result == 0 {
190 return Err(io::Error::last_os_error());
191 }
192 Ok(())
193 }
194
195 fn run_remote_thread<T>(
197 &self,
198 remote_fn: extern "system" fn(*mut T) -> u32,
199 parameter: *mut T,
200 ) -> Result<u32, io::Error> {
201 let thread_handle = self.start_remote_thread(remote_fn, parameter)?;
202
203 let reason = unsafe { WaitForSingleObject(thread_handle.as_raw_handle().cast(), INFINITE) };
204 if reason == WAIT_FAILED {
205 return Err(io::Error::last_os_error());
206 }
207
208 let mut exit_code = MaybeUninit::uninit();
209 let result = unsafe {
210 GetExitCodeThread(thread_handle.as_raw_handle().cast(), exit_code.as_mut_ptr())
211 };
212 if result == 0 {
213 return Err(io::Error::last_os_error());
214 }
215 debug_assert_ne!(
216 result as u32, STILL_ACTIVE,
217 "GetExitCodeThread returned STILL_ACTIVE after WaitForSingleObject"
218 );
219
220 Ok(unsafe { exit_code.assume_init() })
221 }
222
223 #[allow(clippy::not_unsafe_ptr_arg_deref)] fn start_remote_thread<T>(
226 &self,
227 remote_fn: unsafe extern "system" fn(*mut T) -> u32,
228 parameter: *mut T,
229 ) -> Result<OwnedHandle, io::Error> {
230 let thread_handle = unsafe {
232 CreateRemoteThread(
233 self.as_raw_handle().cast(),
234 ptr::null_mut(),
235 0,
236 Some(mem::transmute(remote_fn)),
237 parameter.cast(),
238 0, ptr::null_mut(),
240 )
241 };
242 if thread_handle.is_null() {
243 return Err(io::Error::last_os_error());
244 }
245
246 Ok(unsafe { OwnedHandle::from_raw_handle(thread_handle.cast()) })
247 }
248
249 fn find_module_by_name(
257 &self,
258 module_name: impl AsRef<Path>,
259 ) -> Result<Option<ProcessModule<Self>>, io::Error>
260 where
261 Self: Sized;
262
263 fn find_module_by_path(
271 &self,
272 module_path: impl AsRef<Path>,
273 ) -> Result<Option<ProcessModule<Self>>, io::Error>
274 where
275 Self: Sized;
276
277 fn wait_for_module_by_name(
281 &self,
282 module_name: impl AsRef<Path>,
283 timeout: Duration,
284 ) -> Result<Option<ProcessModule<Self>>, io::Error>
285 where
286 Self: Sized;
287
288 fn wait_for_module_by_path(
292 &self,
293 module_path: impl AsRef<Path>,
294 timeout: Duration,
295 ) -> Result<Option<ProcessModule<Self>>, io::Error>
296 where
297 Self: Sized;
298
299 fn modules(&self) -> Result<Vec<ProcessModule<Self>>, io::Error>
304 where
305 Self: Sized,
306 {
307 let module_handles = self.borrowed().module_handles()?;
308 let mut modules = Vec::with_capacity(module_handles.len());
309 for module_handle in module_handles {
310 modules.push(unsafe { ProcessModule::new_unchecked(module_handle, self.try_clone()?) });
311 }
312 Ok(modules)
313 }
314}
315
316fn is_x32_windows() -> Result<bool, io::Error> {
317 let result = unsafe { GetSystemWow64DirectoryA(ptr::null_mut(), 0) };
319 if result == 0 {
320 let err = io::Error::last_os_error();
321 if err.raw_os_error().unwrap() == ERROR_CALL_NOT_IMPLEMENTED as _ {
322 Ok(true)
323 } else {
324 Err(err)
325 }
326 } else {
327 Ok(false)
328 }
329}
330
331fn is_x64_windows() -> Result<bool, io::Error> {
332 if cfg!(target_arch = "x86_64") {
333 Ok(true)
334 } else {
335 Ok(!is_x32_windows()?)
336 }
337}