The Depths of Windows I/O (Part 6, Final) — Filter Drivers and Minifilters: Why Procmon and Antivirus Scanners Can Intercept I/O

· · Windows, Win32, I/O, Minifilter, Kernel, Device Driver, Security, Bug Investigation

This is the final instalment of the series “The Depths of Windows I/O”.

Ever since Part 1 drew a box labelled “file system filters (antivirus, encryption, Procmon, and the like)” on the device stack diagram, the ones that sit in between have kept turning up throughout this series: why Procmon can record every I/O operation (Part 1); “file access is slow only in that one environment” (Part 2); the reparse point that makes OneDrive start downloading the instant a file is opened (Part 5). This instalment finally faces that interposing mechanism itself head-on — file system filter drivers and minifilters — and ties off every thread the series has left dangling.

1. The Bottom Line First

  • “Intercepting I/O” is an extension point the OS officially sanctions. A file system filter can see, rewrite, deny, or handle in its place a request made to the file system (Section 2).1
  • The current standard is the minifilter. To solve the problems of the legacy approach that intercepted the device stack directly (unpredictable ordering, no way to unload), the model changed generations to one that registers callbacks with the Filter Manager (FltMgr) that ships with Windows (Section 2).23
  • The mechanics are pre/post callbacks. Before and after each operation, filters are called in registration order — that is, altitude order. Pass through, complete, deny, rewrite — the “choices a driver has” from Part 1 apply directly (Section 3).2
  • Altitude decides the order. Each purpose-specific group is assigned a numeric band (Activity Monitor 360000-389999, Anti-Virus 320000-329999, and so on), and each instance attached to a volume gets a unique number within it (Section 4).45
  • You can see who lives on your PC with fltmc. Procmon (only while it is running), antivirus software, OneDrive’s cloud filter — they all line up here (Section 5).
  • An antivirus exclusion setting means “skip that product’s own scan” — it has no effect on other minifilters. Exclusions trade away protection, and for development volumes there is a safer option: Dev Drive (asynchronous scanning) (Section 6).67
  • Investigating “why is only this one environment slow” starts with Procmon’s Duration column and comparing fltmc configurations (Section 7).

2. A History of Those Who Interpose — From Legacy Filters to FltMgr

A file system filter driver is a driver that can intercept requests bound for the file system (or the volume beneath it). It can log a request, monitor it, alter its contents, and even deny it or handle it in the request’s place — it is the foundation on which antivirus software, encryption, backup, and hierarchical storage software are built.1

The old implementation approach (the legacy filter) stacked its own device object directly onto the device stack we saw in Part 1. The mechanism is straightforward, but in practice it was riddled with problems: the order in which filters stacked up depended on load order and was hard to guarantee, once stacked a filter could not safely get out again (it could not be unloaded), and it became a breeding ground for compatibility bugs between filters.

So Windows introduced the Filter Manager (FltMgr). FltMgr itself sits on the stack as an OS-supplied filter, and individual filter functionality registers callbacks with FltMgr as a minifilter.2

Minifilter approach - the current standardregisters callbackregisters callbackFilter Manager - FltMgrships with the OS, the only thing that sits on the stackMinifilter A - higher altitudeMinifilter B - lower altitudeFile systemOrder is deterministic via altitudecan load at any timea supporting filter can also unloadLegacy approachLegacy filter ALegacy filter BFile systemOrder is left to load ordercannot be unloaded safely

Figure 1: A change of generation — from “stacking” onto the stack to “registering” with FltMgr

The advantages of the minifilter model are officially enumerated — it can load at any time, ordering can be controlled, and a filter that implements an unload callback can even be unloaded while running (a filter that does not implement one, or that refuses, cannot be removed).3 To coexist with legacy filters, FltMgr can sit at multiple points on the stack as several “frames”, and a minifilter is guaranteed to return to the same position (the same altitude) even after being unloaded and reloaded.2 Practically all modern antivirus, monitoring, and sync software is built as this kind of minifilter.

3. How a Minifilter Behaves — Pre/Post Callbacks

