Skip to content

Latest commit

 

History

History
860 lines (681 loc) · 22 KB

File metadata and controls

860 lines (681 loc) · 22 KB

FDebug - Advanced Implementation Guide

Detailed best practices, design patterns, and implementation strategies for the fdebug anti-debug protection system.


Table of Contents

  1. Architectural Design Patterns
  2. Integration Strategies
  3. Real-World Use Cases
  4. Performance Optimization
  5. Testing and Validation
  6. Security Considerations
  7. Troubleshooting

Architectural Design Patterns

Pattern 1: The Shield Pattern (Defense in Depth)

Protect the most critical assets with nested security layers:

use fdebug::protector::{Protector, SecureVault, ShieldedExecution, CoupledLogic, Corruptible};

/// Critical encryption key - maximum protection
struct CryptoMaterial {
    master_key: [u8; 32],
    initialization_vector: [u8; 16],
}

impl Corruptible for CryptoMaterial {
    fn corrupt_if_needed(mut self, token: u64) -> Self {
        // Extreme corruption if token is invalid
        if token & 0xFFFFFFFF == 0 || token == u64::MAX {
            self.master_key = [0; 32];
            self.initialization_vector = [0; 16];
        }
        self
    }
}

fn get_crypto_material(protector: &Protector) -> CryptoMaterial {
    // Layer 1: Run as coupled logic (automatic corruption)
    // Layer 2: Wrap in SecureVault (requires token)
    // Layer 3: Use XOR with token (additional mixing)
    
    let vault = SecureVault::new(vec![
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
    ]);
    
    protector.run_secure(&vault, |seed, token| {
        // XOR seed with token for additional protection
        let mut master_key = [0u8; 32];
        for i in 0..32 {
            master_key[i] = seed[i % 16] ^ ((token >> (i % 8 * 8)) as u8);
        }
        
        let mut iv = [0u8; 16];
        for i in 0..16 {
            iv[i] = seed[i] ^ ((token.rotate_left(i as u32) as u8));
        }
        
        CryptoMaterial {
            master_key,
            initialization_vector: iv,
        }
    })
}

fn main() {
    let protector = fdebug::setup_anti_debug!(fdebug::protector::get_dynamic_seed());
    let material = get_crypto_material(&protector);
    
    // If running under debugger:
    // - token is corrupted by VEH
    // - master_key and IV are garbage
    // - encryption produces garbage
    // Application appears to work but is worthless
}

Pattern 2: The Sentinel Pattern (Watchdog Verification)

Continuously verify protected operations are executing correctly:

use fdebug::protector::{Protector, SecureVault, ShieldedExecution};
use std::sync::atomic::{AtomicBool, Ordering};

static LAST_OPERATION_VALID: AtomicBool = AtomicBool::new(true);

fn protected_operation(protector: &Protector, data: &[u8]) -> u32 {
    // Get score before operation
    let score_before = get_suspicion_score();
    
    // Execute protected operation
    let vault = SecureVault::new(data.to_vec());
    let result = protector.run_secure(&vault, |data, token| {
        let mut sum = 0u32;
        for (i, byte) in data.iter().enumerate() {
            sum = sum.wrapping_add((*byte as u32).wrapping_mul(token as u32));
        }
        sum ^ (token as u32)
    });
    
    // Verify result consistency
    let score_after = get_suspicion_score();
    
    // Sentinel check: if suspicion increased, operation may be compromised
    if score_after > score_before {
        LAST_OPERATION_VALID.store(false, Ordering::SeqCst);
        eprintln!("[!] Sentinel check failed - operation compromised");
        return 0;
    }
    
    LAST_OPERATION_VALID.store(true, Ordering::SeqCst);
    result
}

fn is_system_healthy(protector: &Protector) -> bool {
    // Check if last operation was valid AND current state is clean
    LAST_OPERATION_VALID.load(Ordering::SeqCst) && 
    get_suspicion_score() == 0
}

Pattern 3: The Checksum Pattern (Self-Verifying Data)

Data structures that validate their own integrity:

use std::num::Wrapping;

#[derive(Clone)]
struct ProtectedValue<T: Clone + Copy> {
    value: T,
    checksum: u32,
}

