WFP用来扩展NDIS Filter引擎的功能,完成更加复杂的数据包操作。
WFP提供的主要扩展机制是Callout Driver。
通过Callout Driver 向Filter 引擎注册一个或者多个Callout。
Filter引擎通过Filter关联的Callout,来决定何时调用Callout的函数。
每个Filter规则都工作在一个特定的 Filtering Layer。
TCP/IP 网络栈中预先定义了若干 Filtering Layer,开发者无法添加和删除 Filtering Layer。
TCP/IP 网络栈处于硬件设备网卡和用户态软件应用之间。当数据包进入网卡并向上流往用户应用程序时,会经过一系列的网络栈进行封包/解包等处理。WFP使用网络栈中预先定义的一些 Filtering Layer,实现对网络包的复杂过滤操作。
Filter Engine 是WFP的核心,负责管理Filter和Callout。
Filtering Layer 表示 TCP/IP 网络栈中的一个可以过滤的点。
每一个 Filtering Layer 可以访问数据包的特定字段。
用户态的 Filtering Layer 识别符,每个用128 bit的GUID代表,名字格式 FWPM_XXX。列表如下:
Management Filtering Layer Identifiers
内核态的 Filtering Layer识别符,每个用64 bit的LUID代表,名字格式 FWPS_XXX。列表如下:
Run-time Filtering Layer Identifiers
开发者可以为 Filtering Layer 创建多个子层(sub-layers),以方便过滤决策。
每个 Callout 有一个GUID,及三个Callout函数。
1
2
3
4
5
6
7
typedef struct FWPS_CALLOUT0_ {
GUID calloutKey;
UINT32 flags;
FWPS_CALLOUT_CLASSIFY_FN0 classifyFn;
FWPS_CALLOUT_NOTIFY_FN0 notifyFn;
FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn;
} FWPS_CALLOUT0;
Win7使用 FWPS_CALLOUT1, Win8使用FWPS_CALLOUT2,及相应编号的注册函数。参考 WFP Version-Independent Names and Targeting Specific Versions of Windows
通过调用 FwpsCalloutRegister0 把 Callout 注册到 Filter 引擎。注册成功后,返回 CalloutId,后续的操作,如注销Callout等,需要该Id。
1
2
3
4
5
6
status =
FwpsCalloutRegister0(
deviceObject,
&Callout,
&CalloutId
);
每个 Callout Driver 可以注册多个Callout,每个Callout都需要调用一次 FwpsCalloutRegister0。
原型:
1
2
3
4
5
NTSTATUS NTAPI notifyFn0(
_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
_In_ const GUID *filterKey,
_In_ const FWPS_FILTER0 *filter
)
发生跟Callout相关联的事件时,Filter引擎会回调该函数。目前的事件有:
添加了一条使用了该Callout的Filter。
一条使用该Callout的Filter被移除。
原型:
1
2
3
4
5
6
7
8
void NTAPI classifyFn0(
_In_ const FWPS_INCOMING_VALUES0 *inFixedValues,
_In_ const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
_Inout_ void *layerData,
_In_ const FWPS_FILTER0 *filter,
_In_ UINT64 flowContext,
_Inout_ FWPS_CLASSIFY_OUT0 *classifyOut
)
当数据包满足Filter的条件,Filter引擎会回调该函数,把数据包交给Callout处理。
原型:
1
2
3
4
5
void NTAPI flowDeleteFn(
_In_ UINT16 layerId,
_In_ UINT32 calloutId,
_In_ UINT64 flowContext
)
当关联了一个Context的数据流停止时,Filter引擎回调该函数,通知Callout。
Filter 代表一条过滤规则,描述了过滤条件、挂接的Flitering Layer、及满足条件后采取的行动。
原型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct FWPM_FILTER0_ {
GUID filterKey;
FWPM_DISPLAY_DATA0 displayData;
UINT32 flags;
GUID *providerKey;
FWP_BYTE_BLOB providerData;
GUID layerKey;
GUID subLayerKey;
FWP_VALUE0 weight;
UINT32 numFilterConditions;
FWPM_FILTER_CONDITION0 *filterCondition;
FWPM_ACTION0 action;
union {
UINT64 rawContext;
GUID providerContextKey;
};
GUID *reserved;
UINT64 filterId;
FWP_VALUE0 effectiveWeight;
} FWPM_FILTER0;
filterCondition 是一个过滤条件数组,条目数由 numFilterConditions 指定。
如下代码设定初始化了一个过滤条件。
1
2
3
4
5
6
FWPM_FILTER_CONDITION0 conds[1];
// ICMP Type == Destination Unreachable.
conds[0].fieldKey = FWPM_CONDITION_ICMP_TYPE;
conds[0].matchType = FWP_MATCH_EQUAL;
conds[0].conditionValue.type = FWP_UINT16;
conds[0].conditionValue.uint16 = 3;
action 表示对满足过滤条件的数据采取的行动。可以通过calloutkey 把 Callout 指定为 action。
1
2
3
4
5
6
7
typedef struct FWPM_ACTION0_ {
FWP_ACTION_TYPE type;
union {
GUID filterType;
GUID calloutKey;
};
} FWPM_ACTION0;
1
2
3
4
5
6
7
8
9
10
11
12
13
result = FwpmEngineOpen0(
NULL,
RPC_C_AUTHN_WINNT,
NULL,
NULL,
&engineHandle );
result = FwpmTransactionBegin0(engine, 0);
EXIT_ON_ERROR(FwpmTransactionBegin0);
result = FwpmFilterAdd0(engine, &filter, NULL, NULL);
EXIT_ON_ERROR(FwpmFilterAdd0);
result = FwpmTransactionCommit0(engine);
EXIT_ON_ERROR(FwpmTransactionCommit0);
当流量抵达 Filtering Layer 后,会按照sub-layers的权重,依次流经每个sub-layer。 每个sub-layer对流量返回 Permit, Block, Continue 或者没有匹配到任何条件。 即使高权重的sub-layer已经返回了 Block ,依然会调用后面的 sub-layer,以保证到达 Filtering Layer 的流量通过每个 sub-layer。 这样做的好处是,不会出现观察者类型的Filter(比如log)漏掉记录。
然后过滤引擎综合所有sub-layer的结果,依据预定义的策略,得出最终结果。 预定义策略遵循[1]:
过滤决策流程图 外面的大框表示Filtering Layer, 里面的小框表示sub-layer 每个sub-layer包含一些过滤规则
[1] Filter Arbitration https://msdn.microsoft.com/en-us/library/aa364008(v=vs.85).aspx