Windows驱动开发入门-WFP过滤平台

碎碎念

本节讲网络传输层过滤,其中传输层接口TDI接口技术已被淘汰,取代其的新技术称为Windows过滤平台WFP。WFP包含用户态API和内核态API,本节只讲内核态开发。

WFP分为两大层次模块,用户态基础过滤引擎BFE和内核态过滤引擎KMFE。基础过滤引擎对上提供C调用方式的API以及RPC接口,这些接口封装在fwpuclnt.dll中。基础过滤引擎对下和内核态过滤引擎交互,受内核态过滤引擎控制。内核态过滤引擎与系统网络协议栈交互,通过垫片(此垫片非彼垫片)内核模块从网络协议栈Tcpip.sys中获取网络数据,垫片被插入到网络协议栈各个层中。垫片获取到数据后,通过内核态过滤引擎提供的分类API,把数据传到WFP相应分层中。内核态过滤引擎分为若干个分层,每个分层中存在一个或多个子层和过滤器,子层是分层的更小划分。子层被赋予不同权重,同一个子层中,WFP按照权重从大到小把数据交给相应子层。

例如TCP/IP协议栈中从上到下有数据流分层垫片、ALE网络连接管理、传输分层垫片(TCP/UDP)、网络分层垫片(IPv4/IPv6),他们通过分类API,与内核态过滤引擎不同子层交互,后者从上到小有流/报文数据分层、发送.接收ALE分层、发送/接收传输分层、发送/接收IP分层。

WFP架构中存在过滤器数据结构,里面保存网络数据包的拦截规则和处理动作,开发者可向WFP的内核态过滤引擎添加过滤器。

本节代码报fwpsk.h语法错误的话,在DriverEntry例程文件最开头补上#define NDIS620,表示NDIS版本6.20,用别的版本的话可以自己查对应的操作系统版本。若链接错误需要在项目属性的链接器中添加:

1
WDMSec.lib;FwpKClnt.lib;Fwpuclnt.lib;NTOSKrnl.lib;NetIO.lib;NDIS.lib;UUID.lib;advapi32.lib;kernel32.lib;

基本对象模型

内核态过滤引擎检查网络数据包是否命中过滤器的规则,对于命中规则的过滤器,内核态过滤引擎执行这些过滤器中指定的动作。动作有放行或拦截网络数据包。一个分层中可能有多个子层和多个过滤器,一次网络事件可能同时命中多个过滤器规则。此时过滤仲裁器模块计算出最终过滤动作并交由内核态过滤引擎,后者将最终过滤结果反馈给垫片。

垫片安插在系统网络协议栈的不同层中,获取网络协议栈的数据。安插在不同协议层的垫片获取到的数据不同。垫片还能把内核态过滤引擎的过滤结果反馈给网络协议栈。

呼出接口Callout由一系列回调函数和一个GUID组成。当网络数据命中某过滤器规则且过滤器指定一个呼出接口时,该呼出接口内的回调函数会被调用。不同呼出接口的回调函数实现不同功能,系统内置了一部分呼出接口,开发者也可向系统注册自己的呼出接口来完成特定逻辑。FWPS_CALLOUT结构描述呼出接口信息:

1
2
3
4
5
6
7
8
9
10
#define FWPS_CALLOUT FWPS_CALLOUT3
typedef struct FWPS_CALLOUT3_ {
// Uniquely identifies the callout. This must be the same GUID supplied to
GUID calloutKey; // FwpmCalloutAdd0. 该呼出接口的唯一标识
UINT32 flags; // Flags 特性 一般0
//下面仨回调函数
FWPS_CALLOUT_CLASSIFY_FN3 classifyFn; // Pointer to the classification function.
FWPS_CALLOUT_NOTIFY_FN3 notifyFn; // Pointer to the notification function.
FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn; // Pointer to the flow delete function.
} FWPS_CALLOUT3;

内核态中,每个分层用64位的LUID标识,称为运行时过滤分层标识。用户态中,每个分层用129位的GUID标识,称为管理过滤分层标识。部分常用管理过滤分层标识如下,分别位接收/发送IPv4/6网络数据包层:

1
2
3
4
FWPM_LAYER_INBOUND_IPPACKET_V4
FWPM_LAYER_INBOUND_IPPACKET_V6
FWPM_LAYER_OUTBOUND_IPPACKET_V4
FWPM_LAYER_OUTBOUND_IPPACKET_V6

子层结构体定义为:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct FWPM_SUBLAYER0_ {
GUID subLayerKey; //子层标识
FWPM_DISPLAY_DATA0 displayData; //显示数据
UINT32 flags; //子层特性 FWPM_SUBLAYER_FLAG_PERSISTENT标识为永久性子层 与基础过滤引擎生命周期相同
PGUID providerKey;
FWP_BYTE_BLOB providerData;
UINT16 weight; //子层权重
} FWPM_SUBLAYER0;
typedef struct FWPM_DISPLAY_DATA0_ {
wchar_t* name; //对象名字
wchar_t* description; //对象描述
} FWPM_DISPLAY_DATA0;

