0%

使用WFP实现数据包过滤(2)

WFP框架简介

在VISTA及以上的系统中,微软提供了对 TDI 封装的 WFP 框架,简化了处理逻辑。WFP 把数据包处理流程
划分了许多个层,在我们关注的 layer 上注册 calloutfilter ,就可以针对相应的操作进行处理

这些层的 GUID 可以在 fwpmk.h 头文件中找到定义,另外微软给出了数据传输时所流经的层:
(TCP Packet Flows) https://docs.microsoft.com/en-us/windows/win32/fwp/tcp-packet-flows
(UDP Packet Flows) https://docs.microsoft.com/en-us/windows/win32/fwp/udp-packet-flows
所有非TCP的处理方式全归到UDP中,比如ICMP会依照UDP流程进行处理

根据微软提供的数据传输流程,我们参考WDK例子中的 src\network\trans\ddproxy 项目来进行修改
(1) 首先要在ALE层获取建立连接的进程信息

1
2
3
4
5
// 完成连接的层(不含握手阶段)
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
// 发起连接或接受连接的层
FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
FWPM_LAYER_ALE_AUTH_CONNECT_V4

(2) 获取进程信息后,存储到 FlowContext 并绑定到数据处理层

1
2
3
4
// TCP数据层
FWPM_LAYER_STREAM_V4
// UDP数据层
FWPM_LAYER_DATAGRAM_DATA_V4

注意:如果绑定 TRANSPORT 层会先于 ESTABLISHED 抓到的UDP第1个包,所以就没有进程信息

相关函数和结构体

我们以在 WIN7 目标系统运行为例,注册时所使用的相关函数的通用定义为

1
2
3
4
5
6
7
NTSTATUS NTAPI FwpmEngineOpen(
_In_opt_ const wchar_t *serverName,
_In_ UINT32 authnService,
_In_opt_ SEC_WINNT_AUTH_IDENTITY_W *authIdentity,
_In_opt_ const FWPM_SESSION *session,
_Out_ HANDLE *engineHandle
); // 打开WFP过滤引擎
1
2
3
4
5
NTSTATUS NTAPI FwpsCalloutRegister(
_Inout_ void *deviceObject,
_In_ const FWPS_CALLOUT *callout,
_Out_opt_ UINT32 *calloutId
); // 注册呼出接口函数
1
2
3
4
5
6
NTSTATUS NTAPI FwpmCalloutAdd(
_In_ HANDLE engineHandle,
_In_ const FWPM_CALLOUT *callout,
_In_opt_ PSECURITY_DESCRIPTOR sd,
_Out_opt_ UINT32 *id
); // 添加呼出接口层
1
2
3
4
5
6
DWORD WINAPI FwpmFilterAdd(
_In_ HANDLE engineHandle,
_In_ const FWPM_FILTER *filter,
_In_opt_ SECURITY_DESCRIPTOR sd,
_Out_opt_ UINT64 *id
); // 添加过滤规则

以上几个函数的参数中,还有3个关键的结构体参数

1
2
3
4
5
6
7
typedef struct FWPS_CALLOUT0_ {
GUID calloutKey; // 注册时提供的Callout唯一ID
UINT32 flags;
FWPS_CALLOUT_CLASSIFY_FN0 classifyFn; // 每当有网络数据要处理时,筛选器引擎都会调用此函数
FWPS_CALLOUT_NOTIFY_FN0 notifyFn;
FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN0 flowDeleteFn; // 每当调用项处理的数据流终止时,筛选器引擎都会调用此函数
} FWPS_CALLOUT0; // 设置回调函数
1
2
3
4
5
6
7
8
9
typedef struct FWPM_CALLOUT0_ {
GUID calloutKey; // 与上边的Callout唯一ID相同
FWPM_DISPLAY_DATA0 displayData; // 用于描述Callout的字符串
UINT32 flags;
GUID *providerKey;
FWP_BYTE_BLOB providerData;
GUID applicableLayer; // 当前Callout要应用到哪个层上
UINT32 calloutId;
} FWPM_CALLOUT0; // 设置想要注册的层
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; // 初始化为0,添加到引擎时,会自动生成
FWPM_DISPLAY_DATA0 displayData; // 用于描述Filter的字符串
UINT32 flags;
GUID *providerKey;
FWP_BYTE_BLOB providerData;
GUID layerKey; // 当前Filter要应用到哪个层上
GUID subLayerKey; // 使用通用子层
FWP_VALUE0 weight; // 权重,设置为FWP_EMPTY,引擎会自动分配
UINT32 numFilterConditions; // 过滤条件的数量
FWPM_FILTER_CONDITION0 *filterCondition; // 过滤条件的内容
FWPM_ACTION0 action; // 要行使的规则,允许或阻止,或由Callout返回规则
union {
UINT64 rawContext;
GUID providerContextKey;
};
GUID *reserved;
UINT64 filterId;
FWP_VALUE0 effectiveWeight;
} FWPM_FILTER0; // 设置对应层的过滤规则

