Finding Windows Filtering Platform (WFP) Callouts

WFP callouts are functions that are registered by kernel mode WFP filters with the networking stack (NETIO.sys). This document describes a technique to find a list of WFP callouts registered on the system.

The variable netio!gWfpGlobal is the starting point for most WFP data structures.
0: kd> dp   netio!gWfpGlobal L1
fffff880`017536a0  fffffa80`03718000 <<< pointer to the WFP global structure
The number of callouts and the pointer the array of callout structures is stored in this global table. To find the offset of these fields use the following technique:
0: kd> u netio!FeInitCalloutTable
NETIO!FeInitCalloutTable:
fffff880`01732a60 fff3            push    rbx
fffff880`01732a62 4883ec20        sub     rsp,20h
fffff880`01732a66 488b05330c0200  mov     rax,qword ptr [NETIO!gWfpGlobal (fffff880`017536a0)]
fffff880`01732a6d 33c9            xor     ecx,ecx
fffff880`01732a6f ba57667043      mov     edx,43706657h
fffff880`01732a74 48898848050000  mov     qword ptr [rax+548h],rcx <<< Offset of number of Entries
fffff880`01732a7b 48898850050000  mov     qword ptr [rax+550h],rcx <<< Offset of pointer to array of callout structures
fffff880`01732a82 4c8b05170c0200  mov     r8,qword ptr [NETIO!gWfpGlobal (fffff880`017536a0)]
The find the number of built in layers and the pointer to the array of calllout structures use the following technique:
0: kd> dq fffffa80`03718000 + 548 L1
fffffa80`03718548  00000000`0000011e <<< number of entries

0: kd> dps fffffa80`03718000 + 550 L1
fffffa80`03718550  fffffa80`0509c000 <<< Pointer to array of callout structures
To confirm that callout array pointer is correct, use the following technique:
0: kd> !pool fffffa80`0509c000 
Pool page fffffa800509c000 region is Nonpaged pool
*fffffa800509c000 : large page allocation, Tag is WfpC, size is 0x4790 bytes
            Pooltag WfpC : WFP callouts, Binary : netio.sys
To find the size of each structure in this array use the following technique :
Number of layers
0: kd>  u NETIO!InitDefaultCallout
NETIO!InitDefaultCallout:
fffff880`01730230 fff3            push    rbx
fffff880`01730232 4883ec20        sub     rsp,20h
fffff880`01730236 4c8d05ab320200  lea     r8,[NETIO!gFeCallout (fffff880`017534e8)]
fffff880`0173023d ba57667043      mov     edx,43706657h
fffff880`01730242 b940000000      mov     ecx,40h <<< size of each entry in the array allocated from NPP
fffff880`01730247 e8f4eefdff      call    NETIO!WfpPoolAllocNonPaged (fffff880`0170f140)
fffff880`0173024c 488bd8          mov     rbx,rax
fffff880`0173024f 4885c0          test    rax,rax
To display all the elements of the arrary use the following command template :
"r $t0=array_base;.for ( r $t1=0; @$t1 < array_count; r $t1=@$t1+1 ) {dps @$t0+2*@$ptrsize L2; r $t0=@$t0+structure_size;}"
0: kd> r $t0=fffffa80`0509c000;.for ( r $t1=0; @$t1 < 11e; r $t1=@$t1+1 ) {dps @$t0+2*@$ptrsize L2; r $t0=@$t0+40;}
fffffa80`0509c000  00000000`00000000
fffffa80`0509c008  00000000`00000000
fffffa80`0509c010  00000000`00000000
fffffa80`0509c018  00000000`00000000
fffffa80`0509c020  00000000`00000000
fffffa80`0509c028  00000000`00000000
fffffa80`0509c030  00000000`00000000
fffffa80`0509c038  00000000`00000000
fffffa80`0509c040  00000001`00000001
fffffa80`0509c048  00000000`00000000
fffffa80`0509c050  fffff880`019bf640 tcpip!IPSecInboundTransportFilterCalloutClassifyV4 <<< WFP callouts
fffffa80`0509c058  fffff880`0180f2c0 tcpip!IPSecInboundTransportFilterCalloutNotifyV6+0x2  <<< WFP callouts
fffffa80`0509c060  00000000`00000000
fffffa80`0509c068  00000000`00000000
fffffa80`0509c070  00000000`00000000
fffffa80`0509c078  00000000`00000000
fffffa80`0509c080  00000001`00000001
.
.
.