过滤器中的规则称为过滤条件,当一个过滤器中所有过滤条件全部成立时,才认为该过滤器规则被命中。使用过滤器时必需明确过滤器被添加到内核态过滤引擎哪个分层中,同一分层中可存在多个过滤器,不同过滤器被赋予不同权重,同子层内权重互相不能相同。过滤器还可关联呼出接口,当过滤器规则命中时WFP执行该过滤器关联的呼出接口内的回调函数。回调函数返回过滤结果允许或拦截到WFP。过滤器结构如下:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#define FWPM_FILTER FWPM_FILTER0
typedef struct FWPM_FILTER0_ FWPM_FILTER0;
typedef struct FWPM_FILTER0_ {
GUID filterKey; //过滤器唯一标识 指定0则过滤引擎自动分配
FWPM_DISPLAY_DATA0 displayData;
UINT32 flags;
PGUID providerKey;
FWP_BYTE_BLOB providerData;
GUID layerKey; //管理过滤分层标识
GUID subLayerKey; //子层GUID 添加到哪个子层 设为IID_NULL则被添加到默认子层
FWP_VALUE0 weight; //权重
UINT32 numFilterConditions; //过滤条件个数
FWPM_FILTER_CONDITION0* filterCondition; //过滤条件数组
FWPM_ACTION0 action;
union {
UINT64 rawContext;
GUID providerContextKey;
};
PGUID reserved;
UINT64 filterId;
FWP_VALUE0 effectiveWeight;
} FWPM_FILTER0;
typedef struct FWP_VALUE0_ {
FWP_DATA_TYPE type;
union {
UINT8 uint8;
UINT16 uint16;
UINT32 uint32;
PUINT64 uint64;
INT8 int8;
INT16 int16;
INT32 int32;
PINT64 int64;
FLOAT float32;
PDOUBLE double64;
FWP_BYTE_ARRAY16* byteArray16;
FWP_BYTE_BLOB* byteBlob;
SID* sid;
FWP_BYTE_BLOB* sd;
FWP_TOKEN_INFORMATION* tokenInformation;
FWP_BYTE_BLOB* tokenAccessInformation;
LPWSTR unicodeString;
FWP_BYTE_ARRAY6* byteArray6;
};
} FWP_VALUE0;
typedef enum FWP_DATA_TYPE_ { //weight.type的取值决定匿名结构体成员
FWP_EMPTY = 0, //权重自动分配
FWP_UINT8 = (FWP_EMPTY + 1),
FWP_UINT16 = (FWP_UINT8 + 1), //权重高63~60位 低60位自动分配
FWP_UINT32 = (FWP_UINT16 + 1),
FWP_UINT64 = (FWP_UINT32 + 1),
FWP_INT8 = (FWP_UINT64 + 1),
FWP_INT16 = (FWP_INT8 + 1),
FWP_INT32 = (FWP_INT16 + 1),
FWP_INT64 = (FWP_INT32 + 1),
FWP_FLOAT = (FWP_INT64 + 1),
FWP_DOUBLE = (FWP_FLOAT + 1),
FWP_BYTE_ARRAY16_TYPE = (FWP_DOUBLE + 1),
FWP_BYTE_BLOB_TYPE = (FWP_BYTE_ARRAY16_TYPE + 1),
FWP_SID = (FWP_BYTE_BLOB_TYPE + 1),
FWP_SECURITY_DESCRIPTOR_TYPE = (FWP_SID + 1),
FWP_TOKEN_INFORMATION_TYPE = (FWP_SECURITY_DESCRIPTOR_TYPE + 1),
FWP_TOKEN_ACCESS_INFORMATION_TYPE = (FWP_TOKEN_INFORMATION_TYPE + 1),
FWP_UNICODE_STRING_TYPE = (FWP_TOKEN_ACCESS_INFORMATION_TYPE + 1),
FWP_BYTE_ARRAY6_TYPE = (FWP_UNICODE_STRING_TYPE + 1),
FWP_SINGLE_DATA_TYPE_MAX = 0xff,
FWP_V4_ADDR_MASK = (FWP_SINGLE_DATA_TYPE_MAX + 1),
FWP_V6_ADDR_MASK = (FWP_V4_ADDR_MASK + 1),
FWP_RANGE_TYPE = (FWP_V6_ADDR_MASK + 1),
FWP_DATA_TYPE_MAX = (FWP_RANGE_TYPE + 1)
} FWP_DATA_TYPE;
typedef struct FWPM_FILTER_CONDITION0_ {
GUID fieldKey; //网络数据包字段标识 如数据包远程IP地址字段为FWPM_CONDITION_IP_REMOTE_ADDRESS
FWP_MATCH_TYPE matchType; //匹配类型
FWP_CONDITION_VALUE0 conditionValue; //过滤条件的值
} FWPM_FILTER_CONDITION0;
typedef enum FWP_MATCH_TYPE_ {
FWP_MATCH_EQUAL = 0, //fieldKey与conditionValue相等
FWP_MATCH_GREATER = (FWP_MATCH_EQUAL + 1), //大于
FWP_MATCH_LESS = (FWP_MATCH_GREATER + 1),
FWP_MATCH_GREATER_OR_EQUAL = (FWP_MATCH_LESS + 1),
FWP_MATCH_LESS_OR_EQUAL = (FWP_MATCH_GREATER_OR_EQUAL + 1),
FWP_MATCH_RANGE = (FWP_MATCH_LESS_OR_EQUAL + 1),
FWP_MATCH_FLAGS_ALL_SET = (FWP_MATCH_RANGE + 1),
FWP_MATCH_FLAGS_ANY_SET = (FWP_MATCH_FLAGS_ALL_SET + 1),
FWP_MATCH_FLAGS_NONE_SET = (FWP_MATCH_FLAGS_ANY_SET + 1),
FWP_MATCH_EQUAL_CASE_INSENSITIVE = (FWP_MATCH_FLAGS_NONE_SET + 1),
FWP_MATCH_NOT_EQUAL = (FWP_MATCH_EQUAL_CASE_INSENSITIVE + 1),
FWP_MATCH_PREFIX = (FWP_MATCH_NOT_EQUAL + 1),
FWP_MATCH_NOT_PREFIX = (FWP_MATCH_PREFIX + 1),
FWP_MATCH_TYPE_MAX = (FWP_MATCH_NOT_PREFIX + 1)
} FWP_MATCH_TYPE;
typedef struct FWP_CONDITION_VALUE0_ {
FWP_DATA_TYPE type;
union {
UINT8 uint8;
UINT16 uint16;
UINT32 uint32;
PUINT64 uint64;
INT8 int8;
INT16 int16;
INT32 int32;
PINT64 int64;
FLOAT float32;
PDOUBLE double64;
FWP_BYTE_ARRAY16* byteArray16;
FWP_BYTE_BLOB* byteBlob;
SID* sid;
FWP_BYTE_BLOB* sd;
FWP_TOKEN_INFORMATION* tokenInformation;
FWP_BYTE_BLOB* tokenAccessInformation;
LPWSTR unicodeString;
FWP_BYTE_ARRAY6* byteArray6;
FWP_V4_ADDR_AND_MASK* v4AddrMask;
FWP_V6_ADDR_AND_MASK* v6AddrMask;
FWP_RANGE0* rangeValue;
};
} FWP_CONDITION_VALUE0;
typedef struct FWPM_ACTION0_ {
FWP_ACTION_TYPE type; //动作类型 过滤条件成立后的动作
//FWP_ACTION_BLOCK 拦截
//FWP_ACTION_PERMIT 放行
//FWP_ACTION_CALLOUT_TERMINATING 回调呼出接口内回调函数 后者始终返回允许或拦截
//FWP_ACTION_CALLOUT_INSPECTION 同上 不会返回允许或拦截
//FWP_ACTION_CALLOUT_UNKNOWN 同上 可能返回允许或拦截
union {
GUID filterType;
GUID calloutKey; //呼出接口GUID type取值FWP_ACTION_CALLOUT_*时 过滤器规则命中后WFP回调该GUID对应的呼出接口结构体内回调函数
};
} FWPM_ACTION0;