impl<T: Clone + Copy> ProtectedValue<T> 
where
    T: Into<u64> + From<u64>,
{
    fn new(value: T) -> Self {
        let check = Self::calculate_checksum(value);
        ProtectedValue { value, checksum: check }
    }
    
    fn calculate_checksum(value: T) -> u32 {
        let val_u64: u64 = value.into();
        let mut check = 0u32;
        
        for i in 0..8 {
            let byte = ((val_u64 >> (i * 8)) & 0xFF) as u8;
            check = check.wrapping_mul(31).wrapping_add(byte as u32);
        }
        
        check ^ 0xDEADBEEF
    }
    
    fn validate(&self) -> bool {
        let expected = Self::calculate_checksum(self.value);
        self.checksum == expected
    }
    
    fn get_safe(&self, protector: &Protector) -> Option<T> {
        if !self.validate() {
            return None;
        }
        
        let vault = SecureVault::new(self.value);
        protector.run_secure(&vault, |val, token| {
            if token == 0 || token == u64::MAX {
                None
            } else {
                Some(*val)
            }
        })
    }
}

fn main() {
    let protector = Protector::new(0xACE);
    
    let protected = ProtectedValue::new(42u32);
    
    match protected.get_safe(&protector) {
        Some(value) => println!("Value: {}", value),
        None => eprintln!("[!] Value integrity check failed"),
    }
}

Integration Strategies

Strategy 1: Incremental Protection

Start with low-risk areas, then expand:

// Phase 1: Protect only initialization
fn init_with_protection(protector: &Protector) {
    println!("[*] System initializing with anti-debug enabled...");
    // Minimal protection during startup
}

// Phase 2: Protect business-critical operations
fn process_transaction(protector: &Protector, amount: f64) -> bool {
    protector.run_secure(&SecureVault::new(amount), |val, token| {
        validate_amount(*val, token as f64)
    })
}

// Phase 3: Protect everything
fn comprehensive_protection(protector: &Protector) {
    // All operations use run_secure or run_coupled
}

Strategy 2: Selective Protection by Feature Flag

// In Cargo.toml
// [features]
// max-protection = ["fdebug"]
// default = []

#[cfg(feature = "max-protection")]
use fdebug::protector::Protector;

#[cfg(feature = "max-protection")]
fn protect_operation<T, F>(protector: &Protector, operation: F) -> T 
where
    F: FnOnce() -> T,
{
    protector.run_secure(&SecureVault::new(()), |_, _| {
        operation()
    })
}

#[cfg(not(feature = "max-protection"))]
fn protect_operation<T, F>(_protector: &Protector, operation: F) -> T
where
    F: FnOnce() -> T,
{
    operation()
}

fn main() {
    let protector = Protector::new(0xBEEFCAFE);
    
    let result = protect_operation(&protector, || {
        // Business logic
        42
    });
    
    println!("Result: {}", result);
}

Strategy 3: Runtime Configuration

pub struct ProtectionConfig {
    enabled: bool,
    level: ProtectionLevel,
    monitor_interval: std::time::Duration,
}

pub enum ProtectionLevel {
    None,
    Low,
    Medium,
    High,
    Maximum,
}

impl ProtectionConfig {
    fn should_protect_operation(&self, operation_risk: u32) -> bool {
        if !self.enabled {
            return false;
        }
        
        let protection_threshold = match self.level {
            ProtectionLevel::None => u32::MAX,
            ProtectionLevel::Low => 100,
            ProtectionLevel::Medium => 50,
            ProtectionLevel::High => 25,
            ProtectionLevel::Maximum => 0,
        };
        
        operation_risk > protection_threshold
    }
}

fn process_with_config(
    protector: &Protector,
    config: &ProtectionConfig,
    data: &[u8],
) -> Vec<u8> {
    let operation_risk = data.len() as u32;
    
    if config.should_protect_operation(operation_risk) {
        // Protect high-risk operations
        let vault = SecureVault::new(data.to_vec());
        protector.run_secure(&vault, |data, token| {
            transform_with_token(data, token)
        })
    } else {
        // Low-risk operations skip protection
        data.to_vec()
    }
}

fn transform_with_token(data: &[u8], token: u64) -> Vec<u8> {
    let mut result = data.to_vec();
    for i in 0..result.len() {
        result[i] ^= ((token >> (i % 8 * 8)) as u8);
    }
    result
}

Real-World Use Cases

Use Case 1: Software Licensing

use fdebug::protector::{Protector, SecureVault, ShieldedExecution};
use chrono::{DateTime, Utc, Duration};

#[derive(Clone)]
struct License {
    key: String,
    expiration: DateTime<Utc>,
    product_id: u32,
    checksum: u64,
}

