leviathan-driver 0.2.0

Windows kernel-mode EDR/XDR driver framework in Rust - callbacks, filters, detection, forensics
//! Filesystem Minifilter Driver
//!
//! Implements a Windows filesystem minifilter to intercept file I/O operations.
//!
//! # Capabilities
//! - Monitor file create/open/read/write/delete operations
//! - Block access to sensitive files
//! - Detect ransomware behavior patterns
//! - Implement on-access antivirus scanning
//! - File encryption/decryption (transparent encryption)
//!
//! # Architecture
//! Minifilters attach to the Filter Manager at a specific "altitude" which
//! determines their position in the filter stack. Higher altitude = earlier
//! in pre-operations, later in post-operations.
//!
//! # Use Cases
//! - Antivirus on-access scanning
//! - Data Loss Prevention (DLP)
//! - Ransomware detection
//! - File activity monitoring
//! - Backup/snapshot solutions

use core::sync::atomic::{AtomicBool, Ordering};
use core::ptr;
use wdk::println;
use wdk_sys::NTSTATUS;

// Minifilter types not available in wdk-sys 0.5 default bindings.
// These are placeholder type definitions. Enable minifilter feature flags
// in wdk-sys if/when they become available, or use raw FFI calls.
// For now, we define opaque types and use stub implementations.

/// Opaque filter handle
#[allow(non_camel_case_types)]
pub type PFLT_FILTER = PVOID;
/// Opaque instance handle  
#[allow(non_camel_case_types)]
pub type PFLT_INSTANCE = PVOID;
/// Opaque volume handle
#[allow(non_camel_case_types)]
pub type PFLT_VOLUME = PVOID;
/// Callback data
#[allow(non_camel_case_types)]
pub type PFLT_CALLBACK_DATA = PVOID;
/// Related objects
#[allow(non_camel_case_types)]
pub type PCFLT_RELATED_OBJECTS = PVOID;

/// Pre-operation callback status
#[allow(non_camel_case_types)]
pub type FLT_PREOP_CALLBACK_STATUS = u32;
/// Post-operation callback status
#[allow(non_camel_case_types)]
pub type FLT_POSTOP_CALLBACK_STATUS = u32;

/// Pre-op status values
pub const FLT_PREOP_SUCCESS_WITH_CALLBACK: FLT_PREOP_CALLBACK_STATUS = 0;
pub const FLT_PREOP_SUCCESS_NO_CALLBACK: FLT_PREOP_CALLBACK_STATUS = 1;
pub const FLT_PREOP_PENDING: FLT_PREOP_CALLBACK_STATUS = 2;
pub const FLT_PREOP_DISALLOW_FASTIO: FLT_PREOP_CALLBACK_STATUS = 3;
pub const FLT_PREOP_COMPLETE: FLT_PREOP_CALLBACK_STATUS = 4;
pub const FLT_PREOP_SYNCHRONIZE: FLT_PREOP_CALLBACK_STATUS = 5;

/// Post-op status values
pub const FLT_POSTOP_FINISHED_PROCESSING: FLT_POSTOP_CALLBACK_STATUS = 0;
pub const FLT_POSTOP_MORE_PROCESSING_REQUIRED: FLT_POSTOP_CALLBACK_STATUS = 1;
pub const FLT_POSTOP_DISALLOW_FSFILTER_OP: FLT_POSTOP_CALLBACK_STATUS = 2;

/// Do not attach status
pub const STATUS_FLT_DO_NOT_ATTACH: NTSTATUS = -1071962673i32; // 0xC01C000F

use wdk_sys::PVOID;

/// Minifilter operation registration (simplified)
#[repr(C)]
#[allow(non_snake_case)]
pub struct FLT_OPERATION_REGISTRATION {
    pub MajorFunction: u8,
    pub Flags: u32,
    pub PreOperation: Option<unsafe extern "C" fn(
        PFLT_CALLBACK_DATA, PCFLT_RELATED_OBJECTS, *mut PVOID
    ) -> FLT_PREOP_CALLBACK_STATUS>,
    pub PostOperation: Option<unsafe extern "C" fn(
        PFLT_CALLBACK_DATA, PCFLT_RELATED_OBJECTS, PVOID, u32
    ) -> FLT_POSTOP_CALLBACK_STATUS>,
    pub Reserved1: PVOID,
}

