Thursday, December 8, 2011

Debugging Minifilters: Finding the Callbacks

When debugging interop issues I often need to be able to set breakpoints on a certain filter's dispatch function for a certain operation. In most cases the other filter is written by a 3rd party and I have no symbols for it at all and so it's not easy to look at function names and guess what the appropriate function to set the breakpoint on is. So in this post I would like to show how to find the appropriate routines for each operation for both legacy filters and minifilters. Because i'm using Microsoft filters for which symbols are available you can see all the function names, but of course the same principle applies even when you don't have symbols just that the names won't look so pretty.
When dealing with legacy filters the approach to get the callbacks is pretty well-known and it relies on looking at the fields in the DRIVER_OBJECT:
0: kd> !drvobj sr
Driver object (82332290) is for:
Driver Extension List: (id , addr)

Device Object list:
8207d830  8213add0  8235e020  8238e248
0: kd> dt 82332290 nt!_DRIVER_OBJECT
   +0x000 Type             : 0n4
   +0x002 Size             : 0n168
   +0x004 DeviceObject     : 0x8207d830 _DEVICE_OBJECT
   +0x008 Flags            : 0x12
   +0x00c DriverStart      : 0xf8489000 Void
   +0x010 DriverSize       : 0x11f00
   +0x014 DriverSection    : 0x823eb998 Void
   +0x018 DriverExtension  : 0x82332338 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING "\FileSystem\sr"
   +0x028 FastIoDispatch   : 0xf848b4c0 _FAST_IO_DISPATCH
   +0x02c DriverInit       : 0xf8498fd4     long  sr!GsDriverEntry+0
   +0x030 DriverStartIo    : (null) 
   +0x034 DriverUnload     : (null) 
   +0x038 MajorFunction    : [28] 0xf848e726     long  sr!SrCreate+0
0: kd> dps 82332290+0x38 L0n28
823322c8  f848e726 sr!SrCreate
823322cc  f8489428 sr!SrPassThrough
823322d0  f8489428 sr!SrPassThrough
823322d4  f8489428 sr!SrPassThrough
823322d8  f8489320 sr!SrWrite
82332330  f8489428 sr!SrPassThrough
82332334  f848dd52 sr!SrPnp
Of course, legacy filters also have a couple of other sets of entrypoints, the FastIO routines and the FS_FILTER_CALLBACKS, which we might need to set breakpoints on:
0: kd> dt 0xf848b4c0 _FAST_IO_DISPATCH
   +0x000 SizeOfFastIoDispatch : 0x70
   +0x004 FastIoCheckIfPossible : 0xf8490914     unsigned char  sr!SrFastIoCheckIfPossible+0
   +0x008 FastIoRead       : 0xf8490962     unsigned char  sr!SrFastIoRead+0
   +0x00c FastIoWrite      : 0xf84909b0     unsigned char  sr!SrFastIoWrite+0
   +0x060 FastIoQueryOpen  : 0xf8490f30     unsigned char  sr!SrFastIoQueryOpen+0
   +0x064 ReleaseForModWrite : (null) 
   +0x068 AcquireForCcFlush : (null) 
   +0x06c ReleaseForCcFlush : (null) 

0: kd> dt 0x82332338 nt!_DRIVER_EXTENSION
   +0x000 DriverObject     : 0x82332290 _DRIVER_OBJECT
   +0x004 AddDevice        : (null) 
   +0x008 Count            : 0
   +0x00c ServiceKeyName   : _UNICODE_STRING "sr"
   +0x014 ClientDriverExtension : (null) 
   +0x018 FsFilterCallbacks : 0x8236d238 _FS_FILTER_CALLBACKS
0: kd> dt 0x8236d238 _FS_FILTER_CALLBACKS
   +0x000 SizeOfFsFilterCallbacks : 0x38
   +0x004 Reserved         : 0
   +0x008 PreAcquireForSectionSynchronization : 0xf8490c30     long  sr!SrPreAcquireForSectionSynchronization+0
   +0x00c PostAcquireForSectionSynchronization : (null) 
   +0x030 PreReleaseForModifiedPageWriter : (null) 
   +0x034 PostReleaseForModifiedPageWriter : (null) 
