The less code the better, right? Minifilters seem the perfect illustration of this. They can help many Windows developers spend less time writing code and lower the risk of introducing bugs.
In this article, we discuss how to develop minifilter drivers for Windows that will show open files in the debug output. To illustrate the simplicity and effectiveness of the minifilter approach, we use the example from our article on file system driver development.
This tutorial will be useful for C/C++ developers and Windows device driver developers that want to learn how to develop a simple file system minifilter driver.
Contents:
In Windows, there are two types of filter drivers: legacy file system filter drivers and minifilter drivers. A legacy Windows file system filter driver can directly modify file system behavior and is called during each file system input/output (I/O) operation.
However, Microsoft currently favors minifilter drivers. A minifilter driver connects to the file system indirectly by registering all needed callback filtering procedures in a filter manager.
The latter is a Windows file system filter driver that gets activated and connects to the file system stack only when a minifilter is loaded. The filter manager calls the filtering procedures registered by a minifilter for a specific I/O operation.
In short, even though they donโt have direct access to the file system, minifilters can still change system behavior. However, minifilters are much easier to write and use than their legacy alternatives. To illustrate this, we use an example filter from our previous Windows file system driver tutorial that displays the names of opened files in the debug output. Letโs see how we accomplished this task with the help of a minifilter driver.
Need a custom driver to enhance your device and systems?
Reach out to our kernel and driver developers to improve your systemโs stability, performance, and efficiency!
Implementing a file system minifilter driver for Windows
Before we start developing a minifilter driver, we need to install Visual Studio 2019 with all available SDKs and the Windows Driver Kit (WDK). Then we can move to the key files composing our minifilter driver:
- Main.cpp
- FsMinifilter.cpp
- FsMinifilter.inf
Main.cpp
To develop a file system minifilter driver for Windows, we first need to declare a global variable that will store the handle of our minifilter driver after itโs registered, launched, and stopped:
//
// The minifilter handle that results from a call to FltRegisterFilter
// NOTE: This handle must be passed to FltUnregisterFilter during minifilter unloading
//
PFLT_FILTER g_minifilterHandle = NULL;
Then we need to implement the DriverEntry function, which is our driverโs entry point. We register our minifilter driver in this function:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
//
// register minifilter driver
//
NTSTATUS status = FltRegisterFilter(DriverObject, &g_filterRegistration, &g_minifilterHandle);
if (!NT_SUCCESS(status))
{
return status;
}
// ...
}
Next, we need to call the FltStartFiltering function to launch our minifilter so it can actually start filtering I/O requests:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
// ...
//
// start minifilter driver
//
status = FltStartFiltering(g_minifilterHandle);
if (!NT_SUCCESS(status))
{
FltUnregisterFilter(g_minifilterHandle);
}
return status;
}
When registering the minifilter, we pass the address of the FLT_REGISTRATION struct object to the FltRegisterFilter function:
//
// The FLT_REGISTRATION structure provides information about a file system minifilter to the filter manager.
//
CONST FLT_REGISTRATION g_filterRegistration =
{
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
NULL, // Context registration
g_callbacks, // Operation callbacks
InstanceFilterUnloadCallback, // FilterUnload
InstanceSetupCallback, // InstanceSetup
InstanceQueryTeardownCallback, // InstanceQueryTeardown
NULL, // InstanceTeardownStart
NULL, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
};
Read also
How to Hook 64-Bit Code from WOW64 32-Bit Mode
Maintain seamless compatibility between your 32- and 64-bit applications to enhance your system security and integrity with our step-by-step guide!
The FltRegisterFilter function contains special callback procedures for loading, initiating, and disabling the driver. Weโll get back to these procedures a bit later.
Right now, we need to pass the array of the FLT_OPERATION_REGISTRATION structures describing pre-operation and post-operation procedures for a specific I/O operation to the FLT_REGISTRATION structure. For our minifilter, registering a pre-operation callback for the IRP_MJ_CREATE operation will be enough:
//
// Constant FLT_REGISTRATION structure for our filter.
// This initializes the callback routines our filter wants to register for.
//
CONST FLT_OPERATION_REGISTRATION g_callbacks[] =
{
{
IRP_MJ_CREATE,
0,
PreOperationCreate,
0
},
{ IRP_MJ_OPERATION_END }
};
FsMinifilter.cpp
This file contains all callback procedures needed for the operation of our minifilter driver.
A minifilter driver can register pre- and post-operation procedures for every I/O operation. These procedures are pretty similar to dispatch and completion procedures of a regular filter driver.
We need to implement a pre-operation procedure for the IRP_MJ_CREATE operation in which we output the file names:
FLT_PREOP_CALLBACK_STATUS FLTAPI PreOperationCreate(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
{
//
// Pre-create callback to get file info during creation or opening
//
DbgPrint("%wZ\n", &Data->Iopb->TargetFileObject->FileName);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
Next, we need to implement the InstanceFilterUnloadCallback procedure for unloading our minifilter. In this procedure, we call the FltUnregisterFilter function.
Note: Microsoft documentation strongly recommends registering this procedure so that the filter manager can unload the minifilter when needed:
NTSTATUS FLTAPI InstanceFilterUnloadCallback(_In_ FLT_FILTER_UNLOAD_FLAGS Flags)
{
//
// This is called before a filter is unloaded.
// If NULL is specified for this routine, then the filter can never be unloaded.
//
if (NULL != g_minifilterHandle)
{
FltUnregisterFilter(g_minifilterHandle);
}
return STATUS_SUCCESS;
}
The two other procedures โ InstanceSetupCallback and InstanceQueryTeardownCallback โ are needed for our driver to be able to connect to and disconnect from all disk partitions. These procedures are just stubs that do nothing and return STATUS_SUCCESS:
NTSTATUS FLTAPI InstanceSetupCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_SETUP_FLAGS Flags,
_In_ DEVICE_TYPE VolumeDeviceType,
_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
//
// This is called to see if a filter would like to attach an instance to the given volume.
//
return STATUS_SUCCESS;
}
NTSTATUS FLTAPI InstanceQueryTeardownCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
)
{
//
// This is called to see if the filter wants to detach from the given volume.
//
return STATUS_SUCCESS;
}
FsMinifilter.inf
When creating a driver project, Visual Studio generates an INF file: a configuration file containing all information that the operating system needs for installing a minifilter driver. We need to change some template values in the INF file generated by Visual Studio:
- Set Class and ClassGuid values for the minifilter driver. The ClassGuid value can be generated with the help of Visual Studio, while the Class value can be chosen in Windows documentation for minifilter development.
Here’s how we set those values in our example:
;; ...
[Version]
Class = "Bottom"
ClassGuid = {21D41938-DAA8-4615-86AE-E37344C18BD8}
;; ...
- Set the LoadOrderGroup value in accordance with the Class value chosen earlier:
;; ...
[MiniFilter.Service]
LoadOrderGroup = "FSFilter Bottom"
;; ...
- Set the Instance1.Altitude value, which determines the order in which a specific minifilter driver will be loaded within a particular Class (at your discretion):
;; ...
Instance1.Altitude = "47777"
;; ...
With these three files composed, we successfully created a minifilter driver and can now move to building and installing it.
Related project
Improving a Windows Audio Driver to Obtain a WHQL Release Signature
Explore how Apriorit’s expertise in driver development helped our client to improve their audio driver’s performance and stability, earning them a WHQL Release Signature.
Installing a minifilter driver
Now we need to install the driver using the INF file. To start, stop, and remove the driver, we use the Service Control (SC) utility (sc.exe).
Letโs start with installing the driver.
- To install the minifilter driver, right-click the INF file and select the Install option:
- Check if the driver was installed properly and then start it:
- To stop or delete our minifilter driver, we use the SC utility:
As you can see, our minifilter driver is quite easy to work with. But can it actually do the job? Letโs find out!
Checking the performance of our minifilter driver
To see if our code works as itโs supposed to, weโll use the following tools for minifilter development:
- Sysinternals DebugView utility for monitoring the debug output
- OSR DeviceTree utility for displaying devices and drivers
Once the minifilter driver is up and running, open the DebugView utility to see the names of all opened files:
We also need to launch the DeviceTree utility to see how our minifilter driver connects to volume devices. As itโs the filter manager and not the minifilter itself that connects directly to the file system, in the device tree of our file system, weโll see the filter manager but no minifilters:
As you can see, building and attaching a file system minifilter driver takes much less effort than working with legacy filter drivers.
If you want to learn more about drivers, check out our another article that explains how to develop virtual disk driver for any Windows version.
Read also
Controlling and Monitoring a Network with User Mode and Driver Mode Techniques: Overview, Pros and Cons, WFP Implementation
Take charge of your network traffic! Optimize your network management and get insights on implementing powerful user-mode and driver-mode techniques from a guide by Apriorit experts.
Conclusion
Minifilter drivers can provide the same results as legacy file system filter drivers but require less effort to develop. When working with minifilters, you can implement filters for precisely the I/O operations you need for the task at hand.
Thanks to their simplicity, flexibility, reliability, and great performance, minifilters are the main filter driver development approach that Microsoft recommends for Windows systems. In this Windows filter driver development tutorial, we showed you the basics of creating one yourself. And if you want to learn how to test drivers, check out our article on Windows driver testing basics.
At Apriorit, we have a team of creative kernel and driver development experts and file system minifilter developers who have mastered the art of building device drivers and minifilters of any complexity. Leverage our expertise in Windows driver model development.
Looking for a dedicated driver development team?
Benefit from our extensive 20-year track record in specialized driver development to reinforce your product and broaden its features!