use super::macros::{check_ptr, err};
use crate::Result;
use core::fmt::{Display, Formatter};
use once_cell::race::OnceBool;
use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, GetCurrentProcessId, OpenProcess};
use winapi::um::winnt::{
HANDLE, IMAGE_FILE_MACHINE_UNKNOWN, PROCESS_ALL_ACCESS, PROCESS_QUERY_INFORMATION,
PROCESS_QUERY_LIMITED_INFORMATION,
};
use winapi::um::wow64apiset::IsWow64Process2;
#[derive(Debug)]
pub struct Process {
proc: usize,
pid: u32,
perms: DWORD,
wow: OnceBool,
}
impl Process {
pub fn new(pid: u32, perms: DWORD) -> Result<Self> {
let proc = check_ptr!(OpenProcess(perms, FALSE, pid)) as usize;
Ok(Self {
proc,
pid,
perms,
wow: Default::default(),
})
}
#[cfg(test)]
pub(in super::super) unsafe fn from_raw_parts(proc: usize, pid: u32, perms: DWORD) -> Self {
Self {
proc,
pid,
perms,
wow: Default::default(),
}
}
pub fn self_proc() -> Self {
Process {
proc: unsafe { GetCurrentProcess() as usize },
pid: unsafe { GetCurrentProcessId() },
perms: PROCESS_ALL_ACCESS,
wow: Default::default(),
}
}
#[must_use]
#[cfg_attr(not(any(feature = "ntdll", test)), allow(unused))]
pub fn has_real_handle(&self) -> bool {
self.get_proc() != unsafe { GetCurrentProcess() }
}
#[must_use]
#[cfg_attr(not(feature = "ntdll"), allow(unused))]
pub fn err_pseudo_handle(&self) -> Result<()> {
if !self.has_real_handle() {
return Err(crate::error::CustomError::InvalidInput.into());
}
Ok(())
}
unsafe fn unchecked_impl_is_under_wow(&self) -> Result<bool> {
let mut process_machine: u16 = 0;
let mut native_machine: u16 = 0;
if IsWow64Process2(
self.proc as HANDLE,
&mut process_machine as *mut u16,
&mut native_machine as *mut u16,
) == FALSE
{
return Err(err("IsWow64Process2 number 1"));
}
crate::trace!("proc:{:#x},native:{:#x}", process_machine, native_machine);
Ok(process_machine != IMAGE_FILE_MACHINE_UNKNOWN)
}
pub fn unchecked_is_under_wow(&self) -> Result<bool> {
self.wow
.get_or_try_init(|| unsafe { self.unchecked_impl_is_under_wow() })
}
#[must_use]
pub fn is_under_wow(&self) -> Result<bool> {
if self.has_perm(PROCESS_QUERY_INFORMATION)
|| self.has_perm(PROCESS_QUERY_LIMITED_INFORMATION)
{
self.unchecked_is_under_wow()
} else {
Err(crate::error::CustomError::PermissionDenied.into())
}
}
#[must_use]
pub fn get_proc(&self) -> HANDLE {
self.proc as HANDLE
}
#[must_use]
pub fn get_pid(&self) -> u32 {
self.pid
}
#[must_use]
pub fn has_perm(&self, perm: DWORD) -> bool {
return self.perms & perm == perm;
}
}
impl Drop for Process {
fn drop(&mut self) {
crate::trace!("Cleaning Process Handle");
if unsafe { CloseHandle(self.proc as HANDLE) } == FALSE {
crate::error!("Error during Process Handle cleanup!");
err("CloseHandle of OpenProcess");
}
}
}
impl Display for Process {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"(proc:{:x},pid: {},perms:{:x}, wow:{:#?})",
self.proc as usize, self.pid, self.perms, self.wow
)
}
}
#[cfg(test)]
mod test {
use crate::Result;
use winapi::um::winnt::PROCESS_ALL_ACCESS;
extern crate std;
#[test]
fn new() {
let r = super::Process::new(std::process::id(), PROCESS_ALL_ACCESS);
assert!(r.is_ok(), "{}", r.unwrap_err());
}
#[test]
fn has_real_handle() -> Result<()> {
assert!(!super::Process::self_proc().has_real_handle());
assert!(super::Process::new(std::process::id(), PROCESS_ALL_ACCESS)?.has_real_handle());
Ok(())
}
#[test]
fn has_perm() {
assert!(super::Process::self_proc().has_perm(PROCESS_ALL_ACCESS))
}
}