impl License {
    fn calculate_checksum(key: &str, exp: DateTime<Utc>, product: u32) -> u64 {
        let mut hash = 0u64;
        for byte in key.as_bytes() {
            hash = hash.wrapping_mul(31).wrapping_add(*byte as u64);
        }
        hash = hash.wrapping_add(exp.timestamp() as u64);
        hash = hash.wrapping_add(product as u64);
        hash ^ 0xDEADBEEFCAFEBABE
    }
    
    fn new(key: String, product_id: u32, days_valid: i64) -> Self {
        let expiration = Utc::now() + Duration::days(days_valid);
        let checksum = Self::calculate_checksum(&key, expiration, product_id);
        
        License {
            key,
            expiration,
            product_id,
            checksum,
        }
    }
    
    fn verify(&self, protector: &Protector) -> bool {
        // Verify integrity
        let expected = Self::calculate_checksum(&self.key, self.expiration, self.product_id);
        if expected != self.checksum {
            return false;
        }
        
        // Verify with security token
        let vault = SecureVault::new(self.clone());
        
        protector.run_secure(&vault, |lic, token| {
            // Check expiration
            if Utc::now() > lic.expiration {
                return false;
            }
            
            // Token-based verification
            let key_hash: u64 = lic.key.as_bytes().iter()
                .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(*b as u64));
            
            (key_hash ^ token ^ lic.product_id as u64) != 0
        })
    }
}

fn main() {
    let protector = Protector::new(0xDEADBEEF);
    
    let license = License::new(
        "ABC123DEF456GHI789JKL012MNO345PQR".to_string(),
        1001,
        30,
    );
    
    if license.verify(&protector) {
        println!("[+] License valid - feature unlocked");
    } else {
        println!("[-] License invalid or running under debugger");
    }
}

Use Case 2: API Key Protection

use fdebug::protector::{Protector, SecureVault, ShieldedExecution};
use std::sync::Mutex;

struct APIKeyManager {
    protector: Protector,
    keys: Mutex<Vec<String>>,
}

impl APIKeyManager {
    fn new(seed: u32) -> Self {
        APIKeyManager {
            protector: Protector::new(seed),
            keys: Mutex::new(Vec::new()),
        }
    }
    
    fn add_key(&self, key: String) {
        let vault = SecureVault::new(key.clone());
        
        let validated = self.protector.run_secure(&vault, |key, token| {
            // Validate key format (typical: 32+ alphanumeric)
            key.len() >= 32 && 
            key.chars().all(|c| c.is_alphanumeric()) &&
            (token % 7 == 0) // Token validation
        });
        
        if validated {
            if let Ok(mut keys) = self.keys.lock() {
                keys.push(key);
            }
        }
    }
    
    fn use_key<F, T>(&self, index: usize, operation: F) -> Option<T>
    where
        F: FnOnce(&str) -> T,
    {
        let keys = self.keys.lock().ok()?;
        let key = keys.get(index)?;
        
        let vault = SecureVault::new(key.clone());
        
        Some(self.protector.run_secure(&vault, |key, token| {
            if token == 0 {
                // Corrupted by debugger
                return operation(""); // Use empty key
            }
            operation(key)
        }))
    }
}

fn main() {
    let manager = APIKeyManager::new(0xACE123);
    
    manager.add_key("your_api_key_here".to_string());
    manager.add_key("your_api_key_here".to_string());
    
    manager.use_key(0, |api_key| {
        if api_key.is_empty() {
            eprintln!("[!] API key corrupted - environment compromised");
        } else {
            println!("[+] API key loaded safely: {}", &api_key[..16]);
        }
    });
}

Use Case 3: Sensitive Data Masking

use fdebug::protector::{Protector, SecureVault, ShieldedExecution, CoupledLogic, Corruptible};

#[derive(Clone)]
struct UserData {
    name: String,
    email: String,
    phone: String,
    ssn: String,
}

impl Corruptible for UserData {
    fn corrupt_if_needed(mut self, token: u64) -> Self {
        if token & 0xFF == 0 {
            // Corrupt sensitive data if running under debugger
            self.ssn = "000-00-0000".to_string();
            self.email = "redacted@example.com".to_string();
            self.phone = "555-1234".to_string();
        }
        self
    }
}