Things are similar for minifilters. There is a !fltkd command that tries to make things easier, but in my oppinion it could do a better job at helping out. For one, even though the callbacks are defined on a per-filter basis, the !fltkd.filter command that lists the contents of the filter structure doesn't actually show the registered callbacks, and instead one must use the the !fltkd.instance command on one of the instances of the filter , command that has a special flag that can be used to see the callbacks:
1: kd> !fltkd.filters

Filter List: 92cddd1c "Frame 0" 
   FLT_FILTER: 94144008 "luafv" "135000"
      FLT_INSTANCE: 94146008 "luafv" "135000"
   FLT_FILTER: 92cdda58 "FileInfo" "45000"
      FLT_INSTANCE: 930e7730 "FileInfo" "45000"
      FLT_INSTANCE: 93448dc8 "FileInfo" "45000"
      FLT_INSTANCE: 9357c340 "FileInfo" "45000"
      FLT_INSTANCE: 93584560 "FileInfo" "45000"
      FLT_INSTANCE: 9359a948 "FileInfo" "45000"
1: kd> !fltkd.filter 94144008 

FLT_FILTER: 94144008 "luafv" "135000"
   FLT_OBJECT: 94144008  [02000000] Filter
      RundownRef               : 0x00000008 (4)
      PointerCount             : 0x00000001 
      PrimaryLink              : [92cdda64-92cddd1c] 
   Frame                    : 92cddcc0 "Frame 0" 
   Flags                    : [00000006] FilteringInitiated NameProvider
   DriverObject             : 941427a8 
   FilterLink               : [92cdda64-92cddd1c] 
   PreVolumeMount           : 81f980cc  luafv!LuafvPreRedirect 
   PostVolumeMount          : 00000000  (null) 
   FilterUnload             : 00000000  (null) 
   InstanceSetup            : 81fa462b  luafv!LuafvInstanceSetup 
   InstanceQueryTeardown    : 00000000  (null) 
   InstanceTeardownStart    : 00000000  (null) 
   InstanceTeardownComplete : 00000000  (null) 
   ActiveOpens              : (941440dc)  mCount=1 
   Communication Port List  : (94144108)  mCount=0 
   Client Port List         : (94144134)  mCount=0 
   VerifierExtension        : 00000000 
   Operations               : 94144164 
   OldDriverUnload          : 00000000  (null) 
   SupportedContexts        : (941440a0)
      VolumeContexts           : (941440a0)
      InstanceContexts         : (941440a4)
         ALLOCATE_CONTEXT_NODE: 94144a20 "luafv" [01] LookasideList (size=608)
      FileContexts             : (941440a8)
      StreamContexts           : (941440ac)
      StreamHandleContexts     : (941440b0)
         ALLOCATE_CONTEXT_NODE: 94144ae8 "luafv" [01] LookasideList (size=20)
      TransactionContext       : (941440b4)
   InstanceList             : (94144038)
      FLT_INSTANCE: 94146008 "luafv" "135000"
1: kd> !fltkd.instance 94146008 4

FLT_INSTANCE: 94146008 "luafv" "135000"
   CallbackNodes            : (94146054)
         CALLBACK_NODE: 94146584  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
         CALLBACK_NODE: 9414656c  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
         CALLBACK_NODE: 9414611c  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
       PREPARE_MDL_WRITE (-17)
         CALLBACK_NODE: 941462b4  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
       CREATE (0)
         CALLBACK_NODE: 941462cc  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
         CALLBACK_NODE: 941462e4  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
       SET_QUOTA (26)
         CALLBACK_NODE: 9414653c  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
       PNP (27)
         CALLBACK_NODE: 94146554  Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
