Windows handles the stack.
Filter Manager manages volume attachment, filter ordering, and callback dispatch. FSDriver does not build a legacy attachment chain, create its own device, or expose an IRP dispatch table to a client.
Implementation notes / C and the Windows kernel
Follow a file request beneath the application. A small C driver uses Filter Manager callbacks to observe file activity, inspect write buffers, and demonstrate opt-in blocking.
Educational kernel driver. Monitor-only by default.
Source-based walkthroughs—not live kernel traces.
01 / The problem
Saving a document looks like one action in an application. Underneath, it can involve opening a file, writing bytes, renaming a temporary file, and closing handles. A file-system minifilter is a kernel component that can receive callbacks around selected operations as they pass through the Windows file-system stack.
This project makes that mechanism visible. It reports activity for paths containing \FSDRIVERTEST and demonstrates how a callback can allow a request or complete it with an error. The goal is to understand kernel contracts and ownership—not to claim a production antivirus, audit trail, or access-control system.
The browser only explains this flow. It cannot load the driver, read your files, or change any kernel flags.
02 / Architecture
Filter Manager manages volume attachment, filter ordering, and callback dispatch. FSDriver does not build a legacy attachment chain, create its own device, or expose an IRP dispatch table to a client.
A registration table selects operations. Helpers obtain and release normalized file names, match the watched substring, print diagnostic messages, and optionally inspect or reject selected requests.
The package declares a demand-start file-system service, its dependency on FltMgr, and an instance altitude. The source, stamped INF, and signed catalog have distinct jobs.
This is not a user-mode FileSystemWatcher. Its callbacks run inside the kernel request path. A mistake can affect the whole OS, which is why installation and experiments belong in a disposable VM.
Ordinary file operations drive the callbacks. Messages use DbgPrintEx; WinDbg can change three private-symbol flags. There is no service UI, communication port, or browser-to-driver connection.
Read the implementation ↗ · Inspect the installation manifest ↗
03 / Driver lifecycle
DriverEntry reports the watched substring and default monitor-only state.FltRegisterFilter receives FilterRegistration, which references the operation table and unload callback.FltStartFiltering begins filtering. If startup fails after registration, the driver unregisters before returning the failure.FsDriverUnload calls FltUnregisterFilter and clears the handle. Unregistration waits for callbacks and instance teardown.Lifecycle sketch / simplified from source
DriverEntry
├─ FltRegisterFilter
│ └─ failure → return status
└─ FltStartFiltering
├─ failure → unregister → return
└─ success → callbacks can run
FsDriverUnload
└─ FltUnregisterFilter
└─ g_FilterHandle = NULL
The callback wiring is data, not a hidden framework convention: Callbacks[] → FilterRegistration → FltRegisterFilter. An operation not registered in the table does not reach this driver.
04 / Callback contracts
Most messages are emitted before the file system finishes. Create/open is different: its post-operation callback can distinguish a successful new creation from opening or overwriting an existing file.
| Operation | Where it is handled | What it tells you |
|---|---|---|
IRP_MJ_CREATE | Pre + post-create | Requests a post callback; reports successful create, open, overwrite, or supersede outcomes for watched names. |
IRP_MJ_READ | Pre-operation | Reports a read request. It does not report bytes actually read. |
IRP_MJ_WRITE | Pre-operation | Reports a write request; can inspect a bounded prefix or reject the request when its flag is enabled. |
IRP_MJ_SET_INFORMATION | Pre-operation | Distinguishes rename, delete-disposition, and end-of-file size requests. Only delete-disposition requests have an optional block here. |
IRP_MJ_CLEANUP | Pre-operation | Reports last-handle cleanup for a file object when name lookup succeeds. |
IRP_MJ_CLOSE | Registered, reporting skipped | The reporting helper avoids querying the name during file-object teardown. |
FLT_PREOP_SUCCESS_NO_CALLBACK lets processing continue without requesting a post-operation callback from this filter.
FLT_PREOP_SUCCESS_WITH_CALLBACK asks Filter Manager to invoke this filter again after create processing.
FLT_PREOP_COMPLETE stops the request here. The block path sets STATUS_ACCESS_DENIED and clears IoStatus.Information.
05 / Source-based walkthrough
These curated scenarios explain branches in the C source. They are not a kernel simulator, a recorded test run, or proof of runtime behavior. Watched-file scenarios assume non-paging I/O at PASSIVE_LEVEL and successful name lookup unless stated otherwise.
Showing: Write / monitor. All flags off.
g_BlockWrites = 0 · g_BlockDeletes = 0 · g_InspectWrites = 0
06 / Decisions and tradeoffs
FltGetFileNameInformation returns a reference that must be released with FltReleaseFileNameInformation. The helper uses a length-bounded UNICODE_STRING comparison instead of treating a path as a null-terminated C string.
The helper skips reporting above PASSIVE_LEVEL, for paging I/O, and at close. Unicode debug output requires that IRQL. Failed name queries also return without blocking: incomplete visibility is a deliberate limitation, not a retry loop.
The write-inspection helper uses an existing MDL or attempts FltLockUserBuffer, maps pages with MmGetSystemAddressForMdlSafe, and guards the read with structured exception handling. Text output is capped at 64 bytes and may stop at a null byte.
Three volatile BOOLEAN flags default to false. Matching is a substring test across attached volumes: FsDriverTestOther can also match. It is not an exact C:\FsDriverTest allowlist.
Debugger detail that matters: use eb for the one-byte flags, not ed. A four-byte write can overwrite neighboring data. The historical design notes are preserved, but the standalone source and README correct those commands.
07 / Build and run it yourself
The supported package target is Windows 11 24H2 or newer. It uses Parameters\Instances registration and matching catalog targets. The INF section names do not enforce an OS minimum; check your test VM version before installing.
Kernel driver, real consequences. Use a disposable snapshot-backed VM with no valuable files. Keep a kernel debugger and recovery path available. Do not install this learning driver on the development host or a production machine.
Install Visual Studio with Desktop development with C++, a compatible Windows SDK and WDK, and WDK integration. Open FSDriver/FSDriver.slnx and choose Debug | x64, or run this from Developer PowerShell at the repository root:
msbuild .\FSDriver\FSDriver.vcxproj /m /p:Configuration=Debug /p:Platform=x64
Keep the generated package together
FSDriver.sys is the driver, the stamped FSDriver.inf installs it, and fsdriver.cat signs the package contents. Keep the matching FSDriver.pdb for debugging. Do not deploy the unstamped source INF.
Take a snapshot, configure kernel debugging and VM test-signing policy, and reboot as required. Secure Boot policy can restrict test signing; do not weaken the host to work around it.
Trust the build's public test certificate in the VM's Local Machine Trusted Root and Trusted Publishers stores as required by your signing setup. Never copy private signing keys. Transfer the complete generated package.
The repository does not provide a production signing certificate, an assigned production altitude, or an automatic driver installer.
pnputil /add-driver .\FSDriver.inf /install
sc.exe qc FSDriver
fltmc load FSDriver
fltmc filters
fltmc instances -f FSDriver
Non-PnP primitive driver package
Record the published oemNN.inf name. Verify service creation rather than relying on a device-match message. Altitude 370000 must not collide with another filter; it is for local testing only.
New-Item -ItemType Directory C:\FsDriverTest -Force
Set-Content C:\FsDriverTest\notes.txt 'hello filter'
Get-Content C:\FsDriverTest\notes.txt
Rename-Item C:\FsDriverTest\notes.txt renamed.txt
Remove-Item C:\FsDriverTest\renamed.txt
Observe in the kernel debugger
Look for FsDriver: messages. If needed, enable error-level output with ed nt!Kd_IHVDRIVER_Mask 1; this mask really is 32-bit. Callback messages do not map one-to-one to shell commands.
| Command | Demonstrates | Restore afterward |
|---|---|---|
eb FSDriver!g_InspectWrites 1 | Bounded text inspection of eligible writes. Use only non-sensitive test content. | eb FSDriver!g_InspectWrites 0 |
eb FSDriver!g_BlockWrites 1 | Access denied for an eligible write request to an existing watched file. | eb FSDriver!g_BlockWrites 0 |
eb FSDriver!g_BlockDeletes 1 | Access denied for an eligible delete-disposition request. | eb FSDriver!g_BlockDeletes 0 |
Finish the experiment: reset the flags, verify normal file operations, then run fltmc unload FSDriver. Remove the package with pnputil /delete-driver oemNN.inf /uninstall, using the exact published name recorded earlier. Reboot the VM if requested.
08 / Evidence and validation
The standalone integration has a verified Debug x64 package build using Visual Studio 2026 and WDK 10.0.28000.0. That establishes build and packaging results—not installation, behavioral correctness, or kernel safety.
FSDriver.sys only, with a debugger and recovery snapshot.No automated kernel-runtime tests were found for this project. There is no bundled runtime capture, performance benchmark, or verified deployment in this write-up. The interactive walkthrough above is authored from source, not generated by the driver.
The GitHub Pages workflow publishes documentation only. It does not compile, sign, or test the kernel driver.
09 / Limits and lessons
The transferable lesson is how to work inside a kernel callback contract: register only the operations you need, know which context you are in, pair every acquired reference with a release, initialize completion results when rejecting a request, and keep observed evidence separate from intended behavior.
10 / Continue in the repository
Build prerequisites, output locations, signing guidance, and the VM validation checklist.
Start with DriverEntry and the registration tables, then read the pre-operation callback, post-create callback, and reporting helpers.
Preserved internship explanations and an earlier source snapshot. They contain historical assumptions; use the current README for setup.
Extracted into a standalone project from the original internship work. The site shares its visual language with the Windows Heap Manager project.