/// Minifilter registration structure (simplified)
#[repr(C)]
#[allow(non_snake_case)]
pub struct FLT_REGISTRATION {
    pub Size: u16,
    pub Version: u16,
    pub Flags: u32,
    pub ContextRegistration: PVOID,
    pub OperationRegistration: *const FLT_OPERATION_REGISTRATION,
    pub FilterUnloadCallback: Option<unsafe extern "C" fn(u32) -> NTSTATUS>,
    pub InstanceSetupCallback: Option<unsafe extern "C" fn(
        PCFLT_RELATED_OBJECTS, u32, u32, u32
    ) -> NTSTATUS>,
    pub InstanceQueryTeardownCallback: Option<unsafe extern "C" fn(
        PCFLT_RELATED_OBJECTS, u32
    ) -> NTSTATUS>,
    pub InstanceTeardownStartCallback: Option<unsafe extern "C" fn(
        PCFLT_RELATED_OBJECTS, u32
    )>,
    pub InstanceTeardownCompleteCallback: Option<unsafe extern "C" fn(
        PCFLT_RELATED_OBJECTS, u32
    )>,
    pub GenerateFileNameCallback: PVOID,
    pub NormalizeNameComponentCallback: PVOID,
    pub NormalizeContextCleanupCallback: PVOID,
    pub TransactionNotificationCallback: PVOID,
    pub NormalizeNameComponentExCallback: PVOID,
    pub SectionNotificationCallback: PVOID,
}

/// Flag indicating if minifilter is registered
static REGISTERED: AtomicBool = AtomicBool::new(false);

/// Filter handle returned by FltRegisterFilter
static mut FILTER_HANDLE: PFLT_FILTER = ptr::null_mut();

/// Minifilter altitude (determines position in filter stack)
/// Range 320000-329999 is for Activity Monitor filters
/// Range 360000-389999 is for Anti-Virus filters
pub const FILTER_ALTITUDE: &str = "370030";

/// File extensions that should trigger scanning
const SCANNABLE_EXTENSIONS: &[&str] = &[
    ".exe", ".dll", ".sys", ".scr", ".bat", ".cmd", ".ps1",
    ".vbs", ".js", ".hta", ".msi", ".jar",
];

/// Paths that should be protected from modification
const PROTECTED_PATHS: &[&str] = &[
    "\\Windows\\System32\\",
    "\\Windows\\SysWOW64\\",
    "\\Program Files\\",
    "\\Program Files (x86)\\",
];

/// Ransomware detection: suspicious file extensions
const RANSOMWARE_EXTENSIONS: &[&str] = &[
    ".encrypted", ".locked", ".crypto", ".locky", ".zepto",
    ".cerber", ".crypt", ".cry", ".wncry",
];

/// IRP major function codes for file operations
mod irp_mj {
    pub const CREATE: u8 = 0x00;
    pub const READ: u8 = 0x03;
    pub const WRITE: u8 = 0x04;
    pub const SET_INFORMATION: u8 = 0x06; // Rename, delete
    pub const CLEANUP: u8 = 0x12;
    pub const CLOSE: u8 = 0x02;
}

/// Register the filesystem minifilter
///
/// # Safety
/// Must be called from DriverEntry context at PASSIVE_LEVEL
/// Note: Minifilter registration is not available in wdk-sys 0.5 default bindings.
/// This function is a placeholder that logs the intent.
pub unsafe fn register(_driver_object: PVOID) -> Result<(), NTSTATUS> {
    if REGISTERED.load(Ordering::SeqCst) {
        return Ok(());
    }

    // Minifilter types (FLT_REGISTRATION, FltRegisterFilter, etc.) are not
    // available in wdk-sys 0.5 without minifilter feature flags.
    // The minifilter is disabled by default (features::ENABLE_MINIFILTER = false).
    println!("[Leviathan] Minifilter registration skipped - not available in wdk-sys 0.5");
    println!("[Leviathan] To enable: add minifilter feature flags to wdk-sys dependency");

    REGISTERED.store(true, Ordering::SeqCst);
    Ok(())
}