A minifilter declares to FltMgr “which operations it is interested in”. For example, it might be interested only in IRP_MJ_CREATE (open) and IRP_MJ_WRITE (write). Then, every time such an operation flows through, its pre callback (before the operation) and post callback (after the operation) are invoked.

NTFSMinifilter Blower altitudeMinifilter Ahigher altitudeFltMgrI/O ManagerNTFSMinifilter Blower altitudeMinifilter Ahigher altitudeFltMgrI/O ManagerRequest - IRP_MJ_CREATE and the like, the world of Part 1pre callbackpre callbackTo the file systemResultpost callbackpost callbackCompletion - back into the completion flow from Part 1

Figure 2: Pre/post callbacks. The outbound leg is called in order from highest altitude to lowest, and the return leg in reverse order

What can each callback do? The same structure as “the three choices a driver has” from Part 1, Section 4.3, is offered here through a safer API.

The pre callback is invokedWhat to do with this operationPass it straight throughalso declare no post callback needed, if soDeny itreturn access denied or similar immediatelye.g. - virus detected, write blockedComplete it itselfe.g. - a cloud filter fetchesthe real content and hands it overModify parameters or content, then pass it one.g. - an encryption filter

Figure 3: The choices available to a pre callback. “Observe, stop, take over, or rewrite” are all officially supported

And here is where the homework from Part 4 gets collected: minifilters can also witness Fast I/O (the shortcut that skips creating an IRP). That is because FltMgr threads its callback mechanism through the Fast I/O path too, so there is none of the legacy-era problem of “being blind whenever the shortcut is taken”. The fact that Procmon’s log even lists FASTIO_ lines is entirely down to this vantage point.

4. Altitude — “Elevation” Decides the Order

When several filters are interested in the same operation, who sees it first is a serious question. If antivirus doesn’t see it before encryption, it ends up scanning ciphertext; if a monitoring tool isn’t above everyone else, it cannot observe the whole picture.

Altitude is what decides this order. A load order group and a numeric band are defined for each type of filter. To be precise, the unit that carries an altitude is not the driver as a whole but the “instance” of a minifilter attached to a volume. Numbers are unique, and the larger the number, the higher up the stack (closer to the application) it sits.4 A single driver can define multiple instances and appear at different altitudes, which is why the fltmc instances listing is per instance.

Closer to applications - larger numbersFSFilter Activity Monitor - 360000 to 389999observes and records I/O, Procmon lives hereFSFilter Undelete - 340000 to 349999recovering deleted filesFSFilter Anti-Virus - 320000 to 329999detecting and removing virusesFSFilter Replication - 300000 to 309999replicating to a remote locationFSFilter Continuous Backup - 280000 to 289999continuous backupFurther down - Content Screener,Quota Management, System Recovery,encryption and compression bands continueCloser to the file system - smaller numbers

Figure 4: Altitude bands - excerpt. Each purpose has a defined “elevation it should occupy”

The important point is that these numbers are allocated and managed by Microsoft.5 A vendor does not simply claim one; it applies and is granted one — which is why “monitoring sits above antivirus, and antivirus sits above encryption” holds as an order on every PC. This was the answer to the legacy era’s “load-order lottery.”

Where to apply if you’re building your own minifilter. For developers, here is just the next step to take. You request an altitude by following the procedure in Request a Filter Altitude Identifier: send an English-language email to fsfcomm@microsoft.com with the subject “Filter altitude request”. You need to fill in the company name; a contact (not a personal address but a company alias usable long-term); the product name; the product URL; a description of the filter; the driver’s file name; the filter type; the start type; and your desired load order group and desired altitude. It is also explicitly stated that you should plan for 30 business days of processing, that there is no expedited channel, and that the altitude you are assigned may differ from what you requested.8 Note also that a company that already holds an integer altitude within the same load order group may choose its own value by appending a decimal to that number (for example, 325000.3), and in that case a follow-up email after the fact is all that’s required.8

5. Meet the Residents — Your PC as Seen Through fltmc

Enough theory — let’s look at the real thing. From an elevated command prompt:

:: List of registered minifilters, with altitudes
fltmc

:: Which filters are attached to which volumes
fltmc instances

:: View it from the volume side
fltmc volumes

