Thursday, February 24, 2011

Tracking a minifilter's ActiveOpens files

I've recently done a bit of detective work that I thought might be an interesting thing to share, especially since we've been talking about contexts so much lately. The issue was how to find the files that were opened by a minifilter which prevent that minifilter from unloading. FltMgr actually keeps track of the files that were opened by a minifilter and if the minifilter gets unloaded it will wait for those files to be closed before it unloads the driver. You can see this in the debugger here:

1: kd> !fltkd.filter 94130008 

FLT_FILTER: 94130008 "luafv" "135000"
   FLT_OBJECT: 94130008  [02000000] Filter
      RundownRef               : 0x0000000a (5)
      PointerCount             : 0x00000001 
      PrimaryLink              : [922c75a4-92cdaa24] 
   Frame                    : 92cda9c8 "Frame 0" 
   Flags                    : [00000006] FilteringInitiated NameProvider
   DriverObject             : 9306bc50 
   FilterLink               : [922c75a4-92cdaa24] 
   PreVolumeMount           : 81fbe0cc  luafv!LuafvPreRedirect 
   PostVolumeMount          : 00000000  (null) 
   FilterUnload             : 00000000  (null) 
   InstanceSetup            : 81fca62b  luafv!LuafvInstanceSetup 
   InstanceQueryTeardown    : 00000000  (null) 
   InstanceTeardownStart    : 00000000  (null) 
   InstanceTeardownComplete : 00000000  (null) 
   ActiveOpens              : (941300dc)  mCount=1 
   Communication Port List  : (94130108)  mCount=0 
   Client Port List         : (94130134)  mCount=0 
   VerifierExtension        : 00000000 

So the task is to find that one file that ActiveOpens is tracking for LUAFV. Before we go any further, let's see what the chain of structures looks like, starting from the FILE_OBJECT. Some of the structures are documented while others are not. I've marked the undocumented structures with a '?' at the end of the name. We don't know (or care for this post) what the other members of the structures are.

Now, we need to walk the arrows backwards so the steps we need to follow are:

  1. From fltmgr!_FLT_FILTER->ActiveOpens find fltmgr!FO_CONTEXT?
  2. From fltmgr!FO_CONTEXT? find the nt!FILE_OBJECT_CONTEXTS_HEADER? Structure
  3. From nt!FILE_OBJECT_CONTEXTS_HEADER? Structure find the nt!_IOP_FILE_OBJECT_EXTENSION pointing to it
  4. From the nt!_IOP_FILE_OBJECT_EXTENSION find the nt!_FILE_OBJECT structure that points to it
  5. ???
  6. Profit!!!!

Starting with ActiveOpens, let's look at the structure. Please note that mCount appears to be shifted by 1. Also, please note that mList is a regular doubly linked list and we expect that it contains one entry (since mCount is 1):

1: kd> dt 941300dc _FLT_MUTEX_LIST_HEAD
fltmgr!_FLT_MUTEX_LIST_HEAD
   +0x000 mLock            : _FAST_MUTEX
   +0x020 mList            : _LIST_ENTRY [ 0x93712498 - 0x93712498 ]
   +0x028 mCount           : 2
   +0x028 mInvalid         : 0y0
1: kd> dl 0x93712498 
93712498  941300fc 941300fc 0421000e 706e5043
941300fc  93712498 93712498 00000002 00000001
1: kd> !pool 0x93712498 2
Pool page 93712498 region is Nonpaged pool
*93712430 size:   70 previous size:    8  (Allocated) *FMfc
  Pooltag FMfc : FLTMGR_FILE_OBJECT_CONTEXT structure, Binary : fltmgr.sys

So now we know that the structure that we've been calling fltmgr!FO_CONTEXT? is in fact called FLTMGR_FILE_OBJECT_CONTEXT. Step 1 is done and we're moving on to step 2. We also know the size isn't larger than 0x70. However, we don't know where the LIST_ENTRY is in there. The only other thing we know is that in that structure there must be a member that is of type FSRTL_PER_FILEOBJECT_CONTEXT (and we know this because that's how FILE_OBJECT contexts are implemented; see the documentation for FsRtlInsertPerFileObjectContext). Since the beginning of the _LIST_ENTRY is at address 0x93712498 and the pool block starts at 0x93712430, we can guess our LIST_ENTRY is towards the end of the structure, so we'll go back a bit and display the words. Then we'll look for something that looks like an FSRTL_PER_FILEOBJECT_CONTEXT. FSRTL_PER_FILEOBJECT_CONTEXT doesn't really contain much easily identifiable information but it does contain a LIST_ENTRY, which means we should find two words that look like kernel mode addresses next to each other. I've highlighted possible candidates and then we simply try "dl" on them (if anyone knows a better way I'd love to hear about it, please leave a comment). Of course, if you look at the number carefully you can see that it's very likely that there is a LIST_ENTRY at 93712480 if the list has only one element. But in most cases the list has more than one element so it's hard to tell at a quick glance. So we simply issue a "dl" on each candidate and hope that if they're not doubly linked lists they'll simply run into some invalid address sooner or later.

