Driver Architecture

Deep dive into the kernel-mode minifilter driver: registration, callback pipeline, stream context management, and IRP handling.

Related: System Overview Β· Communication Architecture Β· Kernel Driver Module Β· File Interception Flow


1. Filter Manager Integration

The driver registers with the Windows Filter Manager (FltMgr.sys) as a file system minifilter at altitude 47777 in the FSFilter Activity Monitor group. This places it near the bottom of the filter stack β€” it observes operations but does not modify them.

flowchart TB
    App["Application I/O Request"]
    IoMgr["I/O Manager"]
    FltMgr["Filter Manager (FltMgr.sys)"]
    
    subgraph FilterStack["Minifilter Stack (by Altitude)"]
        direction TB
        AV["Antivirus Filter\n(Altitude ~320000)"]
        Encrypt["Encryption Filter\n(Altitude ~140000)"]
        OurFilter["WindowsFileSystemMinifilter\n(Altitude 47777)"]
    end
    
    FS["File System Driver\n(NTFS / ReFS)"]
    Disk["Disk Driver"]

    App --> IoMgr --> FltMgr
    FltMgr --> AV --> Encrypt --> OurFilter
    OurFilter --> FS --> Disk

    style OurFilter fill:#e63946,color:#fff,stroke:#fff,stroke-width:2px
    style FilterStack fill:#1d3557,color:#fff

2. Registration Structure

The FLT_REGISTRATION structure in FsMinifilter.cpp tells Filter Manager everything about the driver:

classDiagram
    class FLT_REGISTRATION {
        +Size : USHORT
        +Version : USHORT
        +Flags : FLT_REGISTRATION_FLAGS
        +ContextRegistration : FLT_CONTEXT_REGISTRATION[]
        +OperationRegistration : FLT_OPERATION_REGISTRATION[]
        +FilterUnloadCallback()
        +InstanceSetupCallback()
        +InstanceQueryTeardownCallback()
    }

    class FLT_OPERATION_REGISTRATION {
        +MajorFunction : UCHAR
        +Flags : FLT_OPERATION_REGISTRATION_FLAGS
        +PreOperation : PFLT_PRE_OPERATION_CALLBACK
        +PostOperation : PFLT_POST_OPERATION_CALLBACK
    }

    class FLT_CONTEXT_REGISTRATION {
        +ContextType : FLT_CONTEXT_TYPE
        +Flags : USHORT
        +CleanupCallback : PFLT_CONTEXT_CLEANUP_CALLBACK
        +Size : SIZE_T
        +PoolTag : ULONG
    }

    FLT_REGISTRATION --> "5" FLT_OPERATION_REGISTRATION : g_callbacks
    FLT_REGISTRATION --> "1" FLT_CONTEXT_REGISTRATION : g_contextRegistration

3. Callback Registration

The driver registers pre-operation and post-operation callbacks for five IRP major function codes:

IRP Major Function Pre-Callback Post-Callback Purpose Flags
IRP_MJ_CREATE βœ… βœ… Detect file opens and FILE_DELETE_ON_CLOSE β€”
IRP_MJ_READ βœ… ❌ Monitor file reads on .exe/.dll SKIP_PAGING_IO
IRP_MJ_WRITE βœ… ❌ Monitor file modifications on .exe/.dll SKIP_PAGING_IO
IRP_MJ_SET_INFORMATION βœ… βœ… Track delete disposition changes SKIP_PAGING_IO
IRP_MJ_CLEANUP βœ… βœ… Confirm actual file deletion β€”

Callback Pipeline

sequenceDiagram
    participant App as Application
    participant FM as Filter Manager
    participant Pre as Pre-Op Callback
    participant FS as File System
    participant Post as Post-Op Callback
    participant UM as User Mode Monitor

    App->>FM: I/O Request (e.g., CreateFile)
    FM->>Pre: PreOperationXxx()
    
    alt Pre returns FLT_PREOP_SYNCHRONIZE
        Pre-->>FM: Synchronize for PostOp
        FM->>FS: Forward to File System
        FS-->>FM: Completion
        FM->>Post: PostOperationXxx()
        Post->>UM: SendMessageToUserMode()
    else Pre returns SUCCESS_NO_CALLBACK
        Pre-->>FM: No PostOp needed
        FM->>FS: Forward to File System
    end