Running fltmc with no arguments produces the same listing as fltmc filters. The output has four columns, and Microsoft’s documentation shows an example in the same format.9

C:\Windows\system32>fltmc

Filter Name                     Num Instances    Altitude    Frame
------------------------------  -------------  ------------  -----
bindflt                                 1        409800         0
cldflt                                  1        409500         0
WdFilter                                4        328010         0
luafv                                   1        135000         0
FileInfo                                4         45000         0

The above is an illustrative excerpt. The cast of names and instance counts differ by environment, but because the altitude values are fixed numbers assigned by Microsoft, you can cross-check them against the published list mentioned in Section 4.

Here is what each column means.

Column Meaning
Filter Name The name of the filter (driver)
Num Instances How many volumes it is attached to (the instance count from Section 4)
Altitude The altitude. The larger the number, the closer to the application
Frame FltMgr’s frame number. If this reads <Legacy>, it is a sign that a legacy filter that does not use FltMgr is still alive.9

Even from just these five lines you can read off that bindflt and cldflt sit in the topmost FSFilter Top band (400000-409999), WdFilter sits in the Anti-Virus band (320000-329999), and FileInfo sits in the lowest FSFilter Bottom band (40000-49999). The picture from Section 4 — “each purpose has a defined elevation it should occupy” — is confirmed directly by the numbers. And if you start Procmon and run fltmc again, one more line appears in the Activity Monitor band (360000-389999), starting with PROCMON.

The cast varies by environment, but the typical residents are all familiar faces from this series.

  • WdFilter — Microsoft Defender’s minifilter. It sits in the Anti-Virus band. On many PCs it is a checkpoint that every single file I/O operation must pass through.
  • cldflt — the cloud files filter. It is the workhorse behind OneDrive’s Files On-Demand, and it prepares the real content when the reparse point (placeholder) we saw in Part 5 is opened.10
  • PROCMON24 (or similar) — a temporary minifilter in the Activity Monitor band that appears only while Process Monitor is running. This is the trick behind how Procmon can see every I/O operation.11 Try running fltmc before and after starting it and compare.
  • Beyond that, there are backup software, encryption (data-loss-prevention) products, EDR, virtualised storage, and more — the more business-oriented the PC, the more residents it accumulates.

Looking back, in this final instalment, at Procmon — the tool we have used since Part 1 — from outside the toolbox, we find a neat closed loop: the observer, too, turns out to be a resident of the very mechanism it observes.

6. Where Does Antivirus Software Spend Its Time?

The single biggest practical impact of filters is the cost of antivirus scanning. Here is a diagram of where the time is actually spent (details vary by product; what follows is a typical shape).

NTFSAV minifilterApplicationNTFSAV minifilterApplicationpre-create - evaluating the path and policy up frontIf the file hasn't been scanned yet,scan its contents hereand revoke the open if there's a problemthis is the main reason opens get slowA modified file becomesa rescan target, e.g. on closeWith a large number of small files, such as build intermediates,this round trip piles up once per fileOpen a filePass it through - performs the openOpen succeeds - post-createIf there's no problem, the handle is returnedWrite and close

Figure 5: Where scanning cost is incurred. Small per file, it dominates once you have tens of thousands of files

With this in mind, two practical topics can be understood precisely.

What an exclusion setting technically does. For I/O on a path matching the exclusion list, the filter’s scanning is skipped. The filter does not vanish from the stack — what actually happens is that the decision “don’t inspect this” gets made earlier. There is one more important limitation: an exclusion only works for the filter belonging to the product that holds that setting. Microsoft Defender’s exclusion setting changes WdFilter’s scanning, and has no effect whatsoever on the behaviour of any other minifilter sitting alongside it (a competing antivirus product, EDR, backup, encryption, and so on). If “I added an exclusion and it’s still slow”, suspect that a different resident is spending the time (compare with fltmc in Section 7). The effect is large, but an exclusion reliably weakens protection at that location. Microsoft’s own documentation repeatedly warns that, because exclusions reduce defences, they should be kept to a minimum after weighing the risk.6 The practical handling of false positives and their performance impact is covered in “When Your In-House Windows App Gets Flagged as a Virus”.

