#![allow(unsafe_code)]
use super::{ProcessProtection, ProtectionError};
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Diagnostics::Debug::{
CheckRemoteDebuggerPresent, IsDebuggerPresent, SetErrorMode, SEM_FAILCRITICALERRORS,
SEM_NOGPFAULTERRORBOX,
};
use windows_sys::Win32::System::ProcessStatus::{
GetProcessWorkingSetSizeEx, SetProcessWorkingSetSizeEx,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, QUOTA_LIMITS_HARDWS_MIN_ENABLE};
pub struct WindowsProcessProtection;
impl ProcessProtection for WindowsProcessProtection {
fn lock_memory(&self) -> Result<(), ProtectionError> {
let process = unsafe { GetCurrentProcess() };
let mut min_ws: usize = 0;
let mut max_ws: usize = 0;
let mut flags: u32 = 0;
let ok = unsafe {
GetProcessWorkingSetSizeEx(
process,
&mut min_ws as *mut usize,
&mut max_ws as *mut usize,
&mut flags as *mut u32,
)
};
if ok == 0 {
let code = unsafe { GetLastError() } as i32;
return Err(ProtectionError::SyscallFailed {
op: "GetProcessWorkingSetSizeEx",
code,
});
}
let ok = unsafe {
SetProcessWorkingSetSizeEx(
process,
min_ws,
max_ws,
flags | QUOTA_LIMITS_HARDWS_MIN_ENABLE,
)
};
if ok == 0 {
let code = unsafe { GetLastError() } as i32;
return Err(ProtectionError::SyscallFailed {
op: "SetProcessWorkingSetSizeEx",
code,
});
}
Ok(())
}
fn disable_core_dump(&self) -> Result<(), ProtectionError> {
let _previous = unsafe { SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX) };
Ok(())
}
fn disable_ptrace(&self) -> Result<(), ProtectionError> {
let local_attached = unsafe { IsDebuggerPresent() } != 0;
let process = unsafe { GetCurrentProcess() };
let mut remote: i32 = 0;
let ok = unsafe { CheckRemoteDebuggerPresent(process, &mut remote as *mut i32) };
if ok == 0 {
let code = unsafe { GetLastError() } as i32;
return Err(ProtectionError::SyscallFailed {
op: "CheckRemoteDebuggerPresent",
code,
});
}
if local_attached || remote != 0 {
return Err(ProtectionError::DebuggerAttached(
"IsDebuggerPresent or CheckRemoteDebuggerPresent reported attach",
));
}
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn apply_all_returns_ok_or_specific_error() {
let proto = WindowsProcessProtection;
match proto.apply_all() {
Ok(()) => {}
Err(ProtectionError::SyscallFailed { op, code }) => {
eprintln!("apply_all SyscallFailed op={op} code={code}");
}
Err(other) => panic!("unexpected error variant: {other:?}"),
}
}
#[test]
fn disable_core_dump_never_errors_on_windows() {
let proto = WindowsProcessProtection;
proto.disable_core_dump().expect("SetErrorMode cannot fail");
}
}