4. Stream Context Management

The driver uses stream contexts (FLT_STREAM_CONTEXT) to track the delete disposition state of files across multiple IRP operations. This is critical because file deletion in Windows spans multiple IRPs.

stateDiagram-v2
    [*] --> NoContext: File opened normally

    NoContext --> ContextCreated: FILE_DELETE_ON_CLOSE\nor SetDisposition IRP
    
    ContextCreated --> DeleteOnClose: Create with\nFILE_DELETE_ON_CLOSE flag
    ContextCreated --> SetDispTrue: SetFileDispositionInfo\n(DeleteFile = TRUE)
    
    SetDispTrue --> SetDispFalse: SetFileDispositionInfo\n(DeleteFile = FALSE)
    SetDispFalse --> SetDispTrue: SetFileDispositionInfo\n(DeleteFile = TRUE)
    
    DeleteOnClose --> Cleanup: IRP_MJ_CLEANUP
    SetDispTrue --> Cleanup: IRP_MJ_CLEANUP
    
    Cleanup --> Deleted: FltQueryInformationFile\nreturns STATUS_FILE_DELETED
    Cleanup --> NotDeleted: File still exists
    
    Deleted --> Notified: SendMessageToUserMode\n(MSG_TYPE_FILE_DELETE)
    
    Notified --> [*]
    NotDeleted --> [*]

FS_STREAM_CONTEXT Structure

Field Type Purpose
NumOps volatile LONG Count of in-flight SetDisposition operations for race detection
IsNotified volatile LONG Ensures delete notification is sent exactly once
SetDisp BOOLEAN Current delete disposition state
DeleteOnClose BOOLEAN Whether file was opened with FILE_DELETE_ON_CLOSE

Race Safety: The NumOps counter uses InterlockedIncrement/InterlockedDecrement to handle concurrent SetDisposition operations. If NumOps > 1 in the pre-op, the driver skips synchronous post-op processing for that thread to avoid deadlocks.


5. File Targeting

Not all file operations trigger notifications. The driver applies a two-stage filter:

flowchart TD
    IRP["Incoming IRP"]
    
    IRP --> PagingCheck{"Paging I/O?"}
    PagingCheck -->|"Yes (READ/WRITE)"| Skip["Skip β€” No Callback"]
    PagingCheck -->|No| PIDCheck{"Process == Monitor PID?"}
    
    PIDCheck -->|Yes| Skip2["Skip β€” Self-Exclusion"]
    PIDCheck -->|No| ExtCheck{"Extension == .exe or .dll?"}
    
    ExtCheck -->|No| Skip3["Skip β€” Not Target"]
    ExtCheck -->|Yes| Send["Send to User Mode"]

    style Send fill:#2d6a4f,color:#fff
    style Skip fill:#6c757d,color:#fff
    style Skip2 fill:#6c757d,color:#fff
    style Skip3 fill:#6c757d,color:#fff
  1. Paging I/O exclusion (FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO): Read and write callbacks ignore memory-manager-initiated I/O.
  2. Self-exclusion: All callbacks check FltGetRequestorProcessId() against g_clientProcessId to prevent the monitor’s own I/O from triggering notifications.
  3. Extension filter (IsTargetExtension()): Only .exe and .dll files are forwarded. The check is case-insensitive and performed on the normalized file name.

6. Memory Management

Resource Allocation Pool Tag Lifetime
Stream Context FltAllocateContext Paged 'xSsF' Attached to stream, freed by Filter Manager
String buffers ExAllocatePool2 / inline Paged 'rSsF' Per-callback scope
File Name Info FltGetFileNameInformation Filter Manager β€” Released with FltReleaseFileNameInformation

The driver has zero dynamic heap allocations β€” all memory is managed through Filter Manager context APIs or stack-allocated structures.


Next Steps