Dev Drive — a new answer. This is a dedicated volume designed for development workloads (lots of small files), on which Microsoft Defender runs in performance mode (asynchronous scanning). It is positioned as a safer alternative to folder exclusions: by default no additional filters get attached, while at the same time a strong warning is spelled out against running with every filter removed.7 It is Microsoft’s current recommended answer to “I want builds to be faster, but exclusions scare me.”

7. The Investigation Procedure for “Only This One Environment Is Slow”

As a final step, let’s fold every tool this series has built up into a single procedure.

YesYesNoNo - sporadicSymptom - the same app, but file access is slow only in one particular environmentLook at Procmon's Duration columnwhich operation - IRP_MJ_CREATE? WRITE?is where the time goesIs one specific operation uniformly slow?Compare fltmc instances against a fast environmentlook at the difference in filter configurationIs a filter in the difference the cause?An exclusion setting - with a risk assessment - orconsidering Dev Drive, or talking to the vendorSuspect something other than a filter -the cache Part 4, fragmentation or the MFT Part 5,the network destination UNC, or the device itself

Figure 6: Isolating filter-caused slowness. The keys are “time taken per operation” and “the difference in filter configuration between environments”

There are two key points. First, Procmon carries a per-operation duration (Duration). If you can break “it’s slow” down into “which operation is slow”, the manhunt is half done. Second, differences between environments are often differences in filter configuration. A development machine versus a production machine, your own PC versus a customer’s PC — simply lining up fltmc output side by side reveals which candidates deserve suspicion.

The first three moves if you’ve never touched Procmon. The Duration column is not shown by default, so here — to keep you from getting stuck at this step — are just the operations needed.

  1. Launch Procmon.exe as an administrator.
  2. Open Options menu > Select Columns… and check Duration in the list of columns.
  3. In Filter menu > Filter… (Ctrl+L), enter Process Name / is / the target exe name / Include, then click Add before clicking OK (the condition is not applied unless you click Add).

With that done, click the Duration column to sort it, and the operations eating the most time collect at the top. For a per-process or per-file summary, you can also use Tools menu > File Summary. General Procmon operation is covered in full in “A Practical Guide to Process Monitor (ProcMon)”.

8. The Series, Brought Together — A Map of Six Instalments

With that, every box on the map drawn in Part 1 has been opened. Let’s put the whole thing on one diagram.

completion returns hereCache-enabled I/O cooperatesthe file system calls into the cacheinterrupt to completion, Part 1ApplicationReadFile / WriteFile / async-awaitPart 2 - Synchronous and asynchronous I/Ohandle modes and OVERLAPPEDPart 3 - IOCP and the .NET thread poolreceiving completions and running continuationsPart 1 - I/O Manager and the IRPname resolution, three objects, the device stackPart 6 - Filters and minifiltersFltMgr, altitude, pre/postPart 4 - The Cache Manager256KB views, the lazy writer, Fast I/Oworks in cooperation with NTFSPart 5 - NTFSthe MFT, streams, links, two journalsThe storage stack and the device

Figure 7: A map of the whole series. The Cache Manager is not a “layer things pass through” but a partner that cooperates with the file system; on a cache miss, NTFS issues the request on to storage

9. Summary — Closing Out the Series

Here is the summary for this final instalment.

  • Intercepting I/O is an extension point the OS officially sanctions, and the current standard is registering callbacks with FltMgr, i.e. a minifilter. Order is decided deterministically by altitude, and Microsoft allocates and manages the numbers.245
  • The mechanics are pre/post callbacks. You can pass through, deny, take over, or rewrite, and you can witness Fast I/O too. Procmon, Defender, and OneDrive are all residents of this same mechanism.11110
  • An exclusion setting equals skipping the scan, and it trades away protection. For development volumes there is a safer option: Dev Drive (asynchronous scanning).67
  • “Only this one environment is slow” gets isolated using Procmon’s Duration and the configuration difference reported by fltmc — the series’ tools become the investigation procedure itself, unchanged.

