//! Anti-Tamper & Debugger Detection //! //! Prevents the binary from being modified or debugged. //! - Binary integrity hash verification //! - Debugger detection (ptrace) //! - String obfuscation helpers use crate::LicenseError; use sha2::{Sha256, Digest}; /// Compute SHA-256 hash of the current binary pub fn current_binary_hash() -> String { let exe_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("unknown")); if let Ok(data) = std::fs::read(&exe_path) { let mut hasher = Sha256::new(); hasher.update(&data); hex::encode(hasher.finalize()) } else { "unknown_binary".to_string() } } /// Verify binary integrity against an expected hash pub fn verify_binary(expected_hash: &str) -> Result<(), LicenseError> { let actual = current_binary_hash(); if actual != expected_hash { return Err(LicenseError::BinaryTampered); } Ok(()) } /// Detect if a debugger is attached pub fn detect_debugger() -> bool { // Linux: check if being ptraced #[cfg(target_os = "linux")] { // Try ptrace(PTRACE_TRACEME, ...) — if it fails, we're being debugged let result = unsafe { libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0) }; if result == -1 { return true; // Being traced } // Detach unsafe { libc::ptrace(libc::PTRACE_DETACH, 0, 0, 0) }; } // Check for common debugger environment variables let debug_env_vars = [ "DEBUG", "DEBUGGER", "JAVA_DEBUG", "PYTHONDEBUG", "TRACE", "TRACEME", "LD_PRELOAD", ]; for var in &debug_env_vars { if std::env::var(var).is_ok() { return true; } } // Check for /proc/self/status TracerPid #[cfg(target_os = "linux")] { if let Ok(content) = std::fs::read_to_string("/proc/self/status") { for line in content.lines() { if line.starts_with("TracerPid:") { let pid = line.trim_start_matches("TracerPid:").trim(); if pid != "0" { return true; } } } } } false } /// Run all anti-tamper checks at startup pub fn run_startup_checks(expected_binary_hash: &str) -> Result<(), LicenseError> { // Check 1: Debugger if detect_debugger() { return Err(LicenseError::DebuggerDetected); } // Check 2: Binary integrity verify_binary(expected_binary_hash)?; Ok(()) } /// Obfuscate a string literal (compile-time) /// Usage: obfstr!("sensitive_string") /// In production, use the `obfstr` crate for true compile-time obfuscation #[macro_export] macro_rules! obf { ($s:expr) => {{ let bytes: &[u8] = $s.as_bytes(); let mut obfuscated: Vec = bytes.iter().map(|b| b ^ 0xAA).collect(); let deobf: Vec = obfuscated.iter().map(|b| b ^ 0xAA).collect(); String::from_utf8(deobf).unwrap_or_default() }}; } #[cfg(test)] mod tests { use super::*; #[test] fn test_binary_hash_not_empty() { let hash = current_binary_hash(); assert!(!hash.is_empty()); assert_eq!(hash.len(), 64); } #[test] fn test_obf_macro() { let original = "license.gyannetra.com"; let result = obf!(original); assert_eq!(result, original); } }