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
- Paging I/O exclusion (
FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO): Read and write callbacks ignore memory-manager-initiated I/O. - Self-exclusion: All callbacks check
FltGetRequestorProcessId()againstg_clientProcessIdto prevent the monitorβs own I/O from triggering notifications. - Extension filter (
IsTargetExtension()): Only.exeand.dllfiles 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
- See how messages flow from kernel to user: Communication Architecture
- Full callback implementation details: Kernel Driver Module
- End-to-end interception walkthrough: File Interception Flow