If you’re a kernel developer and haven’t been living under a rock you might know by now that starting windows vNext Microsoft plans to block legacy filesystem filters from being loaded or attached to file system stacks. You can read more about it here.
Before I go any further with my blog post I want to say from the start that I am not in any way an advocate for legacy filters. I also believe that if you want to write a FSF just go with the minifilter for so many reasons, which I am not going to talk about here.
I wanted to write this post because another post on the OSR NTFSD forum. The Microsoft Team, would announce that they plan to block legacy filter starting with Windows vNext. I wrote a few posts there myself again not doubting the usefulness of a minifilter but rather the method Microsoft has chosen to block the old style legacy filters. I mean in the end they are just WDM drivers that can chose to call IoAttachDeviceToDeviceStack and just to be part of the action, so this was interesting for me since, blocking legacy filters, let’s be honest it’s not real news, we’ve been talking about it for something like 10 years now.
So what I did I went ahead and downloaded the insider preview ISO from here, and started to do a bit of investigating on how the new “feature” is being currently implemented.
After I have installed the build, I have navigated to “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\I/O System” and created a REG_DWORD value called IoBlockLegacyFsFilters, set it’s value to 1 and rebooted the system with a debugger attached.
During boot I also put a breakpoint in “IoRegsiterFsRegistrationChange”.
Everything seemed to be as before apart from one little detail, and that is this new variable nt!IopBlockLegacyFsFilters. This is read from the registry by the kernel early during boot and it is it’s way of knowing if it should block or not legacy filter. A value of 1 means yes, 0 means legacy filters are still allowed.
As you might have already guessed this is used in 2 places, one of the is IoRegisterFsRegistrationChange and the other is IoAttachDeviceToDeviceStackSafe.
So I set a breakpoint at first in IoRegisterFsRegistrationChange and wanted to check further. As expected the filter manager (FltMgr) wanted to setup his first frame and was the one calling this routine. Right from the start you can see it checks first the nt!IopBlockLegacyFsFilters variable.
cmp dword ptr [nt!IopBlockLegacyFsFilters (fffff802`083a1ba8)],1
The next thing it does, it points RCX to the UNICODE_STRING of the DRIVER_OBJECT that called into the routine and then calls another new routine and funny named in my opinion: IopIsKnownGoodLegacyFsFilter.
fffff802`0863d910 4883c138 add rcx,38h ; RCX is the driver object pointer, and offset 0x38 is the DriverName unicode string, now RCX becomes the first parameter for the call below
fffff802`0863d914 e8df07c0ff call nt!IopIsKnownGoodLegacyFsFilter (fffff802`0823e0f8)
fffff802`0863d919 84c0 test al,al ;i am assuming this is a boolean value returned
IopIsKnownGoodLegacyFsFilter apparently take as a parameter a UNICODE_STRING pointer and return a BOOLEAN.
But still, what does it really do ?
From the looks of it, it prepares a call to memcmp of the Buffer in the UNICODE_STRING passed as a parameter and some “well known” hard coded values.
fffff802`0823e114 488d3d15440a00 lea rdi,[nt!`string’+0x30 (fffff802`082e2530)] ; RDI points to the buffer pointer of the allowed UNICODE_STRING values, these are hard-coded values
fffff802`0823e11b 33f6 xor esi,esi
fffff802`0823e11d 4c8bf1 mov r14,rcx ;r14 is points to the UNICODE_STRING
fffff802`0823e120 8bde mov ebx,esi
fffff802`0823e122 0fb747f8 movzx eax,word ptr [rdi-8] ; eax points to the length of the first buffer of the allowed filter names
So let’s check what we have in RDI:
3: kd> dq @rdi l5
fffff802`082e2530 fffff802`082e24b0 00000000`00220020 ; so RDI has the pointers to the buffers of the UNICODE_STRINGs of the allowed filters
fffff802`082e2540 fffff802`082e24d8 00000000`00220020
And now let’s check all individual buffers and what they contain:
3: kd> du fffff802`082e24b0
3: kd> du fffff802`082e24d8
3: kd> du fffff802`082e2500
Hmm interesting, and expected if you ask me. So if your name ( as a driver ) is one of the above you are allowed to load/attach to a filesystem stack.
The pointers from above are the “Buffer” field in a unicode string, as seen from the assembly from above, movzx eax,word ptr [rdi-8], here it gets the unicode string length. 8 is the offset to the buffer and the UNICODE_STRING structure starts with the string Length.
+0x000 Length : Uint2B
+0x002 MaximumLength : Uint2B
+0x008 Buffer : Ptr64 Wchar
So we could also do this:
3: kd> dt _UNICODE_STRING fffff802082e2530-8 ;fffff802082e2530 is the address to where RDI points at first after it is read
+0x000 Length : 0x24
+0x002 MaximumLength : 0x26
+0x008 Buffer : 0xfffff802`082e24b0 “\FileSystem\FltMgr”
The next allowed filter is here:
3: kd> dt _UNICODE_STRING fffff802082e2530-0x8+0x10 ; just add another unicode strig’s size, They appear to be in a vector of unicode strings
+0x000 Length : 0x20
+0x002 MaximumLength : 0x22
+0x008 Buffer : 0xfffff802`082e24d8 “\FileSystem\Msfs”
And the last one:
3: kd> dt _UNICODE_STRING fffff802082e2530-0x8+0x10+0x10
+0x000 Length : 0x20
+0x002 MaximumLength : 0x22
+0x008 Buffer : 0xfffff802`082e2500 “\FileSystem\Npfs”
So bottom line if your driver object’s name is one of the above then, IopIsKnownGoodLegacyFsFilter will return TRUE, which in return routines like: IoRegsiterFsRegistrationChange or IoAttachDeviceToDeviceStack will allow drivers to attach to file system stacks.
I am still assuming this is the beta implementation, but it could very well remain the same in the actual release build. I know Microsoft has done this before with another class of devices, where only verifier could directly attach to the stack, so could just rename your driver to “verifier” during the attach call.
Bottom line is that even with this simple implementation what Microsoft hopes to achieve is discourage future developers to go with the legacy model, which I also encourage. Should the implementation change then the enforcement will be event stronger I assume. We will just have to wait and see.
What do you think about this ?