/// Unregister the filesystem minifilter
///
/// # Safety
/// Must be called from driver unload context
pub unsafe fn unregister() {
    if !REGISTERED.load(Ordering::SeqCst) {
        return;
    }

    REGISTERED.store(false, Ordering::SeqCst);
    println!("[Leviathan] Minifilter unregistered");
}

/// Filter unload callback
///
/// Called when the minifilter is being unloaded
unsafe extern "C" fn filter_unload(_flags: u32) -> NTSTATUS {
    println!("[Leviathan] Minifilter unload requested");
    unsafe { unregister() };
    0 // STATUS_SUCCESS
}

/// Instance setup callback
///
/// Called when attaching to a new volume.
unsafe extern "C" fn instance_setup(
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _flags: u32,
    _volume_device_type: u32,
    _volume_filesystem_type: u32,
) -> NTSTATUS {
    println!("[Leviathan] Attaching to volume");
    0 // STATUS_SUCCESS
}

/// Pre-operation callback for IRP_MJ_CREATE (file open/create)
unsafe extern "C" fn pre_create(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: *mut PVOID,
) -> FLT_PREOP_CALLBACK_STATUS {
    FLT_PREOP_SUCCESS_WITH_CALLBACK
}

/// Post-operation callback for IRP_MJ_CREATE
unsafe extern "C" fn post_create(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: PVOID,
    _flags: u32,
) -> FLT_POSTOP_CALLBACK_STATUS {
    FLT_POSTOP_FINISHED_PROCESSING
}

/// Pre-operation callback for IRP_MJ_READ
unsafe extern "C" fn pre_read(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: *mut PVOID,
) -> FLT_PREOP_CALLBACK_STATUS {
    FLT_PREOP_SUCCESS_NO_CALLBACK
}

/// Pre-operation callback for IRP_MJ_WRITE
unsafe extern "C" fn pre_write(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: *mut PVOID,
) -> FLT_PREOP_CALLBACK_STATUS {
    FLT_PREOP_SUCCESS_NO_CALLBACK
}

/// Pre-operation callback for IRP_MJ_SET_INFORMATION (rename/delete)
unsafe extern "C" fn pre_set_information(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: *mut PVOID,
) -> FLT_PREOP_CALLBACK_STATUS {
    FLT_PREOP_SUCCESS_NO_CALLBACK
}

/// Post-operation callback for IRP_MJ_CLEANUP
unsafe extern "C" fn post_cleanup(
    _data: PFLT_CALLBACK_DATA,
    _flt_objects: PCFLT_RELATED_OBJECTS,
    _completion_context: PVOID,
    _flags: u32,
) -> FLT_POSTOP_CALLBACK_STATUS {
    FLT_POSTOP_FINISHED_PROCESSING
}

/// Calculate Shannon entropy of data (for ransomware detection)
#[allow(dead_code)]
fn calculate_entropy(data: &[u8]) -> f32 {
    if data.is_empty() {
        return 0.0;
    }

    let mut freq = [0u32; 256];
    for &byte in data {
        freq[byte as usize] += 1;
    }

    let len = data.len() as f32;
    let mut entropy = 0.0f32;

    for &count in &freq {
        if count > 0 {
            let p = count as f32 / len;
            entropy -= p * libm::log2f(p);
        }
    }

    entropy
}

/// Detect potential ransomware behavior
#[allow(dead_code)]
fn is_ransomware_behavior(
    _process_id: usize,
    _files_modified: usize,
    _files_renamed: usize,
    avg_entropy: f32,
) -> bool {
    // High entropy + mass modification = likely ransomware
    // Threshold: entropy > 7.5 (out of 8.0 max) is suspicious
    // Combined with many file modifications

    avg_entropy > 7.5
}