Welcome to this new Medium post. In this article, I'll walk you through the implementation of a basic Windows File System Minifilter Driver capable of intercepting file operations at the kernel level
We will use the Windows Filter Manager framework to build a minifilter that hooks file system requests, monitor file access events, and demonstrate how to inspect or block operations such as file creation/opening
By the end of this post, you'll understand the core architecture of minifilter drivers, how callback registration works, and how to start building your own file system monitoring

Courses: Learn how offensive development works on Windows OS from beginner to advanced taking our courses, all explained in C++.
Technique Database: Access 70+ real offensive techniques with weekly updates, complete with code, PoCs, and AV scan results:
Modules: Dive deep into essential offensive topics with our modular text-training program! Get a new module every 14 days. Start at just $1.99 per module, or unlock lifetime access to all modules for $100.
Methodology
Before diving into the full source code, let's break down the logic behind the technique at a high level.
To intercept file system operations using a Windows Minifilter Driver, we follow these logical steps:
- Register the Minifilter: First, we register our driver with the Windows Filter Manager using
FltRegisterFilter. This tells the operating system that our driver wants to participate in the file system filtering pipeline. - Define the Callback Operations: Next, we specify which file system operations we want to intercept by filling the
FLT_OPERATION_REGISTRATIONarray. In this example, we register callbacks forIRP_MJ_CREATE, which is triggered whenever a file is opened or created - Start Filtering: Once registration is complete, we call
FltStartFiltering. At this point, the minifilter becomes active and Windows will begin invoking our callbacks whenever the registered operations occur - Inspect and Process File Requests: Inside the pre-operation callback, we retrieve information about the target file being accessed. This allows us to inspect the filename, extension, path, or other metadata before the request reaches the filesystem
- Allow or Block the Operation: Finally, based on our filtering logic, we either: Allow the request to continue normally, or Block it by completing the IRP with an error status such as
STATUS_ACCESS_DENIED
Code
Full code:
#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>
#pragma comment(lib, "FltMgr.lib")
#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")
PFLT_FILTER gFilterHandle = NULL;
DRIVER_UNLOAD DriverUnload;
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath);
FLT_PREOP_CALLBACK_STATUS PreCreateCallback(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext);
FLT_POSTOP_CALLBACK_STATUS PostCreateCallback(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags);
NTSTATUS FilterUnloadCallback(_In_ FLT_FILTER_UNLOAD_FLAGS Flags);
NTSTATUS InstanceSetupCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_SETUP_FLAGS Flags,
_In_ DEVICE_TYPE VolumeDeviceType,
_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType);
// Operations array
// Tells FltMgr which IRPs we want to intercept.
const FLT_OPERATION_REGISTRATION Callbacks[] =
{
{
IRP_MJ_CREATE, // Intercept file open / create
0, // No special flags
PreCreateCallback, // Pre-operation (before the FS sees it)
PostCreateCallback // Post-operation (after the FS completes it)
},
// Add more operations here as needed, e.g.:
// { IRP_MJ_WRITE, 0, PreWriteCallback, NULL },
// { IRP_MJ_READ, 0, PreReadCallback, NULL },
// { IRP_MJ_SET_INFORMATION, 0, PreSetInfoCallback, NULL },
{ IRP_MJ_OPERATION_END } // Mandatory sentinel
};
const FLT_REGISTRATION FilterRegistration =
{
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
// FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP_ON_UNLOAD
NULL, // ContextRegistration
Callbacks, // Pointer to operations array
FilterUnloadCallback, // Called when fltMC unload or DriverUnload
InstanceSetupCallback, // Called when filter attaches to a volume
NULL, // InstanceQueryTeardownCallback
NULL, // InstanceTeardownStartCallback
NULL, // InstanceTeardownCompleteCallback
NULL, // GenerateFileNameCallback
NULL, // NormalizeNameComponentCallback
NULL // TransactionNotificationCallback
};
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath){
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;
DbgPrint("[BasicMiniFilter] DriverEntry called.\n");
// Step 1: Register the filter with FltMgr.
// This does NOT start interception yet.
status = FltRegisterFilter(
DriverObject,
&FilterRegistration,
&gFilterHandle);
if (!NT_SUCCESS(status))
{
DbgPrint("[BasicMiniFilter] FltRegisterFilter failed: 0x%08X\n", status);
return status;
}
// Step 2: Start filtering. From this point callbacks will fire.
status = FltStartFiltering(gFilterHandle);
if (!NT_SUCCESS(status))
{
DbgPrint("[BasicMiniFilter] FltStartFiltering failed: 0x%08X\n", status);
FltUnregisterFilter(gFilterHandle);
gFilterHandle = NULL;
return status;
}
DbgPrint("[BasicMiniFilter] Loaded successfully.\n");
return STATUS_SUCCESS;
}
// FilterUnloadCallback
// Called by FltMgr when the filter is about to be unloaded.
// Must call FltUnregisterFilter to detach from all volumes.
NTSTATUS FilterUnloadCallback(_In_ FLT_FILTER_UNLOAD_FLAGS Flags){
UNREFERENCED_PARAMETER(Flags);
DbgPrint("[BasicMiniFilter] FilterUnloadCallback called.\n");
if (gFilterHandle != NULL)
{
FltUnregisterFilter(gFilterHandle);
gFilterHandle = NULL;
}
return STATUS_SUCCESS;
}
// InstanceSetupCallback
// Called when the filter is about to attach to a volume.
NTSTATUS InstanceSetupCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_SETUP_FLAGS Flags,
_In_ DEVICE_TYPE VolumeDeviceType,
_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType);
// Only attach to NTFS volumes.
// Remove this check to monitor all filesystems (FAT32, ReFS, network, etc.)
if (VolumeFilesystemType != FLT_FSTYPE_NTFS)
{
DbgPrint("[BasicMiniFilter] Skipping non-NTFS volume.\n");
return STATUS_FLT_DO_NOT_ATTACH;
}
DbgPrint("[BasicMiniFilter] Attaching to NTFS volume.\n");
return STATUS_SUCCESS;
}
// PreCreateCallback
// Called BEFORE the filesystem processes an IRP_MJ_CREATE.
FLT_PREOP_CALLBACK_STATUS PreCreateCallback(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
// Skip kernel-mode opens to avoid recursion and noise
if (Data->RequestorMode == KernelMode)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// Retrieve the normalized file name.
// FLT_FILE_NAME_QUERY_DEFAULT is safe here (PASSIVE_LEVEL, pre-create).
status = FltGetFileNameInformation(
Data,
FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
&nameInfo);
if (!NT_SUCCESS(status))
{
// Name not available (e.g. unnamed pipe, pagefile) — let it pass
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// Parse the name info to populate individual fields (Extension, FinalComponent, etc.)
FltParseFileNameInformation(nameInfo);
DbgPrint("[BasicMiniFilter] PRE-CREATE: %wZ\n", &nameInfo->Name);
// Example: Block access to any file named "blocked.txt"
UNICODE_STRING blockedName = RTL_CONSTANT_STRING(L"blocked.txt");
if (RtlEqualUnicodeString(&nameInfo->FinalComponent, &blockedName, TRUE /*case insensitive*/))
{
DbgPrint("[BasicMiniFilter] Blocking access to: %wZ\n", &nameInfo->Name);
// Set the completion status that the caller will see
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
FltReleaseFileNameInformation(nameInfo);
// FLT_PREOP_COMPLETE: tells FltMgr to complete the IRP right now,
// skipping the filesystem and going straight to post-processing / IRP completion.
return FLT_PREOP_COMPLETE;
}
FltReleaseFileNameInformation(nameInfo);
// Pass through; request post-callback so we can log the result.
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
// PostCreateCallback
// Called AFTER the filesystem has completed IRP_MJ_CREATE.
FLT_POSTOP_CALLBACK_STATUS PostCreateCallback(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
// FLTFL_POST_OPERATION_DRAINING: filter is being torn down, do minimal work
if (FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING))
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
DbgPrint("[BasicMiniFilter] POST-CREATE: status=0x%08X\n",
Data->IoStatus.Status);
return FLT_POSTOP_FINISHED_PROCESSING;
}
// INSTALL DRIVER
//
//copy "C:\Users\s12de\Documents\Github\kernelutils\MiniFiltersWDM\x64\Debug\MiniFiltersWDM.sys" C:\Windows\System32\drivers\MiniFiltersWDM.sys
//
//sc stop filecallbacks
//sc delete filecallbacks
//
//sc create MiniFiltersWDM type = filesys binpath = "C:\Windows\System32\drivers\MiniFiltersWDM.sys" start = demand
//
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters" / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters" / v "SupportedFeatures" / t REG_DWORD / d 3 / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances" / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances" / v "DefaultInstance" / t REG_SZ / d "MiniFiltersWDM Instance" / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / v "Altitude" / t REG_SZ / d "370030" / f
//reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / v "Flags" / t REG_DWORD / d 0 / f
//
//fltMC load MiniFiltersWDM.inf
[Version]
Signature = "$Windows NT$"
Class = "ActivityMonitor"
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %ManufacturerName%
DriverVer = 01/01/2024,1.0.0.0
CatalogFile = MiniFilterDriver.cat
PnpLockdown = 1 ; Correct key (not PnpLockdownFiles)
; ----------------------------------------------------------------
; Destination: Driver Store only (DIRID 13)
; DIRID 13 does NOT support COPYFLG_PROTECTED_WINDOWS_DRIVER_FILE (0x100)
; so DriverFiles section uses no flags.
; ----------------------------------------------------------------
[DestinationDirs]
MiniFilter.DriverFiles = 13 ; Driver Store (Win10 2004+)
[SourceDisksNames]
1 = %DiskName%
[SourceDisksFiles]
MiniFiltersWDM.sys = 1
; ----------------------------------------------------------------
; Install
; ----------------------------------------------------------------
[DefaultInstall.NTamd64]
OptionDesc = %ServiceDescription%
CopyFiles = MiniFilter.DriverFiles
[DefaultInstall.NTamd64.Services]
AddService = %ServiceName%,,MiniFilter.Service
; ----------------------------------------------------------------
; Uninstall
; LegacyUninstall=1 required when using DIRID 13 to support
; both Primitive INF and downlevel compatibility.
; ----------------------------------------------------------------
[DefaultUninstall.NTamd64]
LegacyUninstall = 1
; NOTE: DelFiles is NOT supported with DIRID 13.
; The Driver Store manages file cleanup automatically when DelService runs.
[DefaultUninstall.NTamd64.Services]
DelService = %ServiceName%,0x200 ; Stop and delete service
; ----------------------------------------------------------------
; Files
; ----------------------------------------------------------------
[MiniFilter.DriverFiles]
MiniFiltersWDM.sys ; No flags — 0x100 invalid with DIRID 13
; ----------------------------------------------------------------
; Service
; ----------------------------------------------------------------
[MiniFilter.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %13%\MiniFilterDriver.sys
ServiceType = 2 ; SERVICE_FILE_SYSTEM_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = MiniFilter.AddRegistry
[MiniFilter.AddRegistry]
; All minifilter registry values must live under the Parameters subkey
HKR,"Parameters","DebugFlags",0x00010001,0x0
HKR,"Parameters","SupportedFeatures",0x00010001,0x3
HKR,"Parameters\Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Parameters\Instances\"%DefaultInstance%,"Altitude",0x00000000,%MiniFilterAltitude%
HKR,"Parameters\Instances\"%DefaultInstance%,"Flags",0x00010001,0x0
; ----------------------------------------------------------------
; Strings
; ----------------------------------------------------------------
[Strings]
ManufacturerName = "YourName"
DiskName = "MiniFilterDriver Installation Disk"
ServiceName = "MiniFilterDriver"
ServiceDescription = "Basic MiniFilter Driver"
DefaultInstance = "MiniFilterDriver Instance"
MiniFilterAltitude = "370030"FLT_OPERATION_REGISTRATION
This structure tells the Filter Manager which I/O operations our minifilter wants to intercept:
const FLT_OPERATION_REGISTRATION Callbacks[] =
{
{
IRP_MJ_CREATE,
0,
PreCreateCallback,
PostCreateCallback
},
{ IRP_MJ_OPERATION_END }
};In this case, we intercept IRP_MJ_CREATE, which is triggered whenever a process attempts to create or open a file
PreCreateCallback
This callback is invoked before the filesystem processes the open/create request
Here we can:
- Inspect the file path
- Modify the request
- Block the operation entirely
PostCreateCallback
This callback executes after the filesystem has processed the request
Useful for:
- Logging success/failure
- Measuring latency
- Post-processing handles
Proof of Concept
Let's install and check the resuts:
Installation:
copy "C:\Users\s12de\Documents\Github\kernelutils\MiniFiltersWDM\x64\Debug\MiniFiltersWDM.sys" C:\Windows\System32\drivers\MiniFiltersWDM.sys
sc stop filecallbacks
sc delete filecallbacks
sc create MiniFiltersWDM type=filesys binpath="C:\Windows\System32\drivers\MiniFiltersWDM.sys" start=demand
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters" / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters" / v "SupportedFeatures" / t REG_DWORD / d 3 / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances" / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances" / v "DefaultInstance" / t REG_SZ / d "MiniFiltersWDM Instance" / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / v "Altitude" / t REG_SZ / d "370030" / f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\MiniFiltersWDM\Parameters\Instances\MiniFiltersWDM Instance" / v "Flags" / t REG_DWORD / d 0 / fAnd finally to start the driver:
fltMC load MiniFiltersWDMAnd then we open the DebugView application:

Conclusions
Windows Minifilter drivers provide a way to intercept and control file system operations at the kernel level. In this post, we explored how to register a minifilter, define callback routines, and process file I/O requests using the Filter Manager framework
📌 Follow me: YouTube | 🐦 X | 💬 Discord Server | 📸 Instagram | Newsletter
S12.