And if the whole series has to be summed up in a single line, it is this — Windows I/O is a consistent design in which the destination is decided in a namespace, the request is packed into a packet, the IRP, and flowed between layers, and each layer is built so it can choose to see it, hold it, or take it over. Underneath a single line of File.ReadAllText, this six-part structure runs every single time. Instead of memorising how an API behaves, being able to derive “it must work this way” from this map — that is the capability this series wanted to leave you with. Thank you for staying with this long journey.

KomuraSoft LLC handles investigations into performance problems and defects in Windows business applications that involve filter drivers — cases such as “it’s slow only in one particular environment” or “security software interferes with our own application.”

References

  1. Microsoft Learn, About file system filter drivers. On file system filter drivers being optional drivers that can intercept requests bound for the file system or for other filter drivers; on how intercepting a request lets you extend or replace functionality before it reaches its original destination, and lets you log requests, monitor them, modify data, and prevent operations; and on antivirus utilities, encryption programs, and hierarchical storage management systems being examples of filter drivers.  2 3

  2. Microsoft Learn, Filter Manager Concepts. On the Filter Manager (FltMgr) being a kernel-mode driver that ships with Windows and exposes functionality that simplifies minifilter driver development; on minifilters being able to register processing before and after I/O operations (pre/post callbacks); on FltMgr being able to attach at multiple points in the I/O stack as frames in order to coexist with legacy filters; and on a minifilter returning to the same altitude within the same frame even after being unloaded and reloaded.  2 3 4 5

  3. Microsoft Learn, Advantages of the Filter Manager Model. On the advantages the minifilter model has over the legacy filter model, including better control over filter load order; on how, unlike legacy filters, minifilters can be loaded at any point in time; on the ability to unload; and on the inclusion of things such as connecting to DAX volumes.  2

  4. Microsoft Learn, Load order groups and altitudes for minifilter drivers. On purpose-specific load order groups being defined for file system filters, with each group assigned a range of altitudes; on every filter driver having a unique altitude identifier that determines its relative position against other filters in the I/O stack; and on examples of groups including FSFilter Activity Monitor (360000-389999, observing and reporting I/O), FSFilter Undelete (340000-349999), FSFilter Anti-Virus (320000-329999, detecting and removing viruses during file I/O), FSFilter Replication (300000-309999), and FSFilter Continuous Backup (280000-289999).  2 3

  5. Microsoft Learn, Allocated altitudes. On minifilter altitudes being allocated and managed by Microsoft, with a published list of allocated altitudes maintained; and on that list including WdFilter.sys at 328010 in the FSFilter Anti-Virus group and cldflt.sys at 409500 in the FSFilter Top group.  2 3

  6. Microsoft Learn, Configure and validate exclusions for Microsoft Defender Antivirus. On Microsoft Defender’s exclusion settings removing matching files, folders, and processes from scanning; and on the repeated caution that, because exclusions lower the level of protection, they should be defined carefully after assessing the need.  2 3

  7. Microsoft Learn, Set up a Dev Drive on Windows 11. On Dev Drive being a volume designed for development workloads, on which Microsoft Defender runs in performance mode (asynchronous scanning); on this being positioned, with speed and performance in mind, as a secure alternative to folder exclusions; on no additional filters being attached to a Dev Drive by default; and on the warning that running without an antivirus filter is a serious security risk.  2 3

  8. Microsoft Learn, Request a Filter Altitude Identifier. On applying for a new filter altitude by sending an ASCII text email with the subject “Filter altitude request” to fsfcomm@microsoft.com; on the need to fill in every item — company name, contact email (a long-lived company alias, not a personal one), product name, product URL, a description of the filter, the filter’s file name, filter type, start type, desired load order group, and desired altitude; on processing being expected to take 30 business days, with no channel for applying other than this procedure; on Microsoft sometimes assigning an altitude different from the one requested; and on companies that already hold an integer altitude being able to create their own altitude within the same load order group by appending a decimal, with a follow-up notification afterward being sufficient in that case.  2

  9. Microsoft Learn, Blocking legacy file system filter drivers. On running fltmc filters from an elevated command prompt listing filters in four columns — Filter Name, Num Instances, Altitude, and Frame; and on a Frame value of <Legacy> indicating a legacy file system filter driver that bypasses FltMgr, whereas a minifilter shows a numeric value (such as 0) in the Frame column.  2

  10. Microsoft Learn, Cloud Files API. On the Cloud Files API (cloud filter) being the foundation for sync engines, such as OneDrive’s Files On-Demand, that present cloud files locally as placeholders and fetch the real content on access.  2

  11. Microsoft Learn, Process Monitor - Sysinternals. On Process Monitor being an advanced monitoring tool that displays file system, registry, and process/thread activity in real time (as noted in the body text, you can observe it appearing as a minifilter in the fltmc listing while it runs).  2

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Frequently Asked Questions