fn display_user_info(protector: &Protector, user: UserData) {
    let protected = protector.run_coupled(|token| {
        user
    });
    
    println!("Name: {}", protected.name);
    println!("Email: {}***", &protected.email[..3]);
    println!("Phone: {}***", &protected.phone[..3]);
    println!("SSN: {}***", &protected.ssn[..5]);
    
    if protected.ssn == "000-00-0000" {
        eprintln!("[!] Sensitive data has been masked - security check triggered");
    }
}

fn main() {
    let protector = Protector::new(0xBEEFCAFE);
    
    let user = UserData {
        name: "John Doe".to_string(),
        email: "john@example.com".to_string(),
        phone: "555-1234-5678".to_string(),
        ssn: "123-45-6789".to_string(),
    };
    
    display_user_info(&protector, user);
}

Performance Optimization

Optimization 1: Lazy Initialization

use std::sync::Once;

static INIT: Once = Once::new();
static mut PROTECTOR: Option<Protector> = None;

fn get_protector() -> &'static Protector {
    unsafe {
        INIT.call_once(|| {
            PROTECTOR = Some(Protector::new(get_dynamic_seed()));
        });
        PROTECTOR.as_ref().unwrap()
    }
}

fn main() {
    let protector = get_protector();
    
    // Use protector
    let vault = SecureVault::new(42u32);
    protector.run_secure(&vault, |val, _| {
        println!("Value: {}", val);
    });
}

Optimization 2: Batched Operations

fn process_batch(protector: &Protector, items: Vec<String>) -> Vec<u32> {
    items
        .into_iter()
        .map(|item| {
            let vault = SecureVault::new(item);
            protector.run_secure(&vault, |item, token| {
                (item.len() as u64 ^ token) as u32
            })
        })
        .collect()
}

// More efficient: Single run_secure wrapping the loop
fn process_batch_optimized(protector: &Protector, items: Vec<String>) -> Vec<u32> {
    let vault = SecureVault::new(items);
    
    protector.run_secure(&vault, |items, token| {
        items
            .iter()
            .enumerate()
            .map(|(i, item)| {
                ((item.len() as u64 ^ token).wrapping_add(i as u64)) as u32
            })
            .collect()
    })
}

Optimization 3: Selective Protection

fn process_data_selective(protector: &Protector, data: &[u8]) -> Vec<u8> {
    const PROTECTION_THRESHOLD: usize = 1024; // Protect data > 1KB
    
    if data.len() > PROTECTION_THRESHOLD {
        // Large data: full protection
        let vault = SecureVault::new(data.to_vec());
        protector.run_secure(&vault, |data, token| {
            transform_data(data, token)
        })
    } else {
        // Small data: skip protection overhead
        transform_data(data, 0xDEADBEEFCAFEBABE)
    }
}

fn transform_data(data: &[u8], token: u64) -> Vec<u8> {
    data.iter()
        .enumerate()
        .map(|(i, byte)| byte ^ ((token >> (i % 8 * 8)) as u8))
        .collect()
}

Testing and Validation

Test 1: Protection Effectiveness

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_protection_changes_result() {
        let protector = Protector::new(0xACE123);
        
        let vault = SecureVault::new(42u32);
        let result1 = protector.run_secure(&vault, |val, token| {
            val ^ (token as u32)
        });
        
        // Under debugger, result would be different
        // (but hard to test without actually running under debugger)
        assert!(result1 > 0 || result1 == 0); // Always true, just checking no panic
    }
    
    #[test]
    fn test_corruption_trait() {
        #[derive(Clone)]
        struct TestData(u32);
        
        impl Corruptible for TestData {
            fn corrupt_if_needed(mut self, token: u64) -> Self {
                if token == 0 {
                    self.0 = 0;
                }
                self
            }
        }
        
        let data = TestData(42);
        let corrupted = data.corrupt_if_needed(0);
        assert_eq!(corrupted.0, 0);
        
        let intact = data.corrupt_if_needed(1);
        assert_eq!(intact.0, 42);
    }
}

Test 2: Score Accumulation

#[cfg(test)]
mod score_tests {
    use super::*;
    
    #[test]
    fn test_suspicion_accumulates() {
        use fdebug::protector::{add_suspicion, get_suspicion_score, DetectionSeverity};
        
        let initial_score = get_suspicion_score();
        
        add_suspicion(DetectionSeverity::Low);
        let after_low = get_suspicion_score();
        assert!(after_low >= initial_score + 10);
        
        add_suspicion(DetectionSeverity::Critical);
        let after_critical = get_suspicion_score();
        assert!(after_critical > after_low);
    }
}

