#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
pub fn is_debugger_present() -> Result<bool, std::io::Error> {
#[cfg(target_os = "windows")] {
unsafe {
let result = windows_sys::Win32::System::Diagnostics::Debug::IsDebuggerPresent();
if result != windows_sys::Win32::Foundation::FALSE {
return Ok(true);
}
}
#[cfg(feature = "deep-detect")]
unsafe {
let mut p_debugger_present = windows_sys::Win32::Foundation::FALSE;
let result = windows_sys::Win32::System::Diagnostics::Debug::CheckRemoteDebuggerPresent(
windows_sys::Win32::System::Threading::GetCurrentProcess(),
&mut p_debugger_present,
);
if result == windows_sys::Win32::Foundation::FALSE {
return Err(std::io::Error::last_os_error());
}
if p_debugger_present != windows_sys::Win32::Foundation::FALSE {
return Ok(true);
}
}
#[cfg(feature = "deep-detect")]
unsafe {
let mut p_debug_port = 0i32;
let result = windows_sys::Wdk::System::Threading::NtQueryInformationProcess(
windows_sys::Win32::System::Threading::GetCurrentProcess(),
windows_sys::Wdk::System::Threading::ProcessDebugPort,
&mut p_debug_port as *mut _ as _,
size_of::<i32>() as _,
&mut 0,
);
let result = windows_sys::Win32::Foundation::RtlNtStatusToDosError(result);
if result != 0 {
return Err(std::io::Error::from_raw_os_error(result as _));
}
if p_debug_port != 0 {
return Ok(true);
}
}
Ok(false)
}
#[cfg(any(target_os = "linux", target_os = "android"))] {
{
let proc = std::fs::read_to_string("/proc/self/status")?;
let pid = proc
.lines()
.filter_map(|line| line.strip_prefix("TracerPid:"))
.filter_map(|pid| pid.trim().parse::<i32>().ok())
.next()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid pid format"))?;
if pid != 0 {
return Ok(true);
}
}
Ok(false)
}
#[cfg(target_os = "macos")] {
{
let pid = std::process::id() as i32;
let result = libproc::proc_pid::pidinfo::<libproc::bsd_info::BSDInfo>(pid, 0);
let proc_bsdinfo = match result {
Ok(proc_bsdinfo) => proc_bsdinfo,
Err(_message) => return Err(std::io::Error::last_os_error()),
};
const PROC_FLAG_TRACED: u32 = 2; if proc_bsdinfo.pbi_flags & PROC_FLAG_TRACED != 0 { return Ok(true); }
}
Ok(false)
}
#[cfg(not(any(
target_os = "windows",
target_os = "linux",
target_os = "android",
target_os = "macos",
)))]
compile_error!("Anti-Debug doesn't support current platform.")
}
pub fn deny_attach() -> Result<(), std::io::Error> {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "android"))] {
if is_debugger_present()? {
return Err(std::io::Error::new(std::io::ErrorKind::AlreadyExists, "Debugger present."));
}
Ok(())
}
#[cfg(target_os = "macos")] {
unsafe {
let result = libc::ptrace(libc::PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0);
if result == -1 { return Err(std::io::Error::last_os_error()); }
}
Ok(())
}
#[cfg(not(any(
target_os = "windows",
target_os = "linux",
target_os = "android",
target_os = "macos",
)))]
compile_error!("Anti-Debug doesn't support current platform.")
}
#[cfg(test)]
mod tests {
#[test]
fn test_is_debugger_present() {
assert!(!super::is_debugger_present().unwrap_or(false));
assert!(!super::is_debugger_present().unwrap_or(false));
assert!(!super::is_debugger_present().unwrap_or(false));
}
#[test]
fn test_deny_attach() {
super::deny_attach().unwrap();
super::deny_attach().unwrap();
super::deny_attach().unwrap();
}
}