初始化全局变量

定义和初始化相关全局变量,使用 VS2015 + WDK10.0.15063.0 平台,目标运行在 WIN7 及以上系统

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
#define NDIS_WDM 1
#define NDIS620 1
#include <guiddef.h>
#include <fwpmk.h>
#include <fwpsk.h>
// 在附加依赖项中添加
// $(DDK_LIB_PATH)fwpkclnt.lib
// 结构体定义
typedef struct _REGISTER_INFO {
GUID FwpmLayerGuid;
GUID FwpmSubLayerGuid;
GUID FwpsCalloutGuid;
UINT32 FwpsCalloutID;
UINT32 FwpmCalloutID;
UINT64 FwpmFilterID;
} REGISTER_INFO, *PREGISTER_INFO;
typedef struct _FLOWCXT_ENTRY {
LIST_ENTRY Entry;
UINT64 flowId;
UINT16 layerId;
UINT32 calloutId;
UINT64 Pid;
WCHAR Path[MAX_PATH];
} FLOWCXT_ENTRY, *PFLOWCXT_ENTRY;
// 全局变量
KSPIN_LOCK MyFlowCtxLock = 0;
LIST_ENTRY MyFlowCtxList = { 0 }; // 卸载时要释放FlowContext
PDEVICE_OBJECT MyNetDevice = NULL;
REGISTER_INFO MyConnect = {
// FWPM_LAYER_ALE_AUTH_CONNECT_V4
{ 0xc38d57d1, 0x05a7, 0x4c33, 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82 },
// FWPM_SUBLAYER_UNIVERSAL
{ 0xeebecc03, 0xced4, 0x4380, 0x81, 0x9a, 0x27, 0x34, 0x39, 0x7b, 0x2b, 0x74 },
// 自定义的GUID
{ 0xd969fc61, 0x6fb2, 0x4504, 0x91, 0xce, 0xa9, 0x7c, 0x3c, 0x32, 0xad, 0x36 } };
REGISTER_INFO MyDatagram = {
// FWPM_LAYER_DATAGRAM_DATA_V4
{ 0x3d08bf4e, 0x45f6, 0x4930, 0xa9, 0x22, 0x41, 0x70, 0x98, 0xe2, 0x00, 0x27 },
// FWPM_SUBLAYER_UNIVERSAL
{ 0xeebecc03, 0xced4, 0x4380, 0x81, 0x9a, 0x27, 0x34, 0x39, 0x7b, 0x2b, 0x74 },
// 自定义的GUID
{ 0xd969fc62, 0x6fb2, 0x4504, 0x91, 0xce, 0xa9, 0x7c, 0x3c, 0x32, 0xad, 0x36 } };

注册过滤引擎

实现一个过滤功能需要:先注册Callout层,再添加Callout层到引擎中,最后再绑定Fillter并添加到引擎中

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
NTSTATUS AddFilterEngine(PREGISTER_INFO RegInfo, BOOLEAN UseFlowCtx)
{
HANDLE FwpmEngine = NULL;
FWPM_SESSION FwpmSession = { 0 };
FWPS_CALLOUT FwpsCallout = { 0 };
FWPM_CALLOUT FwpmCallout = { 0 };
FWPM_FILTER FwpmFilter = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
// 打开WFP引擎
Status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &FwpmSession, &FwpmEngine);
if (!NT_SUCCESS(Status)) return Status;
do
{
// 注册呼出接口函数
FwpsCallout.calloutKey = RegInfo->FwpsCalloutGuid;
FwpsCallout.classifyFn = FwpsCalloutClassifyFn;
FwpsCallout.notifyFn = FwpsCalloutNotifyFn;
if (UseFlowCtx)
{
FwpsCallout.flowDeleteFn = FwpsCalloutFlowDeleteFn;
FwpsCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
}
Status = FwpsCalloutRegister(MyNetDevice, &FwpsCallout, &RegInfo->FwpsCalloutID);
if (!NT_SUCCESS(Status)) break;
// 添加呼出接口层
FwpmCallout.displayData.name = L"FwpmCallout";
FwpmCallout.displayData.description = L"FwpmCallout";
FwpmCallout.calloutKey = RegInfo->FwpsCalloutGuid;
FwpmCallout.applicableLayer = RegInfo->FwpmLayerGuid;
Status = FwpmCalloutAdd(FwpmEngine, &FwpmCallout, NULL, &RegInfo->FwpmCalloutID);
if (!NT_SUCCESS(Status)) break;
// 添加过滤规则
FwpmFilter.displayData.name = L"FwpmFilter";
FwpmFilter.displayData.description = L"FwpmFilter";
FwpmFilter.layerKey = RegInfo->FwpmLayerGuid;
FwpmFilter.subLayerKey = RegInfo->FwpmSubLayerGuid;
FwpmFilter.weight.type = FWP_EMPTY;
FwpmFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
FwpmFilter.action.calloutKey = RegInfo->FwpsCalloutGuid;
Status = FwpmFilterAdd(FwpmEngine, &FwpmFilter, NULL, &RegInfo->FwpmFilterID);
if (!NT_SUCCESS(Status)) break;
} while (0);