Common questions about the topic of this article.

What's the difference between a file system filter driver and a minifilter?
Both are "drivers that intercept I/O requests to the file system", but they differ in which generation of interception method they use. The older legacy filter approach stacked its own device object directly onto the file system's device stack, which meant position was decided by load order and hard to guarantee, and there were problems such as not being able to unload safely once loaded. The current standard, the minifilter, registers callbacks with the Filter Manager (FltMgr) that ships with Windows, saying in effect "please call me before and after this operation". Position is decided deterministically by a number called the altitude, it can be loaded at any time, and a filter that implements an unload callback can even be removed while running. Nearly all modern filters — antivirus, encryption, monitoring tools, cloud sync — are implemented as minifilters.
Why can antivirus software inspect every file access?
Because the OS officially provides an extension point for exactly this purpose. A minifilter can register code with the Filter Manager to be called before (the pre callback) and after (the post callback) operations such as opening, reading, or writing a file. An antivirus filter sits in the altitude band reserved for anti-virus (320000-329999), and can, for example, scan the contents immediately after a file open succeeds (post-create) and, if there's a problem, revoke that open so the access fails. As we saw in Part 1 of this series, every file I/O operation flows down the device stack, so the logic is that standing at a fixed position along that path lets you inspect every access. It isn't a hack — it's a mechanism built into the design of the OS.
What does an antivirus exclusion setting (folder exclusion) actually do, technically?
For I/O on a path matching the exclusion list, it makes that product's filter skip the scanning it would otherwise perform. The filter itself does not disappear from the stack; the closer-to-accurate understanding is that the decision "don't inspect this path" gets made earlier. An important limitation is that an exclusion only works for the product that holds that setting. For example, a Microsoft Defender exclusion setting changes the scanning done by Defender's filter (WdFilter), and has no effect on the behaviour of other minifilters that sit alongside it — a competing antivirus product, EDR, backup software, and so on. Each product needs its own exclusion setting, and when "it's still slow even after I added an exclusion", a different filter can be the cause. And as Microsoft's documentation repeatedly warns, an exclusion weakens protection at that location, so it should be kept to a minimum together with a risk assessment. For development use, it is also worth considering Dev Drive (performance mode, i.e. asynchronous scanning), which is designed as a safe alternative to folder exclusions.
How does Process Monitor record every single I/O operation?
Because Procmon itself registers itself with the Filter Manager at startup, as a minifilter in the altitude band for Activity Monitor. If you run fltmc from an elevated command prompt while Procmon is running, you can confirm that a filter with a name starting PROCMON appears in the listing. Because it is present at the pre and post stages of I/O operations on every volume as a minifilter, it can record, without omission, which process performed which operation on which file. The reason the IRP and Fast I/O terminology we've followed throughout this series shows up directly in Procmon's display is precisely because it is observing from a position standing right on the path I/O itself takes.
When a development machine's builds are slow, should I suspect filter drivers?
It's well worth suspecting. A build is a mass of creating, reading, writing, and deleting a huge number of small files, and each one of those becomes a target for inspection by the filters (especially antivirus scanning), which makes it the workload where filter cost is most likely to surface. The basic investigation procedure is to check Procmon's Duration column to see which operation the time is going into, and to compare the difference in filter configuration between environments with fltmc instances. As countermeasures, alongside an exclusion setting adopted after assessing the risk, there's also using Dev Drive, which is designed specifically for development volumes. On a Dev Drive, antivirus runs in performance mode (asynchronous scanning), and Microsoft positions this as a safer alternative to an exclusion setting.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog