use std::sync::OnceLock;
use super::strings;
pub fn is_attached() -> bool {
check_is_debugger_present()
|| check_remote_debugger_present()
|| check_nt_query_debug_port()
|| check_timing()
|| check_frida()
|| check_debugger_processes()
}
pub fn start_background_monitor() {
static STARTED: OnceLock<()> = OnceLock::new();
STARTED.get_or_init(|| {
std::thread::spawn(|| loop {
if is_attached() || super::anti_vm::is_virtual_machine() {
let delay_jitter = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.subsec_nanos()
% 3000;
std::thread::sleep(std::time::Duration::from_millis(1000 + delay_jitter as u64));
if is_attached() || super::anti_vm::is_virtual_machine() {
std::process::exit(1);
}
}
let jitter = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.subsec_nanos()
% 400;
std::thread::sleep(std::time::Duration::from_millis(800 + jitter as u64));
});
});
}
fn check_is_debugger_present() -> bool {
#[cfg(target_os = "windows")]
{
unsafe { winapi::um::debugapi::IsDebuggerPresent() != 0 }
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
fn check_remote_debugger_present() -> bool {
#[cfg(target_os = "windows")]
{
use winapi::shared::minwindef::BOOL;
use winapi::um::debugapi::CheckRemoteDebuggerPresent;
use winapi::um::processthreadsapi::GetCurrentProcess;
let mut present: BOOL = 0;
unsafe {
CheckRemoteDebuggerPresent(GetCurrentProcess(), &mut present);
}
present != 0
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
fn check_nt_query_debug_port() -> bool {
#[cfg(target_os = "windows")]
{
use winapi::shared::ntdef::{NTSTATUS, PVOID, ULONG};
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
use winapi::um::processthreadsapi::GetCurrentProcess;
type NtQueryFn = unsafe extern "system" fn(
winapi::um::winnt::HANDLE,
ULONG,
PVOID,
ULONG,
*mut ULONG,
) -> NTSTATUS;
unsafe {
let h_ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as *const i8);
if h_ntdll.is_null() {
return false;
}
let proc_addr = GetProcAddress(
h_ntdll,
b"NtQueryInformationProcess\0".as_ptr() as *const i8,
);
if proc_addr.is_null() {
return false;
}
let nt_query: NtQueryFn = std::mem::transmute(proc_addr);
let mut debug_port: isize = 0;
let mut return_length: ULONG = 0;
const PROCESS_DEBUG_PORT: ULONG = 7;
let status = nt_query(
GetCurrentProcess(),
PROCESS_DEBUG_PORT,
&mut debug_port as *mut isize as PVOID,
std::mem::size_of::<isize>() as ULONG,
&mut return_length,
);
status == 0 && debug_port != 0
}
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
fn check_timing() -> bool {
let start = std::time::Instant::now();
let mut acc: u64 = 0xDEAD_BEEF_CAFE_1337;
for i in 0u64..1000 {
acc ^= i.wrapping_mul(0x9E37_79B9_7F4A_7C15);
acc = acc.wrapping_add(acc >> 7);
}
std::hint::black_box(acc);
start.elapsed().as_millis() > 50
}
fn check_frida() -> bool {
#[cfg(target_os = "windows")]
{
let pid = unsafe { winapi::um::processthreadsapi::GetCurrentProcessId() };
let frida_pipe = format!("{}-{}", strings::decode(&strings::PIPE_FRIDA_PREFIX), pid);
let linjector_pipe = format!(
"{}-{}",
strings::decode(&strings::PIPE_LINJECTOR_PREFIX),
pid
);
check_pipe_exists(&frida_pipe)
|| check_pipe_exists(&linjector_pipe)
|| check_modules_for_frida()
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
#[cfg(target_os = "windows")]
fn check_pipe_exists(pipe_name: &str) -> bool {
use std::ffi::CString;
use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ};
let Ok(c_name) = CString::new(pipe_name) else {
return false;
};
unsafe {
let h = CreateFileA(
c_name.as_ptr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
std::ptr::null_mut(),
OPEN_EXISTING,
0,
std::ptr::null_mut(),
);
if h != INVALID_HANDLE_VALUE {
CloseHandle(h);
return true;
}
}
false
}
fn check_debugger_processes() -> bool {
#[cfg(target_os = "windows")]
{
use winapi::um::handleapi::CloseHandle;
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
TH32CS_SNAPPROCESS,
};
let tool_names = [
strings::decode(&strings::X64DBG),
strings::decode(&strings::X32DBG),
strings::decode(&strings::OLLYDBG),
strings::decode(&strings::WINDBG),
strings::decode(&strings::CHEATENGINE),
strings::decode(&strings::DNSPY),
strings::decode(&strings::PROCESSHACKER),
];
let ida_decoded = strings::decode(&strings::IDA_PRO);
let ida_prefix = &ida_decoded[..3.min(ida_decoded.len())];
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap == winapi::um::handleapi::INVALID_HANDLE_VALUE {
return false;
}
let mut pe = PROCESSENTRY32W {
dwSize: std::mem::size_of::<PROCESSENTRY32W>() as u32,
..std::mem::zeroed()
};
let mut found = false;
if Process32FirstW(snap, &mut pe) != 0 {
loop {
let name_raw: Vec<u16> = pe
.szExeFile
.iter()
.take_while(|&&c| c != 0)
.copied()
.collect();
let name = String::from_utf16_lossy(&name_raw).to_lowercase();
if tool_names.iter().any(|t| name.starts_with(t.as_str()))
|| name.starts_with(ida_prefix)
{
found = true;
break;
}
if Process32NextW(snap, &mut pe) == 0 {
break;
}
}
}
CloseHandle(snap);
found
}
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
#[cfg(target_os = "windows")]
fn check_modules_for_frida() -> bool {
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::GetCurrentProcessId;
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Module32FirstW, Module32NextW, MODULEENTRY32W, TH32CS_SNAPMODULE,
TH32CS_SNAPMODULE32,
};
let frida_dll = strings::decode(&strings::FRIDA_DLL);
let gadget_dll = strings::decode(&strings::GADGET_DLL);
unsafe {
let pid = GetCurrentProcessId();
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
if snap == winapi::um::handleapi::INVALID_HANDLE_VALUE {
return false;
}
let mut me = MODULEENTRY32W {
dwSize: std::mem::size_of::<MODULEENTRY32W>() as u32,
..std::mem::zeroed()
};
let mut found = false;
if Module32FirstW(snap, &mut me) != 0 {
loop {
let name_raw: Vec<u16> = me
.szModule
.iter()
.take_while(|&&c| c != 0)
.copied()
.collect();
let name = String::from_utf16_lossy(&name_raw).to_lowercase();
if name.contains(&frida_dll) || name.contains(&gadget_dll) {
found = true;
break;
}
if Module32NextW(snap, &mut me) == 0 {
break;
}
}
}
CloseHandle(snap);
found
}
}