#include <ntddk.h>
#pragma warning(push)
#pragma warning(disable:4201) // unnamed struct/union
#pragma warning(disable:4995)
#include <fwpsk.h>
#pragma warning(pop)
#include <ndis.h>
#include <fwpmk.h>
#include <limits.h>
#include <ws2ipdef.h>
#include <in6addr.h>
#include <ip2string.h>
#include <strsafe.h>
#define INITGUID
#include <guiddef.h>
#define bool BOOLEAN
#define true TRUE
#define false FALSE
#define DEVICE_NAME L"\\Device\\WFP_TEST"
#define DEVICE_DOSNAME L"\\DosDevices\\WFP_TEST"
#define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
#define kfree(_p) ExFreePool(_p)
DEFINE_GUID // {6812FC83-7D3E-499a-A012-55E0D85F348B}
(
GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
0x6812fc83,
0x7d3e,
0x499a,
0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b
);
PDEVICE_OBJECT gDevObj;
HANDLE gEngineHandle = 0;
HANDLE gInjectHandle = 0;
//CalloutId
UINT32 gAleConnectCalloutId = 0;
//FilterId
UINT64 gAleConnectFilterId = 0;
/*
以下两个回调函数没啥用
*/
NTSTATUS NTAPI WallNotifyFn
(
IN FWPS_CALLOUT_NOTIFY_TYPE notifyType,
IN const GUID *filterKey,
IN const FWPS_FILTER *filter
)
{
KdPrint(("NotifyFn\n"));
return STATUS_SUCCESS;
}
VOID NTAPI WallFlowDeleteFn
(
IN UINT16 layerId,
IN UINT32 calloutId,
IN UINT64 flowContext
)
{
KdPrint(("FlowDeleteFn\n"));
return;
}
//协议代码转为名称
char* ProtocolIdToName(UINT16 id)
{
char *ProtocolName = kmalloc(16);
RtlZeroMemory(ProtocolName,16);
switch (id)
{
case 1:
strcpy_s(ProtocolName, 4 + 1, "ICMP");
break;
case 2:
strcpy_s(ProtocolName, 4 + 1, "IGMP");
break;
case 6:
strcpy_s(ProtocolName, 3 + 1, "TCP");
break;
case 17:
strcpy_s(ProtocolName, 3 + 1, "UDP");
break;
case 27:
strcpy_s(ProtocolName, 3 + 1, "RDP");
break;
default:
strcpy_s(ProtocolName, 7 + 1, "UNKNOWN");
break;
}
return ProtocolName;
}
//最重要的过滤函数
void NTAPI WallALEConnectClassify
(
IN const FWPS_INCOMING_VALUES0* inFixedValues,//此结构包含筛选层上每个数据字段的值
//incomingValue[index] => index的值是枚举类型FWPS_FIELDS_ALE_AUTH_CONNECT_V4,
//枚举名字说明了该值的类型例如:FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS 就是本地IP
IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,//此结构包含筛选层上每个元数据字段的值。
IN OUT void* layerData,//指向结构的指针,该结构描述正在过滤的层上的原始数据。此参数可能为NULL,
//这取决于所筛选的层和调用classfyFn0标注函数的条件。
//对于流层,此参数指向FWPS_stream_Callout_IO_PACKET 0结构。
//对于所有其他层,如果NetBufferList结构不是NULL,则该参数指向该结构。
IN const void* classifyContext,//由过滤器引擎指向与标注驱动程序关联的上下文数据的指针 没有找出该指针类型
IN const FWPS_FILTER* filter,//指向FWPS_FILTER1结构的指针。此结构描述指定筛选器操作的标注的筛选器。
IN UINT64 flowContext,//包含与数据流关联的上下文的UINT 64类型变量。如果没有与数据流相关联的上下文,则此参数为零。
//如果在不支持数据流的过滤层将标注添加到筛选引擎中,则clamfyFn1回调函数应忽略此参数
OUT FWPS_CLASSIFY_OUT* classifyOut //指向FWPS_GARGY_OUT 0结构的指针,该结构接收clamfyFn1回调函数返回给调用方的任何数据
)
{
char *ProtocolName = NULL;
DWORD LocalIp, RemoteIP;
PWCHAR Path = NULL;
do
{
Path = ExAllocatePoolWithTag(NonPagedPool, inMetaValues->processPath->size+2, 'NET');
if (NULL==Path)
{
break;
}
RtlZeroMemory(Path, inMetaValues->processPath->size + 2);
wcscpy_s(Path, inMetaValues->processPath->size / 2, (WCHAR*)inMetaValues->processPath->data);
LocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
ProtocolName = ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16);
DbgPrint("进程ID : %d", (DWORD)(inMetaValues->processId));
DbgPrint("Path=%S", Path);
DbgPrint("本地地址=%u.%u.%u.%u ", (LocalIp >> 24) & 0xFF, (LocalIp >> 16) & 0xFF, (LocalIp >> 8) & 0xFF, LocalIp & 0xFF);
DbgPrint("端口%d\n", inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16);
DbgPrint("远程地址=%u.%u.%u.%u ", (RemoteIP >> 24) & 0xFF, (RemoteIP >> 16) & 0xFF, (RemoteIP >> 8) & 0xFF, RemoteIP & 0xFF);
DbgPrint("端口%d\n", inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16);
DbgPrint("协议名: %s\n", ProtocolName);
kfree(ProtocolName);
} while (0);
if (NULL != Path)
{
ExFreePoolWithTag(Path, 'NET');
}
classifyOut->actionType = FWP_ACTION_PERMIT;//允许连接
return;
}
NTSTATUS RegisterCalloutForLayer
(
IN const GUID* layerKey,
IN const GUID* calloutKey,
IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
OUT UINT32* calloutId, //注册callout时返回此ID
OUT UINT64* filterId
)
{
NTSTATUS status = STATUS_SUCCESS;
FWPS_CALLOUT sCallout = { 0 };
FWPM_FILTER mFilter = { 0 };
FWPM_FILTER_CONDITION mFilter_condition[1] = { 0 };
FWPM_CALLOUT mCallout = { 0 };
FWPM_DISPLAY_DATA mDispData = { 0 };
BOOLEAN bCalloutRegistered = FALSE;
//这里未指定sCallout.flags
sCallout.calloutKey = *calloutKey; //一个唯一的GUID值,用来标记此callout
sCallout.classifyFn = classifyFn; //每当回调处理网络数据时,过滤器引擎将调用此函数,处理网络数据在这个回调函数里面处理.
sCallout.flowDeleteFn = flowDeleteNotifyFn; //每当终止由回调处理的数据流时,筛选器引擎将调用此函数
sCallout.notifyFn = notifyFn; //卸载callout时,会调用这个回调
//要使用哪个设备对象注册
status = FwpsCalloutRegister(gDevObj, //自己创建的设备对象
&sCallout, //该结构体有用的是指定了classifyFn回调,处理连接时的数据就会用到这个回调函数.
calloutId //返回的calloutID ,取消注册callout会用到这个值
);
if (!NT_SUCCESS(status))
goto exit;
bCalloutRegistered = TRUE;
mDispData.name = L"WFP TEST"; //可选的名字
mDispData.description = L"yxp's WFP TEST"; //可选的描述
//你感兴趣的内容
mCallout.applicableLayer = *layerKey; //过滤层标识符 此过滤层允许授权对传出tcp连接的连接请求,
//以及基于发送的第一个数据包授权传出非tcp通信量
//你感兴趣的内容的GUID
mCallout.calloutKey = *calloutKey; //该GUID值必须与FwpsCalloutRegister注册时的GUID值相同.
mCallout.displayData = mDispData;
//向过滤引擎添加callout
status = FwpmCalloutAdd(gEngineHandle,
&mCallout, //flags没有设置
NULL, //安全描述符 可以为NULL
NULL); //返回一个ID 与FwpsCalloutRegister函数返回的ID是相同的.
if (!NT_SUCCESS(status))
goto exit;
//FWPM_action0结构指定在所有筛选条件都为真时所采取的操作
mFilter.action.calloutKey = *calloutKey;
//交给callout处理 由callout来决定返回阻止(block)或者允许(permit)
mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
//描述
mFilter.displayData.name = L"WFP TEST";
mFilter.displayData.description = L"yxp's WFP TEST";
mFilter.layerKey = *layerKey; //这个值和FwpmCalloutAdd里面使用的值一样 过滤层标识符 此过滤层允许授权对传出tcp连接的连接请求,
//以及基于发送的第一个数据包授权传出非tcp通信量
//过滤条件数
mFilter.numFilterConditions = 0;
//过滤条件
mFilter.filterCondition = mFilter_condition;
mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; //此子层承载所有未分配给任何其他子层的筛选器
//BFE将根据过滤条件自动分配权重
mFilter.weight.type = FWP_EMPTY;
//添加一个新的过滤器对象到系统中
status = FwpmFilterAdd(gEngineHandle, &mFilter, NULL, filterId);
if (!NT_SUCCESS(status))
goto exit;
exit:
if (!NT_SUCCESS(status))
{
if (bCalloutRegistered)
{
FwpsCalloutUnregisterById(*calloutId);
}
}
return status;
}
NTSTATUS WallRegisterCallouts()
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN bInTransaction = FALSE;
BOOLEAN bEngineOpened = FALSE;
FWPM_SESSION session = { 0 };
//置此标志时,会话结束时将自动删除在会话期间添加的任何对象
session.flags = FWPM_SESSION_FLAG_DYNAMIC;
//开启WFP引擎
status = FwpmEngineOpen(NULL,
RPC_C_AUTHN_WINNT,
NULL,
&session,
&gEngineHandle);
if (!NT_SUCCESS(status))
goto exit;
bEngineOpened = TRUE;
// 在当前会话中开始显式事务
status = FwpmTransactionBegin(gEngineHandle, 0);
if (!NT_SUCCESS(status))
goto exit;
bInTransaction = TRUE;
//注册回调函数
status = RegisterCalloutForLayer(
&FWPM_LAYER_ALE_AUTH_CONNECT_V4, //此过滤层允许授权对传出tcp连接的连接请求,
//以及基于发送的第一个数据包授权传出非tcp通信量
&GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
WallALEConnectClassify,
WallNotifyFn,
WallFlowDeleteFn,
&gAleConnectCalloutId,
&gAleConnectFilterId);
if (!NT_SUCCESS(status))
{
DbgPrint("RegisterCalloutForLayer失败!\n");
goto exit;
}
//确认所有内容并提交,让回调函数正式发挥作用
status = FwpmTransactionCommit(gEngineHandle);
if (!NT_SUCCESS(status))
goto exit;
bInTransaction = FALSE;
exit:
if (!NT_SUCCESS(status))
{
if (bInTransaction)
{
FwpmTransactionAbort(gEngineHandle);
}
if (bEngineOpened)
{
FwpmEngineClose(gEngineHandle);
gEngineHandle = 0;
}
}
return status;
}
NTSTATUS WallUnRegisterCallouts()
{
if (gEngineHandle != 0)
{
//删除FilterId
FwpmFilterDeleteById(gEngineHandle, gAleConnectFilterId);
//删除CalloutId
FwpmCalloutDeleteById(gEngineHandle, gAleConnectCalloutId);
//清空FilterId
gAleConnectFilterId = 0;
//反注册CalloutId
FwpsCalloutUnregisterById(gAleConnectCalloutId);
//清空CalloutId
gAleConnectCalloutId = 0;
//关闭引擎
FwpmEngineClose(gEngineHandle);
gEngineHandle = 0;
}
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT driverObject)
{
NTSTATUS status;
UNICODE_STRING deviceDosName = { 0 };
status = WallUnRegisterCallouts();
if (!NT_SUCCESS(status))
{
DbgPrint("WallUnRegisterCallouts失败\n");
return;
}
RtlInitUnicodeString(&deviceDosName, DEVICE_DOSNAME);
IoDeleteSymbolicLink(&deviceDosName);
if (gDevObj)
{
IoDeleteDevice(gDevObj);
gDevObj = NULL;
}
DbgPrint("驱动卸载成功\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING registryPath)
{
UNICODE_STRING deviceName = { 0 };
UNICODE_STRING deviceDosName = { 0 };
NTSTATUS status = STATUS_SUCCESS;
driverObject->DriverUnload = DriverUnload;
RtlInitUnicodeString(&deviceName, DEVICE_NAME);
status = IoCreateDevice(driverObject,
0,
&deviceName,
FILE_DEVICE_NETWORK,
0,
FALSE,
&gDevObj);
if (!NT_SUCCESS(status))
{
DbgPrint("创建设备对象失败\n");
return STATUS_UNSUCCESSFUL;
}
RtlInitUnicodeString(&deviceDosName, DEVICE_DOSNAME);
status = IoCreateSymbolicLink(&deviceDosName, &deviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败\n");
return STATUS_UNSUCCESSFUL;
}
status = WallRegisterCallouts();
if (!NT_SUCCESS(status))
{
DbgPrint("WallRegisterCallouts 失败!\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("过滤驱动加载成功\n");
return status;
}
一个完整的WFP驱动(详细注释)
最新推荐文章于 2023-10-26 00:14:44 发布
本文详细介绍了如何利用Windows Filtering Platform (WFP) 创建一个网络过滤驱动,该驱动注册了一个回调函数,用于在应用程序尝试建立连接时进行拦截和分析。驱动在注册回调函数`WallALEConnectClassify`时,会检查连接的进程信息、IP地址、端口号和协议,并打印相关信息。此外,还展示了如何注册和注销callout以及过滤器,实现对网络流量的精细控制。
摘要由CSDN通过智能技术生成