1use std::{
2 ffi::{CStr, CString, OsString},
3 io,
4 mem::{self, MaybeUninit},
5 path::{Path, PathBuf},
6 ptr::NonNull,
7};
8
9use crate::{
10 error::{GetLocalProcedureAddressError, IoOrNulError},
11 function::{FunctionPtr, RawFunctionPtr},
12 process::{BorrowedProcess, OwnedProcess, Process},
13 utils::{win_fill_path_buf_helper, FillPathBufResult},
14};
15use path_absolutize::Absolutize;
16use widestring::{U16CStr, U16CString};
17use winapi::{
18 shared::{
19 minwindef::{HINSTANCE__, HMODULE},
20 winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_MOD_NOT_FOUND},
21 },
22 um::{
23 libloaderapi::{GetModuleFileNameW, GetModuleHandleW, GetProcAddress},
24 memoryapi::VirtualQueryEx,
25 psapi::{GetModuleBaseNameW, GetModuleFileNameExW},
26 winnt::{MEMORY_BASIC_INFORMATION, PAGE_NOACCESS},
27 },
28};
29
30pub type ModuleHandle = HMODULE;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct ProcessModule<P: Process> {
41 handle: NonNull<HINSTANCE__>,
42 process: P,
43}
44
45pub type OwnedProcessModule = ProcessModule<OwnedProcess>;
47pub type BorrowedProcessModule<'a> = ProcessModule<BorrowedProcess<'a>>;
49
50unsafe impl<P: Process + Send> Send for ProcessModule<P> {}
51unsafe impl<P: Process + Sync> Sync for ProcessModule<P> {}
52
53impl<P: Process> ProcessModule<P> {
54 pub unsafe fn new_unchecked(handle: ModuleHandle, process: P) -> Self {
60 let handle = unsafe { NonNull::new_unchecked(handle) };
61 Self { handle, process }
62 }
63
64 pub unsafe fn new_local_unchecked(handle: ModuleHandle) -> Self {
70 unsafe { ProcessModule::new_unchecked(handle, P::current()) }
71 }
72
73 pub fn borrowed(&self) -> BorrowedProcessModule<'_> {
75 ProcessModule {
76 handle: self.handle,
77 process: self.process.borrowed(),
78 }
79 }
80
81 pub fn find(
84 module_name_or_path: impl AsRef<Path>,
85 process: P,
86 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
87 let module_name_or_path = module_name_or_path.as_ref();
88 if module_name_or_path.parent().is_some() {
89 Self::find_by_path(module_name_or_path, process)
90 } else {
91 Self::find_by_name(module_name_or_path, process)
92 }
93 }
94
95 pub fn find_by_name(
98 module_name: impl AsRef<Path>,
99 process: P,
100 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
101 if process.is_current() {
102 Self::find_local_by_name(module_name)
103 } else {
104 Self::_find_remote_by_name(module_name, process)
105 }
106 }
107
108 pub fn find_by_path(
111 module_path: impl AsRef<Path>,
112 process: P,
113 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
114 if process.is_current() {
115 Self::find_local_by_path(module_path)
116 } else {
117 Self::_find_remote_by_path(module_path, process)
118 }
119 }
120
121 pub fn find_local(
124 module_name_or_path: impl AsRef<Path>,
125 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
126 Self::find(module_name_or_path, P::current())
127 }
128
129 pub fn find_local_by_name(
132 module_name: impl AsRef<Path>,
133 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
134 Self::find_local_by_name_or_abs_path(module_name.as_ref())
135 }
136
137 pub fn find_local_by_path(
140 module_path: impl AsRef<Path>,
141 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
142 let absolute_path = module_path.as_ref().absolutize()?;
143 Self::find_local_by_name_or_abs_path(absolute_path.as_ref())
144 }
145
146 pub(crate) fn find_local_by_name_or_abs_path(
147 module: &Path,
148 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
149 let module = U16CString::from_os_str(module.as_os_str())?;
150 Self::find_local_by_name_or_abs_path_wstr(&module).map_err(|e| e.into())
151 }
152
153 pub(crate) fn find_local_by_name_or_abs_path_wstr(
154 module: &U16CStr,
155 ) -> Result<Option<ProcessModule<P>>, io::Error> {
156 let handle = unsafe { GetModuleHandleW(module.as_ptr()) };
157 if handle.is_null() {
158 let err = io::Error::last_os_error();
159 if err.raw_os_error().unwrap() == ERROR_MOD_NOT_FOUND as _ {
160 return Ok(None);
161 }
162
163 return Err(err);
164 }
165
166 Ok(Some(unsafe { Self::new_local_unchecked(handle) }))
167 }
168
169 fn _find_remote_by_name(
170 module_name: impl AsRef<Path>,
171 process: P,
172 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
173 assert!(!process.is_current());
174
175 process
176 .find_module_by_name(module_name)
177 .map_err(|e| e.into())
178 }
179
180 fn _find_remote_by_path(
181 module_path: impl AsRef<Path>,
182 process: P,
183 ) -> Result<Option<ProcessModule<P>>, IoOrNulError> {
184 assert!(!process.is_current());
185
186 process
187 .find_module_by_path(module_path)
188 .map_err(|e| e.into())
189 }
190
191 #[must_use]
193 pub fn handle(&self) -> ModuleHandle {
194 self.handle.as_ptr()
195 }
196
197 #[must_use]
199 pub fn process(&self) -> &P {
200 &self.process
201 }
202
203 #[must_use]
205 pub fn is_local(&self) -> bool {
206 self.process().is_current()
207 }
208 #[must_use]
210 pub fn is_remote(&self) -> bool {
211 !self.is_local()
212 }
213
214 pub fn path(&self) -> Result<PathBuf, io::Error> {
216 if self.is_local() {
217 win_fill_path_buf_helper(|buf_ptr, buf_size| {
218 let buf_size = buf_size as u32;
219 let result = unsafe { GetModuleFileNameW(self.handle(), buf_ptr, buf_size) };
220 if result == 0 {
221 let err = io::Error::last_os_error();
222 if err.raw_os_error().unwrap() == ERROR_INSUFFICIENT_BUFFER as i32 {
223 FillPathBufResult::BufTooSmall { size_hint: None }
224 } else {
225 FillPathBufResult::Error(err)
226 }
227 } else if result >= buf_size {
228 FillPathBufResult::BufTooSmall { size_hint: None }
229 } else {
230 FillPathBufResult::Success {
231 actual_len: result as usize,
232 }
233 }
234 })
235 } else {
236 win_fill_path_buf_helper(|buf_ptr, buf_size| {
237 let buf_size = buf_size as u32;
238 let result = unsafe {
239 GetModuleFileNameExW(
240 self.process().as_raw_handle().cast(),
241 self.handle(),
242 buf_ptr,
243 buf_size,
244 )
245 };
246 if result == 0 {
247 let err = io::Error::last_os_error();
248 if err.raw_os_error().unwrap() == ERROR_INSUFFICIENT_BUFFER as i32 {
249 FillPathBufResult::BufTooSmall { size_hint: None }
250 } else {
251 FillPathBufResult::Error(err)
252 }
253 } else if result >= buf_size {
254 FillPathBufResult::BufTooSmall { size_hint: None }
255 } else {
256 FillPathBufResult::Success {
257 actual_len: result as usize,
258 }
259 }
260 })
261 }
262 }
263
264 pub fn base_name(&self) -> Result<OsString, io::Error> {
266 if self.is_local() {
267 self.path().map(|path| path.file_name().unwrap().to_owned())
268 } else {
269 win_fill_path_buf_helper(|buf_ptr, buf_size| {
270 let buf_size = buf_size as u32;
271 let result = unsafe {
272 GetModuleBaseNameW(
273 self.process().as_raw_handle().cast(),
274 self.handle(),
275 buf_ptr,
276 buf_size,
277 )
278 };
279 if result == 0 {
280 let err = io::Error::last_os_error();
281 if err.raw_os_error().unwrap() == ERROR_INSUFFICIENT_BUFFER as i32 {
282 FillPathBufResult::BufTooSmall { size_hint: None }
283 } else {
284 FillPathBufResult::Error(err)
285 }
286 } else if result >= buf_size {
287 FillPathBufResult::BufTooSmall { size_hint: None }
288 } else {
289 FillPathBufResult::Success {
290 actual_len: result as usize,
291 }
292 }
293 })
294 .map(|e| e.into())
295 }
296 }
297
298 pub fn get_local_procedure_address(
303 &self,
304 proc_name: impl AsRef<str>,
305 ) -> Result<RawFunctionPtr, GetLocalProcedureAddressError> {
306 if self.is_remote() {
307 return Err(GetLocalProcedureAddressError::UnsupportedRemoteTarget);
308 }
309
310 let proc_name = CString::new(proc_name.as_ref())?;
311 self.get_local_procedure_address_cstr(&proc_name)
312 .map_err(|e| e.into())
313 }
314
315 pub unsafe fn get_local_procedure<F: FunctionPtr>(
323 &self,
324 proc_name: impl AsRef<str>,
325 ) -> Result<F, GetLocalProcedureAddressError> {
326 self.get_local_procedure_address(proc_name)
327 .map(|addr| unsafe { F::from_ptr(addr) })
328 }
329
330 pub(crate) fn get_local_procedure_address_cstr(
331 &self,
332 proc_name: &CStr,
333 ) -> Result<RawFunctionPtr, io::Error> {
334 assert!(self.is_local());
335
336 let fn_ptr = unsafe { GetProcAddress(self.handle(), proc_name.as_ptr()) };
337 if let Some(fn_ptr) = NonNull::new(fn_ptr) {
338 Ok(fn_ptr.as_ptr())
339 } else {
340 Err(io::Error::last_os_error())
341 }
342 }
343
344 pub fn guess_is_loaded(&self) -> bool {
347 self.try_guess_is_loaded().unwrap_or(false)
348 }
349
350 pub fn try_guess_is_loaded(&self) -> Result<bool, io::Error> {
352 if !self.process().is_alive() {
353 return Ok(false);
354 }
355
356 let mut module_info = MaybeUninit::uninit();
357 let raw_module = self.handle.as_ptr().cast();
358 let result = unsafe {
359 VirtualQueryEx(
360 self.process.as_raw_handle().cast(),
361 raw_module,
362 module_info.as_mut_ptr(),
363 mem::size_of::<MEMORY_BASIC_INFORMATION>(),
364 )
365 };
366
367 if result == 0 {
368 Err(io::Error::last_os_error())
369 } else {
370 let module_info = unsafe { module_info.assume_init() };
371 Ok(module_info.BaseAddress == raw_module && module_info.Protect != PAGE_NOACCESS)
372 }
373 }
374}
375
376impl BorrowedProcessModule<'_> {
377 pub fn try_to_owned(&self) -> Result<OwnedProcessModule, io::Error> {
379 self.process
380 .try_to_owned()
381 .map(|process| OwnedProcessModule {
382 process,
383 handle: self.handle,
384 })
385 }
386}
387
388impl TryFrom<BorrowedProcessModule<'_>> for OwnedProcessModule {
389 type Error = io::Error;
390
391 fn try_from(module: BorrowedProcessModule<'_>) -> Result<Self, Self::Error> {
392 module.try_to_owned()
393 }
394}
395
396impl<'a> From<&'a OwnedProcessModule> for BorrowedProcessModule<'a> {
397 fn from(module: &'a OwnedProcessModule) -> Self {
398 module.borrowed()
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 #[test]
407 fn find_local_by_name_present() {
408 let result = BorrowedProcessModule::find_local_by_name("kernel32.dll");
409 assert!(result.is_ok());
410 assert!(result.as_ref().unwrap().is_some());
411
412 let module = result.unwrap().unwrap();
413 assert!(module.is_local());
414 assert!(!module.handle().is_null());
415 }
416
417 #[test]
418 fn find_local_by_name_absent() {
419 let result = BorrowedProcessModule::find_local_by_name("kernel33.dll");
420 assert!(&result.is_ok());
421 assert!(result.unwrap().is_none());
422 }
423}