if (!NT_SUCCESS(Status)) DelFilterEngine(RegInfo);
FwpmEngineClose(FwpmEngine);
return Status;
}

删除过滤功能的函数

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
NTSTATUS DelFilterEngine(PREGISTER_INFO RegInfo)
{
HANDLE FwpmEngine = NULL;
FWPM_SESSION FwpmSession = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
Status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &FwpmSession, &FwpmEngine);
if (!NT_SUCCESS(Status)) return Status;
if (RegInfo->FwpmFilterID)
{
FwpmFilterDeleteById(FwpmEngine, RegInfo->FwpmFilterID);
RegInfo->FwpmFilterID = 0;
}
if (RegInfo->FwpmCalloutID)
{
FwpmCalloutDeleteById(FwpmEngine, RegInfo->FwpmCalloutID);
RegInfo->FwpmCalloutID = 0;
}
if (RegInfo->FwpsCalloutID)
{
FwpsCalloutUnregisterById(RegInfo->FwpsCalloutID);
RegInfo->FwpsCalloutID = 0;
}
FwpmEngineClose(FwpmEngine);
return STATUS_SUCCESS;
}

相关回调函数

如下为注册Callout层时提供的相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void NTAPI FwpsCalloutFlowDeleteFn(
IN UINT16 layerId,
IN UINT32 calloutId,
IN UINT64 flowContext)
{
NTSTATUS Status = STATUS_SUCCESS;
PFLOWCXT_ENTRY FlowCtxEntry = (PFLOWCXT_ENTRY)flowContext;
if (!FlowCtxEntry) return;
if (!FlowCtxEntry->Entry.Flink || !FlowCtxEntry->Entry.Blink) return;
Status = FwpsFlowRemoveContext(FlowCtxEntry->flowId, layerId, calloutId);
if (!NT_SUCCESS(Status)) return;
RemoveFlowCtxList(FlowCtxEntry);
ExFreePool(FlowCtxEntry);
}
1
2
3
4
5
6
7
8
9
10
NTSTATUS NTAPI FwpsCalloutNotifyFn(
IN FWPS_CALLOUT_NOTIFY_TYPE notifyType,
IN const GUID *filterKey,
IN FWPS_FILTER *filter)
{
UNREFERENCED_PARAMETER(notifyType);
UNREFERENCED_PARAMETER(filterKey);
UNREFERENCED_PARAMETER(filter);
return STATUS_SUCCESS;
}
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
void NTAPI FwpsCalloutClassifyFn(
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_FILTER *filter,
IN UINT64 flowContext,
IN OUT FWPS_CLASSIFY_OUT *classifyOut)
{
UNREFERENCED_PARAMETER(classifyContext);
UNREFERENCED_PARAMETER(filter);
UNREFERENCED_PARAMETER(flowContext);
// 设置允许动作,并清除操作标志位
classifyOut->actionType = FWP_ACTION_PERMIT;
if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
classifyOut->rights &= (~FWPS_RIGHT_ACTION_WRITE);
// 判断触发的哪个层
switch (inFixedValues->layerId)
{
case FWPS_LAYER_DATAGRAM_DATA_V4: // 24
DbgPrint("FWPS_LAYER_DATAGRAM_DATA_V4\n");
// 根据flowContext决定是否拦截
// classifyOut->actionType = FWP_ACTION_BLOCK;
break;
case FWPS_LAYER_ALE_AUTH_CONNECT_V4: // 48
DbgPrint("FWPS_LAYER_ALE_AUTH_CONNECT_V4\n");
// 申请流上下文并存入链表中
AllocateFlowContext(inMetaValues, FWPS_LAYER_DATAGRAM_DATA_V4, MyDatagram.FwpsCalloutID);
break;
}
// 设置允许动作,并清除操作标志位
classifyOut->actionType = FWP_ACTION_PERMIT;
if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
classifyOut->rights &= (~FWPS_RIGHT_ACTION_WRITE);
}