前文提到notifyFn、classifyFn和flowDeleteFn回调函数。当一个过滤器关联了呼出接口且规则被命中时,过滤引擎hi掉呼出接口的classify函数。开发者可在该回调函数中获取网络数据包相关信息,具体信息取决于过滤器所在分层,还可以设置对网络数据包的允许或拦截操作,原型如下:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
VOID NTAPI classifyFn1(
_In_ CONST FWPS_INCOMING_VALUES0* inFixedValues, //传入参数 包含网络数据包信息
_In_ CONST FWPS_INCOMING_METADATA_VALUES0* inMetaValues, //元数据值
_Inout_opt_ PVOID layerData, //被过滤的网络原始数据
_In_opt_ CONST PVOID classifyContext, //和呼出接口驱动关联的上下文
_In CONST FWPS_FILTER1* filter, //相关过滤器指针
_In_opt_ UINT64 flowContext, //和流句柄关联的上下文 可用FwpsFlowAssociateContext0把一个上下文与数据流句柄进行关联
_Out_ FWPS_CLASSIFY_OUT0* classifyOut //该函数对这个网络数据包的过滤结果
);
typedef struct FWPS_INCOMING_VALUES0_ {
UINT16 layerId; //运行时过滤分层标识
UINT32 valueCount; //incomingValue数组元素个数
FWPS_INCOMING_VALUE0* incomingValue; //网络数据包相关信息数组
} FWPS_INCOMING_VALUES0;
typedef struct FWPS_INCOMING_VALUE0_ {
FWP_VALUE0 value; //用法参考上文
} FWPS_INCOMING_VALUE0;
typedef struct FWPS_INCOMING_METADATA_VALUES0_ { //每个成员都为FWPS_METADATA_FIELD_*元数据字段标识符来代表
UINT32 currentMetadataValues; // Bitmask representing which values are set. 决定本结构体内部有效成员
// Internal flags;
UINT32 flags;
// Reserved for system use.
UINT64 reserved;
// Discard module and reason.
FWPS_DISCARD_METADATA0 discardMetadata;
// Flow Handle.
UINT64 flowHandle;
// IP Header size.
UINT32 ipHeaderSize;
// Transport Header size
UINT32 transportHeaderSize;
// Process Path.
FWP_BYTE_BLOB* processPath;
// Token used for authorization.
UINT64 token;
// Process Id.
UINT64 processId;
// Source and Destination interface indices for discard indications.
UINT32 sourceInterfaceIndex;
UINT32 destinationInterfaceIndex;
// Compartment Id for injection APIs.
ULONG compartmentId;
// Fragment data for inbound fragments.
FWPS_INBOUND_FRAGMENT_METADATA0 fragmentMetadata;
// Path MTU for outbound packets (to enable calculation of fragments).
ULONG pathMtu;
// Completion handle (required in order to be able to pend at this layer).
HANDLE completionHandle;
// Endpoint handle for use in outbound transport layer injection.
UINT64 transportEndpointHandle;
// Remote scope id for use in outbound transport layer injection.
SCOPE_ID remoteScopeId;
// Socket control data (and length) for use in outbound transport layer injection.
WSACMSGHDR* controlData;
ULONG controlDataLength;
// Direction for the current packet. Only specified for ALE re-authorization.
FWP_DIRECTION packetDirection;
#if (NTDDI_VERSION >= NTDDI_WIN6SP1)
// Raw IP header (and length) if the packet is sent with IP header from a RAW socket.
PVOID headerIncludeHeader;
ULONG headerIncludeHeaderLength;
#if (NTDDI_VERSION >= NTDDI_WIN7)
IP_ADDRESS_PREFIX destinationPrefix;
UINT16 frameLength;
UINT64 parentEndpointHandle;
UINT32 icmpIdAndSequence;
// PID of the process that will be accepting the redirected connection
DWORD localRedirectTargetPID;
// original destination of a redirected connection
SOCKADDR* originalDestination;
#if (NTDDI_VERSION >= NTDDI_WIN8)
HANDLE redirectRecords;
// Bitmask representing which L2 values are set.
UINT32 currentL2MetadataValues;
// L2 layer Flags;
UINT32 l2Flags;
UINT32 ethernetMacHeaderSize;
UINT32 wiFiOperationMode;
#if (NDIS_SUPPORT_NDIS630)
NDIS_SWITCH_PORT_ID vSwitchSourcePortId;
NDIS_SWITCH_NIC_INDEX vSwitchSourceNicIndex;
NDIS_SWITCH_PORT_ID vSwitchDestinationPortId;
#else
UINT32 padding0;
USHORT padding1;
UINT32 padding2;
#endif // (NDIS_SUPPORT_NDIS630)
HANDLE vSwitchPacketContext;
#endif // (NTDDI_VERSION >= NTDDI_WIN8)
#endif // (NTDDI_VERSION >= NTDDI_WIN7)
#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1)
#if (NTDDI_VERSION >= NTDDI_WIN8)
PVOID subProcessTag;
// Reserved for system use.
UINT64 reserved1;
#endif
} FWPS_INCOMING_METADATA_VALUES0;
#define FWPS_METADATA_FIELD_DISCARD_REASON 0x00000001
#define FWPS_METADATA_FIELD_FLOW_HANDLE 0x00000002
#define FWPS_METADATA_FIELD_IP_HEADER_SIZE 0x00000004
#define FWPS_METADATA_FIELD_PROCESS_PATH 0x00000008
#define FWPS_METADATA_FIELD_TOKEN 0x00000010
#define FWPS_METADATA_FIELD_PROCESS_ID 0x00000020
#define FWPS_METADATA_FIELD_SYSTEM_FLAGS 0x00000040
#define FWPS_METADATA_FIELD_RESERVED 0x00000080
#define FWPS_METADATA_FIELD_SOURCE_INTERFACE_INDEX 0x00000100
#define FWPS_METADATA_FIELD_DESTINATION_INTERFACE_INDEX 0x00000200
#define FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE 0x00000400
#define FWPS_METADATA_FIELD_COMPARTMENT_ID 0x00000800
#define FWPS_METADATA_FIELD_FRAGMENT_DATA 0x00001000
#define FWPS_METADATA_FIELD_PATH_MTU 0x00002000
#define FWPS_METADATA_FIELD_COMPLETION_HANDLE 0x00004000
#define FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE 0x00008000
#define FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA 0x00010000
#define FWPS_METADATA_FIELD_REMOTE_SCOPE_ID 0x00020000
#define FWPS_METADATA_FIELD_PACKET_DIRECTION 0x00040000
#if (NTDDI_VERSION >= NTDDI_WIN6SP1)
#define FWPS_METADATA_FIELD_PACKET_SYSTEM_CRITICAL 0x00080000
#define FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU 0x00100000
#define FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU 0x00200000
#define FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED 0x00400000
#define FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER 0x00800000
#if (NTDDI_VERSION >= NTDDI_WIN7)
#define FWPS_METADATA_FIELD_DESTINATION_PREFIX 0x01000000
#define FWPS_METADATA_FIELD_ETHER_FRAME_LENGTH 0x02000000
#define FWPS_METADATA_FIELD_PARENT_ENDPOINT_HANDLE 0x04000000
#define FWPS_METADATA_FIELD_ICMP_ID_AND_SEQUENCE 0x08000000
#define FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID 0x10000000
#define FWPS_METADATA_FIELD_ORIGINAL_DESTINATION 0x20000000
#if (NTDDI_VERSION >= NTDDI_WIN8)
#define FWPS_METADATA_FIELD_REDIRECT_RECORD_HANDLE 0x40000000
#define FWPS_METADATA_FIELD_SUB_PROCESS_TAG 0x80000000

#define FWPS_IS_METADATA_FIELD_PRESENT(metadataValues, metadataField) (((metadataValues)->currentMetadataValues & (metadataField)) == (metadataField)) //用于测试vurrentMetadataValues是否包含某个具体的标识符 返回非0有效 如FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues,FWPS_METADATA_FILED_FLOW_HANDLE)等
typedef struct FWPS_CLASSIFY_OUT0_ {
FWP_ACTION_TYPE actionType; //动作类型
//FWP_ACTION_BLOCK 拦截
//FWP_ACTION_CONTINUE 把早先过滤器设置在数据包上的动作传递给下一个过滤器
//FWP_ACTION_NONE 不做任何动作或决策
//FWP_ACTION_NONE_NO_MATCH 因不匹配过滤器数据类型 不做任何动作或决策
//FWP_ACTION_PERMIT 允许
UINT64 outContext; //系统保留不可修改
UINT64 filterId; //系统保留不可修改
UINT32 rights; //该结构体其他成员是否可修改 如FWPS_RIGHT_ACTION_WRITE表示当前结构体其他非系统保留成员可修改
UINT32 flags; //过滤动作特性 如FWPS_CALSSIFY_OUT_FLAG_ABSORB表示拦截时系统不审计和记录
UINT32 reserved; //系统保留不可修改
} FWPS_CLASSIFY_OUT0;

