Thursday, January 12, 2012

Debugging Minifilters: using !fltkd.filter - Part III

In this post I finally get to look at context information about a filter and how to get to it. The !fltkd extension has split the information about contexts into information about which types of contexts the filter has registered and information about the contexts that the filter is using (they are either referenced by the filter or attached to an object). I'll try to follow the same scheme.
So first we'll look to see what information about the contexts we can get from the _FLT_FILTER structure:
0: kd> dt 94bb2da0 fltmgr!_FLT_FILTER
   +0x000 Base             : _FLT_OBJECT
   +0x014 Frame            : 0x94b58cc0 _FLTP_FRAME
   +0x018 Name             : _UNICODE_STRING "FileInfo"
   +0x020 DefaultAltitude  : _UNICODE_STRING "45000"
   +0x028 Flags            : 2 ( FLTFL_FILTERING_INITIATED )
   +0x02c DriverObject     : 0x9750f598 _DRIVER_OBJECT
   +0x030 InstanceList     : _FLT_RESOURCE_LIST_HEAD
   +0x074 VerifierExtension : 0x94bdcdd0 _FLT_VERIFIER_EXTENSION
   +0x078 VerifiedFiltersLink : _LIST_ENTRY [ 0x926904bc - 0xa0c92b38 ]
   +0x080 FilterUnload     : 0x926b8b6a     long  +ffffffff926b8b6a
   +0x084 InstanceSetup    : (null) 
   +0x088 InstanceQueryTeardown : (null) 
   +0x08c InstanceTeardownStart : (null) 
   +0x090 InstanceTeardownComplete : (null) 
   +0x094 SupportedContextsListHead : 0x94bbee70 _ALLOCATE_CONTEXT_HEADER
   +0x098 SupportedContexts : [6] (null) 
   +0x0b0 PreVolumeMount   : (null) 
   +0x0b4 PostVolumeMount  : (null) 
   +0x0b8 GenerateFileName : (null) 
   +0x0bc NormalizeNameComponent : (null) 
   +0x0c0 NormalizeNameComponentEx : (null) 
   +0x0c4 NormalizeContextCleanup : (null) 
   +0x0c8 KtmNotification  : (null) 
   +0x0cc Operations       : 0x94bb2efc _FLT_OPERATION_REGISTRATION
   +0x0d0 OldDriverUnload  : (null) 
   +0x0d4 ActiveOpens      : _FLT_MUTEX_LIST_HEAD
   +0x100 ConnectionList   : _FLT_MUTEX_LIST_HEAD
   +0x12c PortList         : _FLT_MUTEX_LIST_HEAD
   +0x158 PortLock         : _EX_PUSH_LOCK
0: kd> dp 94bb2da0+0x98 L6
94bb2e38  00000000 94bbee70 00000000 94bbef38
94bb2e48  00000000 00000000
As we can see there are two members that seem to be related to contexts, "SupportedContextsListHead" and "SupportedContexts". Interestingly enough SupportedContexts is an array of fixed size, embedded into the _FLT_FILTER structure. This is the same number of contexts that FltMgr supports in Win7. According to the documentation there seems to be a new context type coming in Win8 (FLT_SECTION_CONTEXT). Anyway, each entry in the array is of type _ALLOCATE_CONTEXT_HEADER, just like the "SupportedContextsListHead" variable. However, the _ALLOCATE_CONTEXT_HEADER doesn't start with a LIST_ENTRY (though it does contain one) so I'm not sure what SupportedContextsListHead actually does (since it's clearly not a list head). Anyway, let's ignore it for now and look at the entries in the array:
0: kd> dt 0x94bbee70 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0x94bb2da0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0x926b7880     void  fileinfo!FIVolumeCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 2
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
0: kd> dt 94bbef38 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0x94bb2da0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0x926b7cc4     void  fileinfo!FIStreamCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 8
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
The one odd bit here is the fact that the context type for the second entry in the array is 8, but we know there are only 6 context types.. Looking at the position of the entries in the array we can conclude that these are bit fields and they most likely map to the values in the FLT_CONTEXT_TYPE define. This also helps us identify the members of the array (first entry is for Volume contexts, the second for Instance contexts and so on). This is still a bit confusing since it would seems that the first entry is for an instance context but it's callback is named fileinfo!FIVolumeCleanup. Most likely what's happened is that FileInfo was using volume contexts and then they switched to using instance contexts (most likely for performance reasons) but didn't change the function name. A quick look at luafv shows it uses the same context type but the callback this time indicates it's an instance context:
0: kd> dt a0c72e70 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0xa0c92ac0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0xa29b8a1f     void  luafv!LuafvInstanceCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 2
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
Another thing worth mentioning is that when allocating contexts of fixed size FltMgr uses a lookaside list. As such we expect that there is a lookaside list related to this context somewhere, and since the name of the structure is "_ALLOCATE_CONTEXT_HEADER" it's easy to guess that the data containing the lookaside list is right after the header. This is especially obvious considering that the memory that was allocated for one context is 0xc8 bytes, much larger than the 0x10 bytes that the _ALLOCATE_CONTEXT_HEADER takes:
0: kd> !pool 0x94bbee70 
Pool page 94bbee70 region is Special pool
*94bbe000 size:  190 data: 94bbee70 (NonPaged) *FMcr  <- we have 0x190 bytes
  Pooltag FMcr : Context registration structures, Binary : fltmgr.sys
0: kd> ? 0x94bbef38 - 0x94bbee70    
Evaluate expression: 200 = 000000c8 <- the second context starts at 0xc8 after the first
0: kd> ? 0xc8 * 2
Evaluate expression: 400 = 00000190 <- 0xc8 * 2 (for two contexts) is exactly 0x190 , the size of our block.. 
We could spend some time to try to reverse-engineer the meaning of the AllocationType and Flags members but it'd take too long.. So let's just see how we can get the contexts that are used by the filter. FltMgr keeps track of all the context it has allocated because it needs to be able to free all of them when the instance is detached, but they are stored inside the actual FLT_INSTANCE structure and while that's an interesting topic in itself, I'd rather not go into it in this post. However, information about all the contexts used by a filter is available when using Driver Verifier. As you might remember from the previous post, the _FLT_VERIFIER_EXTENSION structure has a tree of all the contexts. Unfortunately, the tree for the allocated objects is filled with other stuff as well so it's hard to find just the contexts (file name cache entries are also stored in the verifier tree and when a filter is using FltGetFileNameInformation() most of the verifier entries will be name cache entries (at least that has been my experience so far)).