Windows内核重拾:如何枚举系统中Windows Filtering Platform (WFP)驱动中的callout函数

文章目录
  1. 1. 0x00 前言
  2. 2. 0x01 分析
  3. 3. 0x02 参考

0x00 前言

WFP是Windows提供的一套新的网络数据包过滤框架,用于取代之前的TDI过滤框架,为用户态和内核态都提供API支持,可以很方便的实现网络数据包拦截/编辑,常用于实现防火墙、网速控制及入侵检测等。


WFP框架

WFP架构如上图所示,虽然提供了用户态和内核态的接口,但是干事情部分都在内核层,关于WFP驱动的开发可以看下参考链接1。
在内核态最主要干活的,是通过FwpsCalloutRegister注册的三个回调函数,注册成功后会返回一个callout_id用于标识被注册的wfp callout对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct FWPS_CALLOUT1_
{
// Uniquely identifies the callout. This must be the same GUID supplied to
// FwpmCalloutAdd0.
GUID calloutKey;
// Flags
UINT32 flags;
// Pointer to the classification function.
FWPS_CALLOUT_CLASSIFY_FN1 classifyFn;
// Pointer to the notification function.
FWPS_CALLOUT_NOTIFY_FN1 notifyFn;
// Pointer to the flow delete function.
FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn;
} FWPS_CALLOUT1;

  • classifyFn:主要干活的回调,可以获得网络包全部信息(与注册类型相关),可以实现网络包数据拦截、编辑等。
  • notifyFn:当过滤器被添加到过滤引擎时被调用。
  • flowDeleteFn:当一个网络数据被终止时调用,这个函数中可以对classifyfn中的操作进行清理。
    那如何枚举出这三个函数呢,如何实现类似Pchunter中枚举系统回调的功能呢,关联出时哪一个驱动注册的wfp网络过滤函数?

0x01 分析

最好的方式是找找有没用类似功能的软件,或则前人相关的分析。无奈只能找到一个参考链接2中的msdn中的一个帖子,用户态提供FwpmCalloutEnum这个api可以枚举出系统中wfp信息,只有callout_id,没有注册这个callout的模块和callout function信息。
倒是有工具实现了这个功能,Windows-Kernel-Explorer实现了,但是模块加了VMP,不是很好分析。那就正面入手分析callout function如何注册到系统的了。
好在函数逻辑很简单,函数调用链大概如下:fwpkclnt.sys!FwpsCalloutRegister1—–>netio!KfdAddCalloutEntry——>netio!FeAddCalloutEntry,核心代码在函数FeAddCalloutEntry中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.text:0000000000026CA6 48 8B 05 F3 79 03 00                    mov     rax, cs:gWfpGlobal
.text:0000000000026CAD 8B 5C 24 68 mov ebx, [rsp+38h+call_out_id_a6]
.text:0000000000026CB1 48 C1 E3 06 shl rbx, 6
.text:0000000000026CB5 48 03 98 50 05 00 00 add rbx, [rax+550h]
.text:0000000000026CBC 48 8B CB mov rcx, rbx
.text:0000000000026CBF E8 6C 00 00 00 call IsCalloutEntryAvailable
.text:0000000000026CC4 48 8B F8 mov rdi, rax
.text:0000000000026CC7 48 85 C0 test rax, rax
.text:0000000000026CCA 0F 85 10 8C FF FF jnz loc_1F8E0
.text:0000000000026CD0 48 89 03 mov [rbx], rax
.text:0000000000026CD3 48 89 43 08 mov [rbx+8], rax
.text:0000000000026CD7 48 89 43 28 mov [rbx+28h], rax
.text:0000000000026CDB 48 89 43 30 mov [rbx+30h], rax
.text:0000000000026CDF 48 89 43 38 mov [rbx+38h], rax
.text:0000000000026CE3 8B 44 24 60 mov eax, dword ptr [rsp+38h+arg_20]
.text:0000000000026CE7 48 89 73 20 mov [rbx+20h], rsi
.text:0000000000026CEB 48 8B 74 24 70 mov rsi, [rsp+38h+device_object]
.text:0000000000026CF0 44 89 2B mov [rbx], r13d
.text:0000000000026CF3 C7 43 04 01 00 00 00 mov dword ptr [rbx+4], 1
.text:0000000000026CFA 4C 89 63 10 mov [rbx+10h], r12
.text:0000000000026CFE 48 89 6B 18 mov [rbx+18h], rbp
.text:0000000000026D02 89 43 28 mov [rbx+28h], eax
.text:0000000000026D05 48 85 F6 test rsi, rsi
.text:0000000000026D08 0F 85 BD 8B FF FF jnz loc_1F8CB
.text:0000000000026D0E
.text:0000000000026D0E loc_26D0E: ; CODE XREF: FeAddCalloutEntry-7398↑j
.text:0000000000026D0E ; FeAddCalloutEntry-7380↑j
.text:0000000000026D0E 48 8B 5C 24 40 mov rbx, [rsp+38h+arg_0]
.text:0000000000026D13 48 8B 6C 24 48 mov rbp, [rsp+38h+arg_8]
.text:0000000000026D18 48 8B 74 24 50 mov rsi, [rsp+38h+arg_10]
.text:0000000000026D1D 48 8B C7 mov rax, rdi
.text:0000000000026D20 48 83 C4 20 add rsp, 20h
.text:0000000000026D24 41 5D pop r13
.text:0000000000026D26 41 5C pop r12
.text:0000000000026D28 5F pop rdi
.text:0000000000026D29 C3 retn

可以分析出对于每一个注册的callout,都会将callout的callout function信息放入一个结构体中,这些结构体都放在一个数组中,猜测这个结构体的名称叫CalloutEntry,每一个CallOutEntry的大小应该是64(左移6位,相当于乘以64)。而每一个callout_id对应的CallOutEntry结构的基地址应该等于[[gWfpGlobal]+0x550] + callout_id*64,而CallOutEntry结构体内容应该如下所示。

1
2
3
4
5
6
7
8
+0x0   unknow1
+0x8 unknow2
+0x10 classifyFn
+0x18 notifyFn
+0x20 flowDeleteFn
+0x28 unknow3
+0x30 unknow4
+0x38 device_object

分析到这里再通过关键词gWfpGlobal搜索,可以找到参考链接3,参考链接3给出了一个windbg脚本,这里可以优化下,可以直接再Win7 x64系统下打印出系统中所有的callout function函数地址(如果提示找不到符号地址,可以执行下.reload /f /i netio.sys)。

1
r $t0=poi(poi(netio!gWfpGlobal)+0x550);.for ( r $t1=0; @$t1 < 11e; r $t1=@$t1+1 ) {r $t1;dps @$t0+2*@$ptrsize L3; r $t0=@$t0+40;}

0x02 参考

1.windows驱动-WFP框架介绍及其编程
2.List of callout drivers
3.Finding Windows Filtering Platform (WFP) Callouts