当过滤器被添加到过滤引擎中或从过滤引擎中移除时,WFP调用该过滤器对应呼出接口的notifyFn1函数。开发者可通过该函数得知呼出接口关联的过滤器的操作情况,原型如下。

1
2
3
4
5
6
7
8
9
NTSTATUS NTAPI notifyFn1(
_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, //通告类型 即本次回调原因
//FWPS_CALLOUT_NOTIFY_ADD_FILTER 过滤器被添加到过滤引擎中
//FWPS_CALLOUT_NOTIFY_DELETE_FILTER 过滤器从过滤引擎中移除
//FWPS_CALLOUT_NOTIFY_TYPE_MAX 无意义 测试用
_In_ CONST PGUID filterKey, //过滤器标识 只有notifyType为FWPS_CALLOUT_NOTIFY_ADD_FILTER时该值才非空
_In_ CONST FWPS_FILTER1* filter //将要添加或删除的过滤器
);//STATUS_SUCCESS表示回调函数接受该事件 其他错误码表示回调函数不接受
//返回错误码时 添加时过滤器不添加到过滤引擎中 删除时也会从过滤引擎中删除

当一个网络数据流将要被终止,且该数据流被关联了上下文时,flowDeleteFn被回调,可在该回调函数中清理关联的上下文:

1
2
3
4
5
VOID NTAPI flowDeleteFn(
_In_ UINT16 layerId, //分层标识
_In_ UINT32 calloutId, //相应呼出接口
_In_ UINT64 flowContext //关联的上下文指针
);

WFP操作

开发者在内核态用WFP提供的API实现网络数据包过滤,分为步骤如下:定义一个或多个呼出接口,向过滤引擎注册呼出接口;添加呼出接口到过滤引擎;设计一个或多个子层,将子层添加到分层中;设计过滤器,把呼出接口、子层、分层和过滤器关联起来。

注册呼出接口时可用FwpsCalloutRegister2,定义如下。

1
2
3
4
5
NTSTATUS NTAPI FwpsCalloutRegister2(
_Inout_ PVOID deviceObject, //呼出接口驱动创建的设备对象指针
_In_ CONST FWPS_CALLOUT2* callout, //呼出几口对象
_Out_opt_ PUINT32 calloutId //呼出接口ID 运行时标识
);//成功注册STATUS_SUCCESS 已注册STATUS_FWP_ALREADY_EXISTS 其他错误码则发生错误

卸载呼出接口用FwpsCalloutUnregisterByIdFwpsCalloutUnregisterByKey。前者通过指定呼出接口的运行时标识来对呼出接口进行卸载,后者通过指定呼出接口的GUID来对呼出接口进行卸载。

注册呼出接口后,需要把呼出接口添加到过滤引擎中,在此之前得先用FwpmEngineOpen0打开过滤引擎。

1
2
3
4
5
6
7
NTSTATUS NTAPI FwpmEngineOpen0(
_In_opt_ CONST wchar_t* serverName, //呼出接口驱动必为NULL
_In_ UINT32 authnService, //认证服务 呼出接口驱动必为RPC_C_AUTHN_WINNT或RPC_C_AUTHN_DEFAULT
_In_opt_ SEC_WINNT_AUTH_IDENTIFY_W* authIdentify,
_In_opt_ CONST FWPM_SESSION0* session, //Session信息
_Out_ HANDLE* engineHandle //返回打开的过滤引擎句柄
);

FwpmCalloutAdd0将呼出接口添加到过滤引擎中。

1
2
3
4
5
6
DWORD WINAPI FwpmCalloutAdd0(
_In_ HANDLE engineHandle, //过滤引擎句柄
_In_ CONST FWPM_CALLOUT0* callout, //呼出接口指针
_In_opt_ PSECURITY_DESCRIPTOR sd, //安全描述符
_out_opt_ PUINT32 id //返回ID 可用该ID移除已添加的呼出接口
); //成功添加STATUS_SUCCESS 有相同呼出接口已添加STATUS_FWP_ALREADY_EXISTS 其他错误返回错误码

FwpmCalloutDeleteById0FwpmCalloutDeleteByKey0可移除添加到过滤引擎中的呼出接口。

FwpmSubLayerAdd0添加子层:

1
2
3
4
5
DWORD WINAPI FwpmSubLayerAdd0(
_In_ HANDLE engineHandle, //过滤引擎句柄
_In_ CONST FWPM_SUBLAYER0* subLayer, //要添加的子层对象指针
_In_opt_ PSECURITY_DESCRIPTOR sd //安全描述符
); //成功ERROR_SUCCESS 失败返回错误码

FwpmFilterAdd0添加过滤器:

1
2
3
4
5
6
DWORD WINAPI FwpmFilterAdd0(
_In_ HANDLE engineHandle, //过滤引擎句柄
_In_ CONST FWPM_FILTER0* filter, //要添加的过滤器对象指针
_In_opt_ PSECURITY_DESCRIPTOR sd, //安全描述符
_Out_opt_ PUINT64 id //成功添加后返回分配的Id来唯一标识该过滤器
); //成功ERROR_SUCCESS 失败返回错误码

移除过滤器用FwpmFilterDeleteById0FwpmFilterDeleteByKey0

WFP例子