1: kd> dp 0x93712498-0x60 
93712438  48706345 00000000 00000000 00000000
93712448  00ac5851 45e4702b 2a5794ac 7e7ac1fe
93712458  00000000 00000047 00000068 96040c00
93712468  00000000 0034f110 001d0000 92cda9c8
93712478  94134ce8 93011008 9306b0c8 9306b0c8
93712488  938f1230 00000000 94130008 941307d8
93712498  941300fc 941300fc 0421000e 706e5043
937124a8  937395a8 937126b8 937699d8 93739ad8
1: kd> dl 92cda9c8
92cda9c8  0340f103 960409f8 960409f8 00000000
0340f103  00000000 00000000 00000000 00000000
1: kd> dl 94134ce8 
94134ce8  00800005 92f05788 92f06870 92fa4998
…
1: kd> dl 9306b0c8  
9306b0c8  93712480 93712480 00010006 e56c6946
93712480  9306b0c8 9306b0c8 938f1230 00000000
1: kd> !pool 9306b0c8 2
Pool page 9306b0c8 region is Nonpaged pool
*9306b0a0 size:   30 previous size:   10  (Allocated) *FOCX
  Pooltag FOCX : File System Run Time File Object Context structure, Binary : nt!fsrtl

It looks like we may have found our structure. Now we're starting our step 3 and we have a pointer into a structure of type nt!FILE_OBJECT_CONTEXTS_HEADER?, but since we don't know the type we don't know where the structure starts. Normally what I do is assume it starts right after the pool tag and search for that address, and if that fails search for the address at the next word boundary and so on. Let's see:

1: kd> db 9306b0a0 L0x20
9306b0a0  02 00 06 04 46 4f 43 58-01 00 00 00 00 00 00 00  ....FOCX........
9306b0b0  00 00 00 00 01 00 04 20-00 00 00 00 bc b0 06 93  ....... ........
1: kd> s -d 80000000 L?0x20000000 9306b0a8
9306ba78  9306b0a8 00000000 00000000 00000000  ................
1: kd> !pool 9306ba78  2
Pool page 9306ba78 region is Nonpaged pool
*9306ba60 size:   30 previous size:   10  (Allocated) *Io  
  Pooltag Io   : general IO allocations, Binary : nt!io

So actually this looks pretty good. In other cases I've found multiple random values that looked like references so I've just had to look at each one. But in this case it looks pretty clean. We expect that the pointer is in a structure that's allocated by the IO mgr, and that the structure size something about the size of nt!_IOP_FILE_OBJECT_EXTENSION. So this means we've completed step 3 and we have the nt!_IOP_FILE_OBJECT_EXTENSION structure. In our picture I've shown that in the _IOP_FILE_OBJECT_EXTENSION it is FoExtPerTypeExtension[3] that points to this structure that we just found. I've figured this out by experimenting. I simply got a FILE_OBJECT and added a context by calling FsRtlInsertPerFileObjectContext and watched which value changed. So now we know where the structure starts and we need to search a pointer to it. The pointer we would find here would be a FILE_OBJECT->FileObjectExtension so we expect the pool tag to be "File". Also, we expect the FILE_OBJECT to start 0x7c bytes before the pointer. Please note that the first two results returned by the search were invalid (!pool told me so):

1: kd> ? 0x9306ba78-0x10
Evaluate expression: -1828275608 = 9306ba68
1: kd> s -d 80000000 L?0x20000000 9306ba68
82b80008  9306ba68 9306ba68 00000000 abcddcba  h...h...........
82b8000c  9306ba68 00000000 abcddcba 00000001  h...............
94134d64  9306ba68 04530015 6661754c 00000000  h.....S.Luaf....
1: kd> !pool 94134d64  2
Pool page 94134d64 region is Nonpaged pool
*94134cc0 size:   a8 previous size:  2d8  (Allocated) *File (Protected)
  Pooltag File : File objects
1: kd> !fileobj 0x94134d64-0x7c  



Device Object: 0x92f05788   \Driver\volmgr
Vpb: 0x92f06870
Event signalled
Access: Read Write SharedRead SharedWrite SharedDelete 

Flags:  0x440008
 No Intermediate Buffering
 Handle Created
 Volume Open

FsContext: 0x92fa4998 FsContext2: 0x97e0cbc0
CurrentByteOffset: 0
Cache Data:
  Section Object Pointers: 92f0cd14
  Shared Cache Map: 00000000


File object extension is at 9306ba68:

So this is it, we know now that LUAFV opens the volume using FltCreateFile (if it didn't it would be on the ActiveOpens list). For the record, this is win7:

1: kd> vertarget
Windows 7 Kernel Version 7600 MP (2 procs) Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 7600.16617.x86fre.win7_gdr.100618-1621
Machine Name:
Kernel base = 0x82809000 PsLoadedModuleList = 0x82951810
Debug session time: Thu Feb 17 07:44:10.641 2011 (UTC - 8:00)
System Uptime: 0 days 0:02:42.733