How to tell if a file has had a write operation in the pre-cleanup callback routine

Hi, i’m new to minifilter development. My minifilter intercepts both IRP_MJ_WRITE and IRP_MJ_CLEANUP and I need to filter for files that had been written to, in my pre-cleanup callback routine.

In my post-write callback, i call FltAllocateContext and FltSetStreamHandleContext to allocate and set a stream handle context which contains a ULONG variable to store the length of transfer in a write operation using Data->Iopb->Parameters.Write.Length.

Subsequently, in the pre-cleanup callback, i call FltGetStreamHandleContext to get a context if there was one allocated earlier. If the call is successful, i check that the ULONG variable is > 0 and if so, i deduce that the file had been written to.

May i ask if i am approaching this right? Thank you.

Hi,

You won’t know in pre-cleanup if there was a right, with certainty.
Sure, most access will show up, but some won’t.
By post-close you can know that though. That might happens an hour later
though!

What you want requires heuristics that is specific to your scenario.

Regards, Dejan.

Hi, may i ask why some write operations won’t show up in pre-cleanup and only show up in post-close using the context approach?


Currently, my scenario is to send files for scanning when the last user-mode applications using the files have closed their last handles to the file objects (hence pre-cleanup), but i am only interested in sending files that had write operations performed on them by user-mode applications. Thus, i am using the context approach to help me identify files that had been written to and to send for scanning when apps finished using them .



Would this approach be enough for my scenario? Thanks.

Memory mapped access will not show this way often enough. Writes are seen
after Cleanup only.

Dejan.

@user3211 said:
Hi, i’m new to minifilter development. My minifilter intercepts both IRP_MJ_WRITE and IRP_MJ_CLEANUP and I need to filter for files that had been written to, in my pre-cleanup callback routine.

In my post-write callback, i call FltAllocateContext and FltSetStreamHandleContext to allocate and set a stream handle context which contains a ULONG variable to store the length of transfer in a write operation using Data->Iopb->Parameters.Write.Length.

You can’t do these things in PostWrite because you might be at DISPATCH_LEVEL. Better to do it in PreWrite where you know you’re at IRQL <= APC_LEVEL…Sure, the write could fail, but that doesn’t change the fact that the user intended to write (and failure is rare).

Dejan is correct that this doesn’t cover the case of memory mapped write. In that case the application closes their file HANDLE (* 1) and writes to the file mapping. This updates the file data copy in RAM (* 2), then the Memory Manager eventually/maybe gets around to flushing the data out as paging IRP_MJ_WRITE requests from the System process. So, your heuristic of catching the modifications at PreCleanup doesn’t work. Things get worse if you have more than one application memory mapping for write as you have no idea who is actually modifying the data, just that it was modified by someone at some point.

Two things you can do to mitigate this;

  1. You see the memory mapping happen (IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION) and that MUST happen before the HANDLE is closed. This is your hint that your heuristic doesn’t apply.
  2. While memory map for READ is common, memory map for WRITE is unusual. I cover it a lot in the minifilter seminar and then once everyone is sufficiently terrified I usually advise students to a) make sure they understand the case and b) have code to detect and (importantly) log it. But don’t make yourself insane if checking for non-paging writes in PreWrite along with a check in PreCleanup is usually enough for the common use cases (drag/drop, document save, etc.)

If you’re still not quite sure I highly recommend playing with FileTest and ProcMon. FileTest lets you perform whatever series of file operations you want and then see the results in ProcMon:

http://www.zezula.net/en/fstools/filetest.html
https://learn.microsoft.com/en-us/sysinternals/downloads/procmon

-scott

*1 - The user application can and usually does close the file HANDLE, but it’s not a requirement in the API
*2 - DAX (aka Persistent Memory) screws this up even further…In that case there are no IRP_MJ_WRITE requests AT ALL when dealing with memory mapped for write. It’s really cool stuff, still niche and expensive at the moment though prices have come way down
https://learn.microsoft.com/en-us/windows-server/storage/storage-spaces/persistent-memory-direct-access
https://buy.hpe.com/us/en/options/enterprise-memory/persistent-memory/persistent-memory/intel-optane-512gb-persistent-memory-100-series-for-hpe/p/835810-b21

Just to add: BypassIo can also bypass write filters completely, but this
you can detect and veto.

1 Like

@Dejan_Maksimovic said:
Just to add: BypassIo can also bypass write filters completely, but this
you can detect and veto.

Also read only for now (whether or not they actually enable this in the write path is TBD…place your bets!)

https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/bypassio