拦截TCP协议对外连接80端口。驱动入口函数实现:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT* DriverObject, __in PUNICODE_STRING RegistryPath) {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
UNREFERENCED_PARAMETER(RegistryPath);
do {
if (DriverObject == NULL)
break;
DriverObject->MajorFunction[IRP_MJ_CREATE] = WfpSampleIRPDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = WfpSampleIRPDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = WfpSampleIRPDispatch;
if (FALSE == InitRuleInfo())
break;
g_pDeviceObj = CreateDevice(DriverObject); //创建设备对象
if (g_pDeviceObj == NULL)
break;
if (InitWfp() != STATUS_SUCCESS) //初始化WFP所有操作
break;
DriverObject->DriverUnload = DriverUnload;
nStatus = STATUS_SUCCESS;
} while (FALSE);
if (nStatus != STATUS_SUCCESS) {
UninitWfp();
DeleteDevice();
UninitRuleInfo();
};
return nStatus;
};
NTSTATUS InitWfp(VOID) {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
do {
g_hEngine = OpenEngine(); //打开过滤引擎 获取句柄
if (g_hEngine == NULL)
break;
if (STATUS_SUCCESS != WfpRegisterCallouts(g_pDeviceObj)) //向过滤引擎注册呼出接口
break;
if (STATUS_SUCCESS != WfpAddCallouts()) //向过滤引擎添加呼出接口
break;
if (STATUS_SUCCESS != WfpAddSubLayer()) //向过滤引擎添加子层
break;
if (STATUS_SUCCESS != WfpAddFilters()) //向过滤引擎添加过滤器
break;
nStatus = STATUS_SUCCESS;
} while (FALSE);
return nStatus;
};
HANDLE OpenEngine(VOID) {
FWPM_SESSION0 Session = { 0 };
HANDLE hEngine = NULL;
FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &Session, &hEngine); //打开过滤引擎 返回句柄
return hEngine;
};
DEFINE_GUID(WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID, 0xd969fc67, 0x6fb2, 0x4504, 0x91, 0xce, 0xa9, 0x7c, 0x3c, 0x32, 0xad, 0x36); // {D969FC67-6FB2-4504-91CE-A97C3C32AD36}
NTSTATUS WfpRegisterCallouts(IN OUT PVOID deviceObject) { //设备对象指针
NTSTATUS status = STATUS_UNSUCCESSFUL;
do {
if (deviceObject == NULL)
break;
status = WfpRegisterCalloutImple(deviceObject, Wfp_Sample_Established_ClassifyFn_V4, Wfp_Sample_Established_NotifyFn_V4, Wfp_Sample_Established_FlowDeleteFn_V4, &WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID, 0, &g_uFwpsEstablishedCallOutId); //注册呼出接口 有仨回调函数 还有个GUID
if (status != STATUS_SUCCESS)
break;
status = STATUS_SUCCESS;
} while (FALSE);
return status;
};
NTSTATUS WfpRegisterCalloutImple(
IN OUT void* deviceObject, //设备对象指针
IN FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction, IN FWPS_CALLOUT_NOTIFY_FN NotifyFunction, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction, IN GUID const* calloutKey, IN UINT32 flags, OUT UINT32* calloutId) {
FWPS_CALLOUT sCallout;
NTSTATUS status = STATUS_SUCCESS;
memset(&sCallout, 0, sizeof(FWPS_CALLOUT));
sCallout.calloutKey = *calloutKey;
sCallout.flags = flags;
sCallout.classifyFn = ClassifyFunction;
sCallout.notifyFn = NotifyFunction;
sCallout.flowDeleteFn = FlowDeleteFunction;
status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
return status;
};
DEFINE_GUID(WFP_SAMPLE_SUBLAYER_GUID,0xed6a516a, 0x36d1, 0x4881, 0xbc, 0xf0, 0xac, 0xeb, 0x4c, 0x4, 0xc2, 0x1c); // {ED6A516A-36D1-4881-BCF0-ACEB4C04C21C}
NTSTATUS WfpAddCallouts(VOID) {
NTSTATUS status = STATUS_SUCCESS;
FWPM_CALLOUT fwpmCallout = { 0 };
fwpmCallout.flags = 0;
do {
if (g_hEngine == NULL)
break;
fwpmCallout.displayData.name = (wchar_t*)WFP_SAMPLE_ESTABLISHED_CALLOUT_DISPLAY_NAME;
fwpmCallout.displayData.description = (wchar_t*)WFP_SAMPLE_ESTABLISHED_CALLOUT_DISPLAY_NAME;
fwpmCallout.calloutKey = WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID; //GUID
fwpmCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; //网络连接的建立 该分层属于ALE层 WFP维护数据包状态 如连接状态
status = FwpmCalloutAdd(g_hEngine, &fwpmCallout, NULL, &g_uFwpmEstablishedCallOutId);
if (!NT_SUCCESS(status) && (status != STATUS_FWP_ALREADY_EXISTS))
break;
status = STATUS_SUCCESS;
} while (FALSE);
return status;
};
NTSTATUS WfpAddSubLayer(VOID) { //向过滤引擎增加一个分层 权重为65535
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
FWPM_SUBLAYER SubLayer = { 0 };
SubLayer.flags = 0;
SubLayer.displayData.description = WFP_SAMPLE_SUB_LAYER_DISPLAY_NAME;
SubLayer.displayData.name = WFP_SAMPLE_SUB_LAYER_DISPLAY_NAME;
SubLayer.subLayerKey = WFP_SAMPLE_SUBLAYER_GUID;
SubLayer.weight = 65535;
if (g_hEngine != NULL)
nStatus = FwpmSubLayerAdd(g_hEngine, &SubLayer, NULL);
return nStatus;
};
NTSTATUS WfpAddFilters(VOID) { //向过滤引擎添加一个过滤器 并和之前定义的呼出接口、子层等关联
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
do {
FWPM_FILTER0 Filter = { 0 };
FWPM_FILTER_CONDITION FilterCondition[1] = { 0 };
FWP_V4_ADDR_AND_MASK AddrAndMask = { 0 }; //0
if (g_hEngine == NULL)
break;
Filter.displayData.description = WFP_SAMPLE_FILTER_ESTABLISH_DISPLAY_NAME;
Filter.displayData.name = WFP_SAMPLE_FILTER_ESTABLISH_DISPLAY_NAME;
Filter.flags = 0;
Filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; //分层GUID
Filter.subLayerKey = WFP_SAMPLE_SUBLAYER_GUID; //子层GUID
Filter.weight.type = FWP_EMPTY; //添加到过滤引擎中后 自动分配一个权重
Filter.numFilterConditions = 1;
Filter.filterCondition = FilterCondition; //过滤器条件
Filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; //过滤器动作类型 和呼出接口关联 后者回调函数总返回允许或拦截
Filter.action.calloutKey = WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID; //呼出接口GUID
FilterCondition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; //检查字段为远程IP地址
FilterCondition[0].matchType = FWP_MATCH_EQUAL;
FilterCondition[0].conditionValue.type = FWP_V4_ADDR_MASK;
FilterCondition[0].conditionValue.v4AddrMask = &AddrAndMask; //所有网络数据包都能匹配该过滤条件
nStatus = FwpmFilterAdd(g_hEngine, &Filter, NULL, &g_uEstablishedFilterId);
if (STATUS_SUCCESS != nStatus)
break;
nStatus = STATUS_SUCCESS;
} while (FALSE);
return nStatus;
};
VOID NTAPI Wfp_Sample_Established_ClassifyFn_V4(IN const FWPS_INCOMING_VALUES* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES* inMetaValues, IN OUT VOID* layerData, IN OPTIONAL const void* classifyContext, IN const FWPS_FILTER1* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT* classifyOut) { //回调函数
WORD wDirection = 0;
WORD wRemotePort = 0;
WORD wSrcPort = 0;
WORD wProtocol = 0;
ULONG ulSrcIPAddress = 0;
ULONG ulRemoteIPAddress = 0;
if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
return;
//wDirection表示数据包的方向,取值为 //FWP_DIRECTION_INBOUND/FWP_DIRECTION_OUTBOUND
wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
//wSrcPort表示本地端口,主机序
wSrcPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16;
//wRemotePort表示远端端口,主机序
wRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16;
//ulSrcIPAddress 表示源IP
ulSrcIPAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32;
//ulRemoteIPAddress 表示远端IP
ulRemoteIPAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32;
//wProtocol表示网络协议,可以取值是IPPROTO_ICMP/IPPROTO_UDP/IPPROTO_TCP
wProtocol = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8;
//默认"允许"(PERMIT)
classifyOut->actionType = FWP_ACTION_PERMIT;
if (IsHitRule(wRemotePort))
classifyOut->actionType = FWP_ACTION_BLOCK;
//简单的策略判断,读者可以重写这部分
// if( (wProtocol == IPPROTO_TCP) && (wDirection == FWP_DIRECTION_OUTBOUND) && (wRemotePort == HTTP_DEFAULT_PORT) ) {
// //TCP协议尝试发起80端口的访问,拦截(BLOCK)
// classifyOut->actionType = FWP_ACTION_BLOCK;
// };
//清除FWPS_RIGHT_ACTION_WRITE标记
if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
return;
};