Security Considerations

Consideration 1: Seed Management

// ✅ GOOD - Use get_dynamic_seed() (changes every build)
let protector = Protector::new(get_dynamic_seed());

// ❌ POOR - Hardcoded seed (same across all builds)
let protector = Protector::new(0x12345678);

// ❌ VERY BAD - Predictable seed (uses time)
let seed = std::time::SystemTime::now()
    .duration_since(std::time::UNIX_EPOCH)
    .unwrap()
    .as_secs() as u32;
let protector = Protector::new(seed);

Consideration 2: Data Lifetime

// ❌ DANGEROUS - Data persists after secure_execution
let vault = SecureVault::new(secret_key.clone());
let cached_result = protector.run_secure(&vault, |key, _| {
    key.clone() // Caching is bad!
});
// Data is no longer protected!

// ✅ GOOD - Data only used within secure context
protector.run_secure(&vault, |key, token| {
    if token % 7 == 0 {
        use_key_immediately(key); // Consumed immediately
    }
});

Consideration 3: Token Validation

// ❌ CARELESS - Assumes token is always valid
fn decrypt_with_token(data: &[u8], token: u64) -> Vec<u8> {
    data.iter()
        .map(|b| b ^ ((token >> (b % 8 * 8)) as u8))
        .collect()
}

// ✅ SAFE - Validates token bounds
fn decrypt_with_token_safe(data: &[u8], token: u64) -> Option<Vec<u8>> {
    if token == 0 || token == u64::MAX {
        return None; // Token invalid
    }
    
    Some(data.iter()
        .map(|b| b ^ ((token >> (b % 8 * 8)) as u8))
        .collect())
}

Troubleshooting

Issue 1: False Positives in VMs

Problem: Application triggers detection in legitimate virtual machines.

Solution:

use fdebug::protector::global_state::DIAGNOSTIC_MODE;
use std::sync::atomic::Ordering;

fn check_environment() {
    // Enable diagnostic mode to see what's triggering
    DIAGNOSTIC_MODE.store(true, Ordering::Relaxed);
    
    let protector = Protector::new(get_dynamic_seed());
    
    // Check which checkpoints triggered
    use fdebug::protector::global_state::TRIGGERED_CHECKPOINTS;
    for (i, checkpoint) in TRIGGERED_CHECKPOINTS.iter().enumerate() {
        let score = checkpoint.load(Ordering::Relaxed);
        if score > 0 {
            eprintln!("Checkpoint {}: {} points", i, score);
        }
    }
}

Issue 2: Excessive Corruption

Problem: Legitimate use is getting corrupted.

Solution: Check if suspicion score is baseline:

use fdebug::protector::get_suspicion_score;

fn diagnose_corruption() {
    loop {
        let score = get_suspicion_score();
        println!("[*] Current suspicion: {}", score);
        
        if score > 50 {
            eprintln!("[!] High suspicion - check for:");
            eprintln!("    - Breakpoints in decoy functions");
            eprintln!("    - Hardware debugger (WinDbg)");
            eprintln!("    - VM environment");
            break;
        }
        
        std::thread::sleep(std::time::Duration::from_secs(5));
    }
}

Issue 3: Performance Degradation

Problem: Application is slow with protection enabled.

Solution: Profile and optimize:

use std::time::Instant;

fn benchmark_protected_operation(protector: &Protector) {
    let vault = SecureVault::new(vec![0u8; 1024]);
    
    let start = Instant::now();
    for _ in 0..1000 {
        let _ = protector.run_secure(&vault, |data, token| {
            data.len() as u64 ^ token
        });
    }
    let elapsed = start.elapsed();
    
    println!("[*] 1000 operations: {:?}", elapsed);
    println!("[*] Per-operation: {:?}", elapsed / 1000);
    
    // If > 1ms per operation, consider selective protection
    if elapsed > std::time::Duration::from_millis(1000) {
        eprintln!("[!] Consider reducing protection scope");
    }
}

Summary

The fdebug module provides enterprise-grade protection against debugging and reverse engineering. By following these patterns and best practices, you can:

  1. Detect debugging attempts with multi-vector detection
  2. Obfuscate security logic with polymorphic VM bytecode
  3. Protect critical data with cryptographic tokens
  4. Validate integration with automated tests
  5. Monitor system health with built-in watchdog

The key principle: make debugging so costly and unrewarding that attackers move on to easier targets.