一个完整的WFP驱动(详细注释)

本文详细介绍了如何利用Windows Filtering Platform (WFP) 创建一个网络过滤驱动,该驱动注册了一个回调函数,用于在应用程序尝试建立连接时进行拦截和分析。驱动在注册回调函数`WallALEConnectClassify`时,会检查连接的进程信息、IP地址、端口号和协议,并打印相关信息。此外,还展示了如何注册和注销callout以及过滤器,实现对网络流量的精细控制。
摘要由CSDN通过智能技术生成
#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;
}

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值