实例代码

WfpSample.c:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#define NDIS630
#include "ntddk.h"
#include "fwpmk.h"
#include "fwpsk.h"
#define INITGUID
#include <guiddef.h>
#include "WfpSample.h"
#include "Fwpmu.h"
#include "Rule.h"
//本例子只是为了介绍WFP的用法,在实际应用中,如果只是根据简单的规则拦截数据包的话,可以通过添加过滤器的方法实现。
PDEVICE_OBJECT g_pDeviceObj = NULL;
UINT32 g_uFwpsEstablishedCallOutId = 0;
UINT32 g_uFwpmEstablishedCallOutId = 0;
UINT64 g_uEstablishedFilterId = 0;
HANDLE g_hEngine = NULL;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT* DriverObject, __in PUNICODE_STRING RegistryPath) {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
UNREFERENCED_PARAMETER(RegistryPath);
do {
if (DriverObject == NULL)
break;
DriverObject->MajorFunction[IRP_MJ_CREATE] = WfpSampleIRPDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = WfpSampleIRPDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = WfpSampleIRPDispatch;
if (FALSE == InitRuleInfo())
break;
g_pDeviceObj = CreateDevice(DriverObject);
if (g_pDeviceObj == NULL)
break;
if (InitWfp() != STATUS_SUCCESS)
break;
DriverObject->DriverUnload = DriverUnload;
nStatus = STATUS_SUCCESS;
} while (FALSE);
if (nStatus != STATUS_SUCCESS) {
UninitWfp();
DeleteDevice();
UninitRuleInfo();
};
return nStatus;
};
NTSTATUS WfpSampleIRPDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS nStatus = STATUS_SUCCESS;
ULONG ulInformation = 0;
UNREFERENCED_PARAMETER(DeviceObject);
do {
PIO_STACK_LOCATION IrpStack = NULL;
PVOID pSystemBuffer = NULL;
ULONG uInLen = 0;
if (Irp == NULL)
break;
pSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
if (IrpStack == NULL)
break;
uInLen = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
if (IrpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL)
break;
// 开始处理DeivceIoControl的情况
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_WFP_SAMPLE_ADD_RULE:{
BOOLEAN bSucc = FALSE;
bSucc = AddNetRuleInfo(pSystemBuffer, uInLen);
if (bSucc == FALSE)
nStatus = STATUS_UNSUCCESSFUL;
break;
};
default:{
ulInformation = 0;
nStatus = STATUS_UNSUCCESSFUL;
};
};
} while (FALSE);
if (Irp != NULL) {
Irp->IoStatus.Information = ulInformation;
Irp->IoStatus.Status = nStatus;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
};
return nStatus;
};
PDEVICE_OBJECT CreateDevice(__in struct _DRIVER_OBJECT* DriverObject) {
UNICODE_STRING uDeviceName = { 0 };
UNICODE_STRING uSymbolName = { 0 };
PDEVICE_OBJECT pDeviceObj = NULL;
NTSTATUS nStatsus = STATUS_UNSUCCESSFUL;
RtlInitUnicodeString(&uDeviceName, WFP_DEVICE_NAME);
RtlInitUnicodeString(&uSymbolName, WFP_SYM_LINK_NAME);
nStatsus = IoCreateDevice(DriverObject, 0, &uDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObj);
if (pDeviceObj != NULL)
pDeviceObj->Flags |= DO_BUFFERED_IO;
IoCreateSymbolicLink(&uSymbolName, &uDeviceName);
return pDeviceObj;
};
VOID DeleteDevice() {
UNICODE_STRING uSymbolName = { 0 };
RtlInitUnicodeString(&uSymbolName, WFP_SYM_LINK_NAME);
IoDeleteSymbolicLink(&uSymbolName);
if (g_pDeviceObj != NULL)
IoDeleteDevice(g_pDeviceObj);
g_pDeviceObj = NULL;
};
NTSTATUS InitWfp(VOID) {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
do {
g_hEngine = OpenEngine();
if (g_hEngine == NULL)
break;
if (STATUS_SUCCESS != WfpRegisterCallouts(g_pDeviceObj))
break;
if (STATUS_SUCCESS != WfpAddCallouts())
break;
if (STATUS_SUCCESS != WfpAddSubLayer())
break;
if (STATUS_SUCCESS != WfpAddFilters())
break;
nStatus = STATUS_SUCCESS;
} while (FALSE);
return nStatus;
};
VOID UninitWfp(VOID) {
WfpRemoveFilters();
WfpRemoveSubLayer();
WfpRemoveCallouts();
WfpUnRegisterCallouts();
CloseEngine();
};
VOID DriverUnload(__in struct _DRIVER_OBJECT* DriverObject) {
UninitWfp();
DeleteDevice();
UninitRuleInfo();
return;
};
HANDLE OpenEngine(VOID) {
FWPM_SESSION0 Session = { 0 };
HANDLE hEngine = NULL;
FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &Session, &hEngine);
return hEngine;
};
VOID CloseEngine(VOID) {
if (g_hEngine != NULL)
FwpmEngineClose(g_hEngine);
g_hEngine = NULL;
return;
};
NTSTATUS WfpRegisterCalloutImple(IN OUT void* deviceObject, IN FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction, IN FWPS_CALLOUT_NOTIFY_FN NotifyFunction, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction, IN GUID const* calloutKey, IN UINT32 flags, OUT UINT32* calloutId) {
FWPS_CALLOUT sCallout;
NTSTATUS status = STATUS_SUCCESS;
memset(&sCallout, 0, sizeof(FWPS_CALLOUT));
sCallout.calloutKey = *calloutKey;
sCallout.flags = flags;
sCallout.classifyFn = ClassifyFunction;
sCallout.notifyFn = NotifyFunction;
sCallout.flowDeleteFn = FlowDeleteFunction;
status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
return status;
};
NTSTATUS WfpRegisterCallouts(IN OUT PVOID deviceObject) {
NTSTATUS status = STATUS_UNSUCCESSFUL;
do {
if (deviceObject == NULL)
break;
status = WfpRegisterCalloutImple(deviceObject, Wfp_Sample_Established_ClassifyFn_V4, Wfp_Sample_Established_NotifyFn_V4, Wfp_Sample_Established_FlowDeleteFn_V4, &WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID, 0, &g_uFwpsEstablishedCallOutId);
if (status != STATUS_SUCCESS)
break;
status = STATUS_SUCCESS;
} while (FALSE);
return status;
};
VOID WfpUnRegisterCallouts(VOID) {
FwpsCalloutUnregisterById(g_uFwpsEstablishedCallOutId);
g_uFwpsEstablishedCallOutId = 0;
return;
};
NTSTATUS WfpAddCallouts(VOID) {
NTSTATUS status = STATUS_SUCCESS;
FWPM_CALLOUT fwpmCallout = { 0 };
fwpmCallout.flags = 0;
do {
if (g_hEngine == NULL)
break;
fwpmCallout.displayData.name = (wchar_t*)WFP_SAMPLE_ESTABLISHED_CALLOUT_DISPLAY_NAME;
fwpmCallout.displayData.description = (wchar_t*)WFP_SAMPLE_ESTABLISHED_CALLOUT_DISPLAY_NAME;
fwpmCallout.calloutKey = WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID;
fwpmCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
status = FwpmCalloutAdd(g_hEngine, &fwpmCallout, NULL, &g_uFwpmEstablishedCallOutId);
if (!NT_SUCCESS(status) && (status != STATUS_FWP_ALREADY_EXISTS))
break;
status = STATUS_SUCCESS;
} while (FALSE);
return status;
};
VOID WfpRemoveCallouts() {
if (g_hEngine != NULL) {
FwpmCalloutDeleteById(g_hEngine, g_uFwpmEstablishedCallOutId);
g_uFwpmEstablishedCallOutId = 0;
};
return;
};
NTSTATUS WfpAddSubLayer() {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
FWPM_SUBLAYER SubLayer = { 0 };
SubLayer.flags = 0;
SubLayer.displayData.description = WFP_SAMPLE_SUB_LAYER_DISPLAY_NAME;
SubLayer.displayData.name = WFP_SAMPLE_SUB_LAYER_DISPLAY_NAME;
SubLayer.subLayerKey = WFP_SAMPLE_SUBLAYER_GUID;
SubLayer.weight = 65535;
if (g_hEngine != NULL)
nStatus = FwpmSubLayerAdd(g_hEngine, &SubLayer, NULL);
return nStatus;
};
VOID WfpRemoveSubLayer() {
if (g_hEngine != NULL)
FwpmSubLayerDeleteByKey(g_hEngine, &WFP_SAMPLE_SUBLAYER_GUID);
return;
};
NTSTATUS WfpAddFilters() {
NTSTATUS nStatus = STATUS_UNSUCCESSFUL;
do {
FWPM_FILTER0 Filter = { 0 };
FWPM_FILTER_CONDITION FilterCondition[1] = { 0 };
FWP_V4_ADDR_AND_MASK AddrAndMask = { 0 };
if (g_hEngine == NULL)
break;
Filter.displayData.description = WFP_SAMPLE_FILTER_ESTABLISH_DISPLAY_NAME;
Filter.displayData.name = WFP_SAMPLE_FILTER_ESTABLISH_DISPLAY_NAME;
Filter.flags = 0;
Filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
Filter.subLayerKey = WFP_SAMPLE_SUBLAYER_GUID;
Filter.weight.type = FWP_EMPTY;
Filter.numFilterConditions = 1;
Filter.filterCondition = FilterCondition;
Filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
Filter.action.calloutKey = WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID;
FilterCondition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
FilterCondition[0].matchType = FWP_MATCH_EQUAL;
FilterCondition[0].conditionValue.type = FWP_V4_ADDR_MASK;
FilterCondition[0].conditionValue.v4AddrMask = &AddrAndMask;
nStatus = FwpmFilterAdd(g_hEngine, &Filter, NULL, &g_uEstablishedFilterId);
if (STATUS_SUCCESS != nStatus)
break;
nStatus = STATUS_SUCCESS;
} while (FALSE);
return nStatus;
};
VOID WfpRemoveFilters() {
if (g_hEngine != NULL)
FwpmFilterDeleteById(g_hEngine, g_uEstablishedFilterId);
return;
};
VOID NTAPI Wfp_Sample_Established_ClassifyFn_V4(IN const FWPS_INCOMING_VALUES* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES* inMetaValues, IN OUT VOID* layerData, IN OPTIONAL const void* classifyContext, IN const FWPS_FILTER1* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT* classifyOut) {
WORD wDirection = 0;
WORD wRemotePort = 0;
WORD wSrcPort = 0;
WORD wProtocol = 0;
ULONG ulSrcIPAddress = 0;
ULONG ulRemoteIPAddress = 0;
if (!(classifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
return;
//wDirection表示数据包的方向,取值为 //FWP_DIRECTION_INBOUND/FWP_DIRECTION_OUTBOUND
wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
//wSrcPort表示本地端口,主机序
wSrcPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16;
//wRemotePort表示远端端口,主机序
wRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16;
//ulSrcIPAddress 表示源IP
ulSrcIPAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32;
//ulRemoteIPAddress 表示远端IP
ulRemoteIPAddress = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32;
//wProtocol表示网络协议,可以取值是IPPROTO_ICMP/IPPROTO_UDP/IPPROTO_TCP
wProtocol = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8;
//默认"允许"(PERMIT)
classifyOut->actionType = FWP_ACTION_PERMIT;
if (IsHitRule(wRemotePort))
classifyOut->actionType = FWP_ACTION_BLOCK;
//简单的策略判断,读者可以重写这部分
// if( (wProtocol == IPPROTO_TCP) && (wDirection == FWP_DIRECTION_OUTBOUND) && (wRemotePort == HTTP_DEFAULT_PORT) ) {
// //TCP协议尝试发起80端口的访问,拦截(BLOCK)
// classifyOut->actionType = FWP_ACTION_BLOCK;
// };
//清除FWPS_RIGHT_ACTION_WRITE标记
if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
return;
};
NTSTATUS NTAPI Wfp_Sample_Established_NotifyFn_V4(IN FWPS_CALLOUT_NOTIFY_TYPE notifyType, IN const GUID* filterKey, IN const FWPS_FILTER* filter) {
return STATUS_SUCCESS;
};
VOID NTAPI Wfp_Sample_Established_FlowDeleteFn_V4(IN UINT16 layerId, IN UINT32 calloutId, IN UINT64 flowContext) {};

WfpSample.h:

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
37
38
#pragma once
#ifndef MAX_PATH
#define MAX_PATH (260)
#endif
#define WFP_DEVICE_NAME L"\\Device\\wfp_sample_device"
#define WFP_SYM_LINK_NAME L"\\DosDevices\\wfp_sample_device"
#define WFP_SAMPLE_ESTABLISHED_CALLOUT_DISPLAY_NAME L"WfpSampleEstablishedCalloutName"
#define WFP_SAMPLE_SUB_LAYER_DISPLAY_NAME L"WfpSampleSubLayerName"
#define WFP_SAMPLE_FILTER_ESTABLISH_DISPLAY_NAME L"WfpSampleFilterEstablishName"
#define HTTP_DEFAULT_PORT 80
#define IOCTL_WFP_SAMPLE_ADD_RULE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_READ_ACCESS | FILE_WRITE_ACCESS)
// {D969FC67-6FB2-4504-91CE-A97C3C32AD36}
DEFINE_GUID(WFP_SAMPLE_ESTABLISHED_CALLOUT_V4_GUID, 0xd969fc67, 0x6fb2, 0x4504, 0x91, 0xce, 0xa9, 0x7c, 0x3c, 0x32, 0xad, 0x36);
// {ED6A516A-36D1-4881-BCF0-ACEB4C04C21C}
DEFINE_GUID(WFP_SAMPLE_SUBLAYER_GUID,0xed6a516a, 0x36d1, 0x4881, 0xbc, 0xf0, 0xac, 0xeb, 0x4c, 0x4, 0xc2, 0x1c);
/*函数原型声明*/
void DriverUnload(__in struct _DRIVER_OBJECT* DriverObject);
PDEVICE_OBJECT CreateDevice(__in struct _DRIVER_OBJECT* DriverObject);
NTSTATUS WfpSampleIRPDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS WfpRegisterCalloutImple(IN OUT void* deviceObject,IN FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction,IN FWPS_CALLOUT_NOTIFY_FN NotifyFunction,IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction,IN GUID const* calloutKey,IN UINT32 flags,OUT UINT32* calloutId);
NTSTATUS WfpRegisterCallouts(IN OUT void* deviceObject);
VOID NTAPI Wfp_Sample_Established_ClassifyFn_V4(IN const FWPS_INCOMING_VALUES0* inFixedValues,IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,IN OUT VOID* layerData,IN OPTIONAL const void* classifyContext,IN const FWPS_FILTER1* filter,IN UINT64 flowContext,OUT FWPS_CLASSIFY_OUT0* classifyOut);
NTSTATUS NTAPI Wfp_Sample_Established_NotifyFn_V4(IN FWPS_CALLOUT_NOTIFY_TYPE notifyType, IN const GUID* filterKey, IN const FWPS_FILTER* filter);
VOID NTAPI Wfp_Sample_Established_FlowDeleteFn_V4(IN UINT16 layerId, IN UINT32 calloutId, IN UINT64 flowContext);
NTSTATUS WfpAddCallouts();
NTSTATUS WfpRegisterCallouts(IN OUT void* deviceObject);
NTSTATUS WfpRegisterCalloutImple(IN OUT void* deviceObject,IN FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction,IN FWPS_CALLOUT_NOTIFY_FN NotifyFunction,IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction,IN GUID const* calloutKey,IN UINT32 flags,OUT UINT32* calloutId);
NTSTATUS WfpAddSubLayer();
NTSTATUS WfpAddFilters();
VOID WfpUnRegisterCallouts();
VOID WfpRemoveCallouts();
VOID WfpRemoveSubLayer();
VOID WfpRemoveFilters();
HANDLE OpenEngine();
void CloseEngine();
NTSTATUS InitWfp();
VOID UninitWfp();
VOID DeleteDevice();

Rule.c:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "fltkernel.h"
#include "Rule.h"
LIST_ENTRY g_WfpRuleList = { 0 };
KSPIN_LOCK g_RuleLock = 0;
BOOLEAN InitRuleInfo() {
InitializeListHead(&g_WfpRuleList);
KeInitializeSpinLock(&g_RuleLock);
return TRUE;
};
BOOLEAN UninitRuleInfo() {
do {
KIRQL OldIRQL = 0;
PLIST_ENTRY pInfo = NULL;
PST_WFP_NETINFOLIST pRule = NULL;
if (g_WfpRuleList.Blink == NULL || g_WfpRuleList.Flink == NULL)
break;
KeAcquireSpinLock(&g_RuleLock, &OldIRQL);
while (!IsListEmpty(&g_WfpRuleList)) {
pInfo = RemoveHeadList(&g_WfpRuleList);
if (pInfo == NULL)
break;
pRule = CONTAINING_RECORD(pInfo, ST_WFP_NETINFOLIST, m_linkPointer);
ExFreePoolWithTag(pRule, WFP_TAG);
pRule = NULL;
pInfo = NULL;
};
KeReleaseSpinLock(&g_RuleLock, OldIRQL);
} while (FALSE);
return TRUE;
};
BOOLEAN AddNetRuleInfo(PVOID pBuf, ULONG uLen) {
BOOLEAN bSucc = FALSE;
PST_WFP_NETINFO pRuleInfo = NULL;
do {
PST_WFP_NETINFOLIST pRuleNode = NULL;
KIRQL OldIRQL = 0;
pRuleInfo = (PST_WFP_NETINFO)pBuf;
if (pRuleInfo == NULL)
break;
if (uLen < sizeof(ST_WFP_NETINFO))
break;
pRuleNode = (PST_WFP_NETINFOLIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(ST_WFP_NETINFOLIST), WFP_TAG);
if (pRuleNode == NULL)
break;
memset(pRuleNode, 0, sizeof(ST_WFP_NETINFOLIST));
pRuleNode->m_stWfpNetInfo.m_uSrcPort = pRuleInfo->m_uSrcPort;
pRuleNode->m_stWfpNetInfo.m_uRemotePort = pRuleInfo->m_uRemotePort;
pRuleNode->m_stWfpNetInfo.m_ulSrcIPAddr = pRuleInfo->m_ulSrcIPAddr;
pRuleNode->m_stWfpNetInfo.m_ulRemoteIPAddr = pRuleInfo->m_ulRemoteIPAddr;
pRuleNode->m_stWfpNetInfo.m_ulNetWorkType = pRuleInfo->m_ulNetWorkType;
pRuleNode->m_stWfpNetInfo.m_uDirection = pRuleInfo->m_uDirection;
KeAcquireSpinLock(&g_RuleLock, &OldIRQL);
InsertHeadList(&g_WfpRuleList, &pRuleNode->m_linkPointer);
KeReleaseSpinLock(&g_RuleLock, OldIRQL);
bSucc = TRUE;
break;
} while (FALSE);
return bSucc;
};
BOOLEAN IsHitRule(USHORT uRemotePort) {
BOOLEAN bIsHit = FALSE;
do {
KIRQL OldIRQL = 0;
PLIST_ENTRY pEntry = NULL;
if (g_WfpRuleList.Blink == NULL || g_WfpRuleList.Flink == NULL)
break;
KeAcquireSpinLock(&g_RuleLock, &OldIRQL);
pEntry = g_WfpRuleList.Flink;
while (pEntry != &g_WfpRuleList) {
PST_WFP_NETINFOLIST pInfo = CONTAINING_RECORD(pEntry, ST_WFP_NETINFOLIST, m_linkPointer);
if (uRemotePort == pInfo->m_stWfpNetInfo.m_uRemotePort) {
bIsHit = TRUE;
break;
};
pEntry = pEntry->Flink;
};
KeReleaseSpinLock(&g_RuleLock, OldIRQL);
} while (FALSE);
return bIsHit;
};

Rule.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once
#define WFP_TAG 'wfpT'
#pragma pack(push)
#pragma pack(1)
typedef struct _tagWfp_NetInfo {
USHORT m_uSrcPort; //源端口
USHORT m_uRemotePort; //目标端口
ULONG m_ulSrcIPAddr; //源地址
ULONG m_ulRemoteIPAddr; //目标地址
ULONG m_ulNetWorkType; //协议
USHORT m_uDirection;//数据包的方向,0表示发送,1表示接收
} ST_WFP_NETINFO, * PST_WFP_NETINFO;
typedef struct _tagWfp_NetInfoList {
LIST_ENTRY m_linkPointer;
ST_WFP_NETINFO m_stWfpNetInfo;
}ST_WFP_NETINFOLIST, * PST_WFP_NETINFOLIST;
#pragma pack(pop)
BOOLEAN InitRuleInfo();
BOOLEAN UninitRuleInfo();
BOOLEAN AddNetRuleInfo(PVOID pRuleInfo, ULONG uLen);
BOOLEAN IsHitRule(USHORT uRemotePort);