File System Filter Driver Tutorial

Contents

1.      Introduction
2.      Creating a Simple File System Filter Driver
3.      How to install a driver
4.      Running a Sample
5.      Improvements
6.      Conclusion
7.      Useful references

Introduction

This tutorial will show you how to develop a simple file system filter driver. The demo driver will print the names of opening files to debug output.

The article requires basic windows driver and C/C++ knowledge. However it may be interesting to the people without windows driver experience.

What is a file system filter driver?

A file system filter driver is called on every file system I/O operation (create, read, write, rename and etc) and thus it can modify a file system behavior. File system filter drivers are almost similar to legacy drivers but they require some special steps to do.  Such drivers are used by anti-viruses, security, backup and snapshot software.

Creating a Simple File System Filter Driver

Before starting

To build a driver you need WDK or IFS Kit. You can get them from the Microsoft’s website. Also you have to set an environment variable %WINDDK% to the path where you have installed WDK/IFS Kit.

Be careful: Even a small error in driver may cause BSOD or system instability.

Main.c

Driver entry

This is an entry point of any driver. The first thing that we do is to store DriverObject to a global variable (we will need it later).

////////////////////////////////////////////////////////////////
// Global data

PDRIVER_OBJECT   g_fsFilterDriverObject = NULL;

///////////////////////////////////////////////////////////////
// DriverEntry - Entry point of the driver

NTSTATUS DriverEntry(
    __inout PDRIVER_OBJECT  DriverObject,
    __in    PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG    i      = 0;

    //ASSERT(FALSE); // This will break to debugger

    //
    // Store our driver object.
    //

    g_fsFilterDriverObject = DriverObject;
    ...
}

Set IRP dispatch table

The next step is to populate the IRP dispatch table with function pointers to IRP handlers. In our filter driver there is a generic pass-through IRP handler (which sends request further).  And we will need a handler for IRP_MJ_CREATE to retrieve names of the opening files. Implementation of the IRP handlers will be described later.

//////////////////////////////////////////////////////////////////////////
// DriverEntry - Entry point of the driver

NTSTATUS DriverEntry(
    __inout PDRIVER_OBJECT  DriverObject,
    __in    PUNICODE_STRING RegistryPath
    )
{
    ...
    //
    //  Initialize the driver object dispatch table.
    //

    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
    {
        DriverObject->MajorFunction[i] = FsFilterDispatchPassThrough;
    }

    DriverObject->MajorFunction[IRP_MJ_CREATE] = FsFilterDispatchCreate;
    ...
}

Set Fast-IO dispatch table

A file system filter driver must have the fast-io dispatch table. If you’ve forgot to set up the fast-io dispatch table it will lead system to crash. Fast-io is an alternative way to initiate I/O operation (and it’s faster than IRP). Fast-io operations are always synchronous. If fast-io handler returns FALSE then fast-io way is imposible and IRP will be created.

//////////////////////////////////////////////////////////////////////////
// Global data

FAST_IO_DISPATCH g_fastIoDispatch =
{
    sizeof(FAST_IO_DISPATCH),
    FsFilterFastIoCheckIfPossible,
    ...
};
//////////////////////////////////////////////////////////////////////////
// DriverEntry - Entry point of the driver

NTSTATUS DriverEntry(
    __inout PDRIVER_OBJECT  DriverObject,
    __in    PUNICODE_STRING RegistryPath
    )
{
    ...
    //
    // Set fast-io dispatch table.
    //

    DriverObject->FastIoDispatch = &g_fastIoDispatch;
    ...
}

Register a notification for file system changes

We should track file system being activated/deactivated to perform attaching/detaching of our file system filter driver.  How to start tracking file system changes is shown below.

//////////////////////////////////////////////////////////////////////////
// DriverEntry - Entry point of the driver

NTSTATUS DriverEntry(
    __inout PDRIVER_OBJECT  DriverObject,
    __in    PUNICODE_STRING RegistryPath
    )
{
    ...
    //
    //  Registered callback routine for file system changes.
    //

    status = IoRegisterFsRegistrationChange(DriverObject, FsFilterNotificationCallback);
    if (!NT_SUCCESS(status))
    {
        return status;
    }
    ...
}

Set driver unload routine

The last part of the driver initialization sets an unload routine. Setting the driver unload routine makes the driver unloadable and you can load/unload it multiple times without system restart. However this driver is made unloadable only for debugging purpose because file system filters can’t be unloaded safely. Never do this in production code.

//////////////////////////////////////////////////////////////////////////
// DriverEntry - Entry point of the driver

NTSTATUS DriverEntry(
    __inout PDRIVER_OBJECT  DriverObject,
    __in    PUNICODE_STRING RegistryPath
    )
{
    ...
    //
    // Set driver unload routine (debug purpose only).
    //

    DriverObject->DriverUnload = FsFilterUnload;

    return STATUS_SUCCESS;
}

More by Author

Must Read