However, the above mentioned debugger function returns pointers to a CALLBACK_NODE structure from which one can figure out what the actual function is. Please note that the structure contains unions and so a lot of the members actually point to the same function. It generally is pretty clear which member should be used depending on the function for which we got the CALLBACK_NODE. For this example I took the IRP_MJ_CREATE callback and the NORMALIZE_NAME_COMPONENT callback:
1: kd> dt 941462cc  fltmgr!_CALLBACK_NODE
   +0x000 CallbackLinks    : _LIST_ENTRY [ 0x93448edc - 0x9343757c ]
   +0x008 Instance         : 0x94146008 _FLT_INSTANCE
   +0x00c PreOperation     : 0x81f9f263     _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreCreate+0
   +0x010 PostOperation    : 0x81fa24e8     _FLT_POSTOP_CALLBACK_STATUS  luafv!LuafvPostCreate+0
   +0x00c GenerateFileName : 0x81f9f263     long  luafv!LuafvPreCreate+0
   +0x00c NormalizeNameComponent : 0x81f9f263     long  luafv!LuafvPreCreate+0
   +0x00c NormalizeNameComponentEx : 0x81f9f263     long  luafv!LuafvPreCreate+0
   +0x010 NormalizeContextCleanup : 0x81fa24e8     void  luafv!LuafvPostCreate+0
   +0x014 Flags            : 0 (No matching name)
1: kd> dt 94146584  fltmgr!_CALLBACK_NODE
   +0x000 CallbackLinks    : _LIST_ENTRY [ 0x934374cc - 0x934374cc ]
   +0x008 Instance         : 0x94146008 _FLT_INSTANCE
   +0x00c PreOperation     : 0x81fa09b2     _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvNormalizeNameComponentEx+0
   +0x010 PostOperation    : (null) 
   +0x00c GenerateFileName : 0x81fa09b2     long  luafv!LuafvNormalizeNameComponentEx+0
   +0x00c NormalizeNameComponent : 0x81fa09b2     long  luafv!LuafvNormalizeNameComponentEx+0
   +0x00c NormalizeNameComponentEx : 0x81fa09b2     long  luafv!LuafvNormalizeNameComponentEx+0
   +0x010 NormalizeContextCleanup : (null) 
   +0x014 Flags            : 4 ( CBNFL_USE_NAME_CALLBACK_EX )
Personally I found this approach somewhat cumbersome to use and so what I've done is to list the array of callback directly from the filter object. Please note that the array ends with a record for the IRP_MJ_OPERATION_END pseudo-operation (which is just an array terminator) which has the value of 0x80. This is actually the array of FLT_OPERATION_REGISTRATION structures that is passed to FltRegisterFilter() function in the FLT_REGISTRATION.OperationRegistration field. As such this array does not include the name generation callbacks.
: kd> dt -oca60 94144164 _FLT_OPERATION_REGISTRATION
[0] @ 94144164 MajorFunction 0xec ''  Flags 0  PreOperation 0x81f980cc  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreRedirect+0  PostOperation (null)   Reserved1 (null)   
[1] @ 94144178 MajorFunction 0xed ''  Flags 0  PreOperation 0x81f980cc  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreRedirect+0  PostOperation (null)   Reserved1 (null)   
[2] @ 9414418c MajorFunction 0xee ''  Flags 0  PreOperation 0x81f98005  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreWrite+0  PostOperation (null)   Reserved1 (null)   
[3] @ 941441a0 MajorFunction 0xef ''  Flags 0  PreOperation 0x81f980cc  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreRedirect+0  PostOperation (null)   Reserved1 (null)   
[45] @ 941444e8 MajorFunction 0x19 ''  Flags 0  PreOperation 0x81f980cc  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreRedirect+0  PostOperation (null)   Reserved1 (null)   
[46] @ 941444fc MajorFunction 0x1a ''  Flags 0  PreOperation 0x81f980cc  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPreRedirect+0  PostOperation (null)   Reserved1 (null)   
[47] @ 94144510 MajorFunction 0x1b ''  Flags 0  PreOperation 0x81fa3330  _FLT_PREOP_CALLBACK_STATUS  luafv!LuafvPrePnp+0  PostOperation (null)   Reserved1 (null)   
[48] @ 94144524 MajorFunction 0x80 ''  Flags 0  PreOperation (null)   PostOperation (null)   Reserved1 (null)