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:
\FileSystem\sr
Driver Extension List: (id , addr)
Device Object list:
8207d830 8213add0 8235e020 8238e248
8238e030
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"
+0x024 HardwareDatabase : 0x8067d260 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
+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
nt!_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
nt!_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)
NORMALIZE_NAME_COMPONENT (-22)
CALLBACK_NODE: 94146584 Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
GENERATE_FILE_NAME (-21)
CALLBACK_NODE: 9414656c Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
MDL_WRITE_COMPLETE (-18)
CALLBACK_NODE: 9414611c Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
PREPARE_MDL_WRITE (-17)
...
ACQUIRE_FOR_SECTION_SYNC (-1)
CALLBACK_NODE: 941462b4 Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
CREATE (0)
CALLBACK_NODE: 941462cc Inst:(94146008,"luafv","\Device\HarddiskVolume2") "luafv" "135000"
CREATE_NAMED_PIPE (1)
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
fltmgr!_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)
...