流上下文链表操作

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
NTSTATUS AllocateFlowContext(
IN const FWPS_INCOMING_METADATA_VALUES *inMetaValues,
IN FWPS_BUILTIN_LAYERS FwpsLayerID,
IN UINT32 FwpsCalloutID)
{
PFLOWCXT_ENTRY FlowCtxEntry = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
if (!inMetaValues) return STATUS_UNSUCCESSFUL;
// 检测信息是否有效
if (!FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_FLOW_HANDLE))
return STATUS_UNSUCCESSFUL;
if (!FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PROCESS_ID))
return STATUS_UNSUCCESSFUL;
if (!FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PROCESS_PATH))
return STATUS_UNSUCCESSFUL;
// 申请流上下文内容
FlowCtxEntry = ExAllocatePool(NonPagedPoolNx, sizeof(FLOWCXT_ENTRY));
if (!FlowCtxEntry) return STATUS_UNSUCCESSFUL;
// 初始化需要的信息
RtlZeroMemory(FlowCtxEntry, sizeof(FLOWCXT_ENTRY));
FlowCtxEntry->flowId = inMetaValues->flowHandle;
FlowCtxEntry->layerId = FwpsLayerID;
FlowCtxEntry->calloutId = FwpsCalloutID;
FlowCtxEntry->Pid = inMetaValues->processId;
RtlCopyMemory(FlowCtxEntry->Path, inMetaValues->processPath->data, inMetaValues->processPath->size);
DbgPrint("flowId:%llu layerId:%u calloutId:%u pid:%llu\n path:%ws\n",
FlowCtxEntry->flowId,FlowCtxEntry->layerId, FlowCtxEntry->calloutId, FlowCtxEntry->Pid, FlowCtxEntry->Path);
// 关联流上下文到目标层中
Status = FwpsFlowAssociateContext(
inMetaValues->flowHandle, FlowCtxEntry->layerId, FlowCtxEntry->calloutId, (ULONG64)FlowCtxEntry);
if (NT_SUCCESS(Status))
InsertFlowCtxList(FlowCtxEntry);
else
ExFreePool(FlowCtxEntry);
return Status;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOLEAN RemoveFlowCtxList(PFLOWCXT_ENTRY FlowCtxEntry)
{
KIRQL OldIrql = 0;
BOOLEAN Result = FALSE;
if (!FlowCtxEntry) return FALSE;
if (!FlowCtxEntry->Entry.Blink) return FALSE;
if (!FlowCtxEntry->Entry.Flink) return FALSE;
ExAcquireSpinLock(&MyFlowCtxLock, &OldIrql);
Result = RemoveEntryList(&FlowCtxEntry->Entry);
ExReleaseSpinLock(&MyFlowCtxLock, OldIrql);
FlowCtxEntry->Entry.Blink = NULL;
FlowCtxEntry->Entry.Flink = NULL;
return Result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VOID DestoryFlowCtxList()
{
KIRQL OldIrql = 0;
PLIST_ENTRY Entry = NULL;
PFLOWCXT_ENTRY FlowCtxEntry = NULL;
NTSTATUS Status = STATUS_SUCCESS;
while (!IsListEmpty(&MyFlowCtxList))
{
ExAcquireSpinLock(&MyFlowCtxLock, &OldIrql);
Entry = RemoveHeadList(&MyFlowCtxList);
ExReleaseSpinLock(&MyFlowCtxLock, OldIrql);
if (Entry == &MyFlowCtxList) break; // 链表为空
FlowCtxEntry = CONTAINING_RECORD(Entry, FLOWCXT_ENTRY, Entry);
FlowCtxEntry->Entry.Blink = NULL;
FlowCtxEntry->Entry.Flink = NULL;
Status = FwpsFlowRemoveContext(
FlowCtxEntry->flowId, FlowCtxEntry->layerId, FlowCtxEntry->calloutId);
}
}

驱动的入口函数

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
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(RegistryPath);
DbgPrint("Hello\n");
// 初始化链表
InitializeListHead(&MyFlowCtxList);
KeInitializeSpinLock(&MyFlowCtxLock);
DriverObject->DriverUnload = DriverUnload;
// 创建网络设备
Status = IoCreateDevice(DriverObject, 0, NULL,
FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &MyNetDevice);
if (!NT_SUCCESS(Status)) return Status;
// 注册过滤引擎
do
{
Status = AddFilterEngine(&MyDatagram, TRUE);
if (!NT_SUCCESS(Status)) break;
Status = AddFilterEngine(&MyConnect, FALSE);
if (!NT_SUCCESS(Status)) break;
} while (0);
// 检查是否失败
if (!NT_SUCCESS(Status))
{
DelFilterEngine(&MyDatagram);
DelFilterEngine(&MyConnect);
IoDeleteDevice(MyNetDevice);
}
return Status;
}

在驱动卸载时,注意要主动释放流上下文

1
2
3
4
5
6
7
8
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DelFilterEngine(&MyConnect);
DestoryFlowCtxList();
DelFilterEngine(&MyDatagram);
IoDeleteDevice(MyNetDevice);
}