feat: Universal Linux build + redesigned ostp-guard

- Build static musl binaries (work on any Linux distro)
- Redesign ostp-guard with weighted scoring system (threshold: 4 points)
  - HIGH (2pts): Analysis tools (gdb/ida/ghidra), sandbox artifacts
  - MEDIUM (1pt): Low resources (<1GB RAM), suspicious env vars
- Production VPS safe (1-2 points), sandbox blocked (4+ points)
- Anti-debug: Windows (IsDebuggerPresent), Linux (/proc/self/status)
- Deployment packages for Linux + Windows with SHA256 checksums
This commit is contained in:
2026-01-02 01:38:30 +03:00
parent 5879344336
commit 7ed4217987
23 changed files with 1045 additions and 432 deletions

View File

@@ -8,6 +8,7 @@ description = "OSTP Anti-Reverse Engineering & Protection Module"
rand.workspace = true
tokio.workspace = true
tracing.workspace = true
num_cpus = "1.16"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["debugapi", "processthreadsapi", "winnt", "sysinfoapi", "libloaderapi"] }

View File

@@ -1,7 +1,7 @@
//! Anti-Debugging Detection Module
//! Anti-Debugging Detection Module - Redesigned
//!
//! Detects if the process is being traced/debugged
//! and takes evasive action without obvious crashes.
//! Detects if the process is being actively debugged
//! Focuses on active debugging, not passive analysis
/// Check if any debugger is attached
#[inline(never)]
@@ -22,16 +22,16 @@ pub fn is_debugger_present() -> bool {
}
}
/// Multiple Windows anti-debug techniques
/// Windows anti-debug - only active debugging
#[cfg(windows)]
fn windows_debugger_check() -> bool {
unsafe {
// Method 1: IsDebuggerPresent API
// IsDebuggerPresent - reliable for active debugging
if winapi::um::debugapi::IsDebuggerPresent() != 0 {
return true;
}
// Method 2: CheckRemoteDebuggerPresent
// CheckRemoteDebuggerPresent - detects remote debuggers
let mut is_debugged: i32 = 0;
let process = winapi::um::processthreadsapi::GetCurrentProcess();
if winapi::um::debugapi::CheckRemoteDebuggerPresent(process, &mut is_debugged) != 0 {
@@ -39,123 +39,16 @@ fn windows_debugger_check() -> bool {
return true;
}
}
// Method 3: NtGlobalFlag check (PEB)
// The NtGlobalFlag in PEB is set to 0x70 when debugged
if check_peb_being_debugged() {
return true;
}
// Method 4: Timing check - debugger breakpoints cause delays
if timing_check() {
return true;
}
}
false
}
#[cfg(windows)]
unsafe fn check_peb_being_debugged() -> bool {
// Access PEB through TEB
// This is a low-level check that many debuggers don't hide
#[cfg(target_arch = "x86_64")]
{
let peb: *const u8;
unsafe {
std::arch::asm!(
"mov {}, gs:[0x60]",
out(reg) peb,
options(nostack, nomem)
);
}
if !peb.is_null() {
// BeingDebugged flag at offset 0x2
let being_debugged = unsafe { *peb.add(0x2) };
if being_debugged != 0 {
return true;
}
// NtGlobalFlag at offset 0xBC (x64)
let nt_global_flag = unsafe { *(peb.add(0xBC) as *const u32) };
// FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS
if nt_global_flag & 0x70 != 0 {
return true;
}
}
}
false
}
#[cfg(windows)]
fn timing_check() -> bool {
use std::time::Instant;
// Simple operation that should be instant
let start = Instant::now();
// Do some trivial work
let mut x: u64 = 0;
for i in 0..1000 {
x = x.wrapping_add(i);
}
// Prevent optimization
std::hint::black_box(x);
let elapsed = start.elapsed();
// If this takes more than 100ms, likely being single-stepped
elapsed.as_millis() > 100
}
/// Linux/Unix anti-debug techniques
/// Linux/Unix anti-debug - only /proc/self/status check
#[cfg(unix)]
fn unix_debugger_check() -> bool {
// Method 1: ptrace self-attach trick
if ptrace_check() {
return true;
}
// Method 2: Check /proc/self/status for TracerPid
if proc_status_check() {
return true;
}
// Method 3: Check parent process name
if parent_process_check() {
return true;
}
// Method 4: Timing check
if timing_check_unix() {
return true;
}
false
}
#[cfg(unix)]
fn ptrace_check() -> bool {
// PTRACE_TRACEME = 0
// If we're already being traced, this will fail
unsafe {
let result = libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0);
if result == -1 {
// Already being traced
return true;
}
// Detach from ourselves
libc::ptrace(libc::PTRACE_DETACH, 0, 0, 0);
}
false
}
#[cfg(unix)]
fn proc_status_check() -> bool {
// Read /proc/self/status and check TracerPid
// Check /proc/self/status for TracerPid
// Non-invasive, works everywhere
if let Ok(status) = std::fs::read_to_string("/proc/self/status") {
for line in status.lines() {
if line.starts_with("TracerPid:") {
@@ -172,57 +65,16 @@ fn proc_status_check() -> bool {
false
}
#[cfg(unix)]
fn parent_process_check() -> bool {
// Check if parent is a known debugger
let debuggers = ["gdb", "lldb", "strace", "ltrace", "radare2", "r2", "ida", "x64dbg", "ollydbg"];
if let Ok(ppid_str) = std::fs::read_to_string("/proc/self/stat") {
let parts: Vec<&str> = ppid_str.split_whitespace().collect();
if parts.len() > 3 {
if let Ok(ppid) = parts[3].parse::<i32>() {
let parent_exe = format!("/proc/{}/exe", ppid);
if let Ok(path) = std::fs::read_link(&parent_exe) {
let name = path.file_name()
.and_then(|n| n.to_str())
.unwrap_or("");
for debugger in &debuggers {
if name.contains(debugger) {
return true;
}
}
}
}
}
}
false
}
#[cfg(unix)]
fn timing_check_unix() -> bool {
use std::time::Instant;
let start = Instant::now();
let mut x: u64 = 0;
for i in 0..1000 {
x = x.wrapping_add(i);
}
std::hint::black_box(x);
start.elapsed().as_millis() > 100
}
/// Continuous background monitor (call from separate thread)
pub fn start_background_monitor() -> std::thread::JoinHandle<()> {
/// Background monitoring thread
pub fn start_background_monitor() {
std::thread::spawn(|| {
loop {
std::thread::sleep(std::time::Duration::from_secs(5));
if is_debugger_present() {
// Enter decoy mode silently
crate::decoy_loop();
// Silent exit
std::process::exit(1);
}
}
})
});
}

View File

@@ -1,161 +1,30 @@
//! Anti-VM and Sandbox Detection
//! Anti-VM and Sandbox Detection - Redesigned
//!
//! Detects common virtualization artifacts to prevent
//! analysis in controlled environments.
//! Smart heuristic approach to detect analysis environments
//! without blocking production VPS servers
/// Check if running in a virtual machine or sandbox
/// Check if running in a sandbox/analysis environment
/// Uses weighted scoring to avoid false positives on production VPS
#[inline(never)]
pub fn is_virtual_machine() -> bool {
// Check multiple indicators - any single check might be bypassed
let checks = [
check_vm_mac_addresses,
check_vm_hardware_ids,
check_vm_processes,
check_vm_files,
check_vm_registry,
check_low_resources,
];
let mut score = 0;
// If more than 2 checks trigger, likely a VM
let score: u32 = checks.iter()
.map(|check| if check() { 1 } else { 0 })
.sum();
// HIGH risk indicators (2 points each)
if check_analysis_tools() { score += 2; }
if check_sandbox_artifacts() { score += 2; }
score >= 2
// MEDIUM risk (1 point each)
if check_low_resources() { score += 1; }
if check_suspicious_environment() { score += 1; }
// Threshold: 4+ points = very likely sandbox
// Production VPS: max 1-2 points
// Analysis sandbox: 4+ points (2 HIGH or 1 HIGH + 2 MEDIUM)
score >= 4
}
/// Check for known VM MAC address prefixes
fn check_vm_mac_addresses() -> bool {
#[cfg(windows)]
{
// Get network adapter info and check MAC prefixes
// VMware: 00:0C:29, 00:50:56
// VirtualBox: 08:00:27
// Hyper-V: 00:15:5D
// Parallels: 00:1C:42
// Simplified check via ipconfig output patterns
if let Ok(output) = std::process::Command::new("ipconfig")
.arg("/all")
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
let vm_macs = ["00-0c-29", "00-50-56", "08-00-27", "00-15-5d", "00-1c-42"];
for mac in &vm_macs {
if stdout.contains(mac) {
return true;
}
}
}
}
#[cfg(unix)]
{
if let Ok(output) = std::process::Command::new("ip")
.args(["link", "show"])
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
let vm_macs = ["00:0c:29", "00:50:56", "08:00:27", "00:15:5d", "00:1c:42"];
for mac in &vm_macs {
if stdout.contains(mac) {
return true;
}
}
}
}
false
}
/// Check for VM-specific hardware IDs
fn check_vm_hardware_ids() -> bool {
#[cfg(windows)]
{
// Check WMI for VM indicators
let vm_indicators = [
"vmware", "virtualbox", "vbox", "qemu", "xen",
"virtual", "hyperv", "parallels", "kvm"
];
// Check computer name/model via WMI
if let Ok(output) = std::process::Command::new("wmic")
.args(["computersystem", "get", "model"])
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for indicator in &vm_indicators {
if stdout.contains(indicator) {
return true;
}
}
}
// Check BIOS
if let Ok(output) = std::process::Command::new("wmic")
.args(["bios", "get", "serialnumber"])
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for indicator in &vm_indicators {
if stdout.contains(indicator) {
return true;
}
}
}
}
#[cfg(unix)]
{
// Check /sys/class/dmi/id/
let dmi_paths = [
"/sys/class/dmi/id/product_name",
"/sys/class/dmi/id/sys_vendor",
"/sys/class/dmi/id/board_vendor",
];
let vm_indicators = [
"vmware", "virtualbox", "vbox", "qemu", "xen",
"virtual", "hyperv", "parallels", "kvm", "bochs"
];
for path in &dmi_paths {
if let Ok(content) = std::fs::read_to_string(path) {
let lower = content.to_lowercase();
for indicator in &vm_indicators {
if lower.contains(indicator) {
return true;
}
}
}
}
}
false
}
/// Check for VM-related processes
fn check_vm_processes() -> bool {
let vm_processes = [
"vmtoolsd", "vmwaretray", "vmwareuser", // VMware
"vboxservice", "vboxtray", "vboxclient", // VirtualBox
"xenservice", // Xen
"qemu-ga", // QEMU
"prl_tools", "prl_cc", // Parallels
];
#[cfg(windows)]
{
if let Ok(output) = std::process::Command::new("tasklist").output() {
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for proc in &vm_processes {
if stdout.contains(proc) {
return true;
}
}
}
}
/// HIGH: Check for reverse engineering tools in memory
fn check_analysis_tools() -> bool {
#[cfg(unix)]
{
if let Ok(output) = std::process::Command::new("ps")
@@ -163,8 +32,35 @@ fn check_vm_processes() -> bool {
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for proc in &vm_processes {
if stdout.contains(proc) {
let tools = [
"gdb", "lldb", "strace", "ltrace", "radare2", "r2",
"ida", "ida64", "x64dbg", "ghidra", "binaryninja",
"frida-server", "rizin", "cutter", "hopper",
"qemu-user" // QEMU user-mode for cross-arch analysis
];
for tool in &tools {
if stdout.contains(tool) {
return true;
}
}
}
}
#[cfg(windows)]
{
if let Ok(output) = std::process::Command::new("tasklist")
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
let tools = [
"x64dbg", "x32dbg", "ollydbg", "windbg", "ida", "ida64",
"ghidra", "binaryninja", "pestudio", "processhacker",
"procmon", "procexp", "wireshark", "fiddler"
];
for tool in &tools {
if stdout.contains(tool) {
return true;
}
}
@@ -174,37 +70,14 @@ fn check_vm_processes() -> bool {
false
}
/// Check for VM-specific files
fn check_vm_files() -> bool {
#[cfg(windows)]
{
let vm_files = [
r"C:\Windows\System32\drivers\vmmouse.sys",
r"C:\Windows\System32\drivers\vmhgfs.sys",
r"C:\Windows\System32\drivers\VBoxMouse.sys",
r"C:\Windows\System32\drivers\VBoxGuest.sys",
r"C:\Windows\System32\drivers\VBoxSF.sys",
];
for file in &vm_files {
if std::path::Path::new(file).exists() {
return true;
}
}
}
/// HIGH: Sandbox-specific artifacts
fn check_sandbox_artifacts() -> bool {
#[cfg(unix)]
{
let vm_files = [
"/usr/bin/vmtoolsd",
"/usr/bin/VBoxService",
"/usr/bin/VBoxClient",
"/.dockerenv",
"/run/.containerenv",
];
for file in &vm_files {
if std::path::Path::new(file).exists() {
// Wine (used for Windows malware analysis on Linux)
let wine_paths = ["/tmp/.wine-", "/tmp/.X11-unix"];
for path in &wine_paths {
if std::fs::read_dir(path).is_ok() {
return true;
}
}
@@ -213,24 +86,20 @@ fn check_vm_files() -> bool {
false
}
/// Check Windows registry for VM indicators
fn check_vm_registry() -> bool {
#[cfg(windows)]
{
// Check via reg query
let registry_keys = [
r"HKLM\SOFTWARE\VMware, Inc.\VMware Tools",
r"HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions",
];
for key in &registry_keys {
if let Ok(output) = std::process::Command::new("reg")
.args(["query", key])
.output()
{
if output.status.success() {
return true;
}
/// MEDIUM: Suspicious environment variables
fn check_suspicious_environment() -> bool {
let suspicious = [
"WINE_",
"QEMU_",
"SANDBOX_",
"AFL_", // American Fuzzy Lop fuzzer
"ASAN_", // AddressSanitizer
];
for (key, _) in std::env::vars() {
for pattern in &suspicious {
if key.starts_with(pattern) {
return true;
}
}
}
@@ -238,75 +107,44 @@ fn check_vm_registry() -> bool {
false
}
/// Check for suspiciously low resources (sandbox indicator)
/// MEDIUM: Very low resources (< 1GB RAM or 1 CPU)
fn check_low_resources() -> bool {
// Sandboxes often have minimal resources
// Check CPU count
let cpus = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
if cpus < 2 {
// Single CPU = very suspicious
let cpu_count = num_cpus::get();
if cpu_count < 2 {
return true;
}
// Check available disk space (simplified)
#[cfg(windows)]
// Check available memory
#[cfg(unix)]
{
if let Ok(output) = std::process::Command::new("wmic")
.args(["logicaldisk", "get", "size"])
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout);
// Very small disk = sandbox
if let Some(size_str) = stdout.lines().nth(1) {
if let Ok(size) = size_str.trim().parse::<u64>() {
// Less than 50GB
if size < 50_000_000_000 {
return true;
if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
for line in meminfo.lines() {
if line.starts_with("MemTotal:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
let mb = kb / 1024;
// Less than 1GB = sandbox
return mb < 1024;
}
}
}
}
}
}
false
}
/// Check for analysis tools
pub fn check_analysis_tools() -> bool {
let tools = [
"wireshark", "fiddler", "burp", "charles", // Network
"x64dbg", "x32dbg", "ollydbg", "windbg", // Debuggers
"ida", "ida64", "ghidra", "radare2", "r2", // Disassemblers
"procmon", "procexp", "processhacker", // Process monitors
"pestudio", "die", "exeinfope", // PE analyzers
];
#[cfg(windows)]
{
if let Ok(output) = std::process::Command::new("tasklist").output() {
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for tool in &tools {
if stdout.contains(tool) {
return true;
}
}
}
}
#[cfg(unix)]
{
if let Ok(output) = std::process::Command::new("ps")
.args(["aux"])
.output()
{
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
for tool in &tools {
if stdout.contains(tool) {
return true;
}
use std::mem::MaybeUninit;
unsafe {
let mut mem_status = MaybeUninit::<winapi::um::sysinfoapi::MEMORYSTATUSEX>::uninit();
let mem_status_ptr = mem_status.as_mut_ptr();
(*mem_status_ptr).dwLength = std::mem::size_of::<winapi::um::sysinfoapi::MEMORYSTATUSEX>() as u32;
if winapi::um::sysinfoapi::GlobalMemoryStatusEx(mem_status_ptr) != 0 {
let mem_status = mem_status.assume_init();
let total_mb = mem_status.ullTotalPhys / (1024 * 1024);
return total_mb < 1024;
}
}
}