应用层读取wfp防火墙阻断记录

前言

之前的文档中,描写了如何对WFP防火墙进行操作[链接在此],这篇文档中,描述如何获取WFP防火墙进行阻断的操作记录。

需要注意的坑点

  • 使用FWPM_NET_EVENT_TYPE获取防火墙日志时,需要注意,只有丢弃和内核丢弃,以及几个错误信息是所有操作系统都可用,其它类型无法应用到所有操作系统。所以我们一般不会使用FWPM_NET_EVENT_TYPE来获取防火墙的放行日志。如果有需要读取放行日志的话,可以查询安全日志5156的方式进行(下次专门写文档来描述常用的三种读取方式)。
  • 使用主动读取的方式,在极个别系统上可能存在少量内存泄露(在保障所有内存都按照MSDN的要求释放的基础上,24小时有几十K的级别,没有仔细进行过日志条数的量化。有明白的也请指教)
  • 订阅的方式仅支持win7/2008 R2以上操作系统,建议使用loadlibrary动态加载,避免模块在vista或2008(SP2)操作系统上整体运行失败。

读取阻断日志(方案一:主动读取)

阻断日志即防火墙返回FWP_ACTION_BLOCK动作的记录信息,读取流程大体上分为:创建枚举句柄、执行枚举、释放枚举句柄简单的三步。

创建枚举句柄

DWORD FwpmNetEventCreateEnumHandle0( 
                            HANDLE engineHandle, 
                            const FWPM_NET_EVENT_ENUM_TEMPLATE0 *enumTemplate, 
                            HANDLE *enumHandle 
                            );
  • 输入参数:
    • engineHandle:防火墙句柄,前一章提到过,使用FwpmEngineOpen0打开
    • enumTemplate:枚举的限制条件,常用的是枚举起止时间,也可以设置网络条件以供筛选。
    • enumHandle:输出函数,用于输出枚举句柄
  • 输出参数:
    • 成功返回0,错误时返回WFP Error Codes

执行枚举操作

DWORD FwpmNetEventEnum0( 
                        HANDLE engineHandle, 
                        HANDLE enumHandle, 
                        UINT32 numEntriesRequested, 
                        FWPM_NET_EVENT0 ***entries, 
                        UINT32 *numEntriesReturned 
                        );
  • 输入参数:
    • engineHandle:防火墙句柄,FwpmEngineOpen0打开
    • enumHandle:枚举句柄,由上一步创建
    • numEnteiesRequested:本次想要枚举的最大数量,如果想要一次性返回所有,可以传-1(0xffffffff)
    • entries:用于输出事件数组,需要使用FwpmFreeMemory0释放
    • numEntriesReturned:用于输出事件数目,用于枚举entries
  • 输出参数:
    • 成功返回0,错误时返回WFP Error Codes

释放枚举句柄

DWORD FwpmNetEventDestroyEnumHandle0( 
                            HANDLE engineHandle, 
                            HANDLE enumHandle 
                            );
  • 输入参数:
    • 没啥好说的,两个句柄,参照上面
  • 输出参数:
    • 同上

相关函数1:启用防火墙事件采集

默认防火墙事件采集就是处于开启状态,因此不调用这个函数在大多数系统上可以正常进行事件读取。但是也发现过某些系统需要调用后才可以进行事件读取,建议还是增加此函数的调用

DWORD FwpmEngineSetOption0( 
                HANDLE engineHandle, 
                FWPM_ENGINE_OPTION option, 
                const FWP_VALUE0 *newValue 
                );
  • 输入参数
    • engineHandle:防火墙句柄,由FwpmEngineOpen0打开
    • 需要设置的参数,这个地方可以设置的类型还是蛮多的,可以参考
    • 设置参数对应的值,当option为FWPM_ENGINE_COLLECT_NET_EVENTS时,newValue为1则代表启用。
  • 输出参数
    • 成功返回ERROR_SUCCESS, 失败返回 WFP Error Codes

参考代码

void enumDemoCode()
{
	FWPM_SESSION0 session = { 0 };
	ZeroMemory(&session, sizeof(session));
	session.displayData.name = demoSession;
	session.txnWaitTimeoutInMSec = INFINITE;
	session.flags = FWPM_SESSION_FLAG_DYNAMIC;

	HANDLE hEngine = NULL;
	DWORD dwResult = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &hEngine);
	if (dwResult != ERROR_SUCCESS || hEngine == NULL)
	{
		std::cout << "FwpmEngineOpen0 Failed. ec:" << GetLastError() << std::endl;
		return;
	}

	FWP_VALUE0 inValue = {FWP_EMPTY};
	inValue.type = FWP_UINT32;
	inValue.uint32 = 1;
	FwpmEngineSetOption0(hEngine, FWPM_ENGINE_COLLECT_NET_EVENTS, &inValue);

	FWPM_NET_EVENT_ENUM_TEMPLATE0 enumTemplate = {0};

	SYSTEMTIME nowSystemTime;
	ZeroMemory(&nowSystemTime, sizeof(nowSystemTime));
	GetLocalTime(&nowSystemTime);

	FILETIME nowFileTime;
	ZeroMemory(&nowFileTime, sizeof(nowFileTime));
	if (SystemTimeToFileTime(&nowSystemTime, &nowFileTime))
	{
		enumTemplate.endTime.dwLowDateTime = nowFileTime.dwLowDateTime;
		enumTemplate.endTime.dwHighDateTime = nowFileTime.dwHighDateTime;
	}
	enumTemplate.startTime.dwHighDateTime = 0;
	enumTemplate.startTime.dwLowDateTime = 0;
	enumTemplate.numFilterConditions = 0;

	HANDLE hEnumHandle = NULL;
	dwResult = FwpmNetEventCreateEnumHandle0(hEngine, &enumTemplate, &hEnumHandle);
	if (dwResult != ERROR_SUCCESS || hEnumHandle == NULL)
	{
		std::cout << "Create Enum Handle Failed, ec:" << GetLastError() << std::endl;
		FwpmEngineClose0(hEngine);
		hEngine = NULL;
		return;
	}

	FWPM_NET_EVENT0** netEvents = NULL;
	UINT32 uEventNumber = 0;
	dwResult = FwpmNetEventEnum0(hEngine, hEnumHandle, INFINITE, &netEvents, &uEventNumber);
	if (uEventNumber > 0)
	{
		for (int eventPos = 0; eventPos < uEventNumber; eventPos++)
		{
			FWPM_NET_EVENT0* sigEvent = netEvents[eventPos];
			if (sigEvent->type != FWPM_NET_EVENT_TYPE_CLASSIFY_DROP)
			{
				continue;
			}

			if (sigEvent->header.ipVersion == FWP_IP_VERSION_V4)
			{
				std::wstring wstrLocalIp = ip2str(sigEvent->header.localAddrV4);
				std::wstring wstrRemoteIp = ip2str(sigEvent->header.remoteAddrV4);

				std::wstring wstrProtocol;
				switch (sigEvent->header.ipProtocol)
				{
				case 1:
					wstrProtocol = L"ICMP";
					break;
				case 6:
					wstrProtocol = L"TCP";
					break;
				case 17:
					wstrProtocol = L"UDP";
					break;
				default:
					wstrProtocol = L"Other:";
					wstrProtocol.append(std::to_wstring(sigEvent->header.ipProtocol));
					break;
				}

				std::wcout << wstrLocalIp << "[" << sigEvent->header.localPort << "] ==> " << wstrRemoteIp << "[" << sigEvent->header.remotePort << "] protocol:" << wstrProtocol << std::endl;
			}
		}
	}
       if (netEvents)
       {
              FwpmFreeMemory0((void**)&netEvents);
              netEvents = NULL;
       }

	if (hEnumHandle)
	{
		FwpmNetEventDestroyEnumHandle0(hEngine, hEnumHandle);
		hEnumHandle = NULL;
	}
	if (hEngine)
	{
		FwpmEngineClose0(hEngine);
		hEngine = NULL;
	}

	return;
}

执行结果

读取阻断日志(方案二:事件订阅)

使用事件订阅的方式在防火墙中读取日志,只需要开启和取消事件订阅即可

启动事件订阅

DWORD FwpmNetEventSubscribe0( 
                        HANDLE engineHandle, 
                        const FWPM_NET_EVENT_SUBSCRIPTION0 *subscription,
                        FWPM_NET_EVENT_CALLBACK0 callback, 
                        void *context, 
                        HANDLE *eventsHandle 
                    );
  • 输入参数
    • engineHandle:防火墙句柄,由FwpmEngineOpen0打开
    • subscription:需要订阅的通知类型
    • callback:订阅的回调
    • context:自定义指针,原封不动传递导callback函数
    • eventsHandle:订阅句柄,在FwpmNetEventUnsubscribe0中取消订阅
  • 输出参数
    • 成功返回ERROR_SUCCESS, 失败返回 WFP Error Codes

取消事件订阅

DWORD FwpmNetEventUnsubscribe0( 
                        HANDLE engineHandle, 
                        HANDLE eventsHandle 
                    );
  • 输入参数
    • engineHandle:防火墙句柄,由FwpmEngineOpen0打开
    • eventsHandle:订阅句柄,由FwpmNetEventSubscribe0返回
  • 输出参数
    • 成功返回ERROR_SUCCESS, 失败返回 WFP Error Codes

回调函数定义

void CALLBACK FuncFwpmNetEventCallback0(
                PVOID FwContext, 
                FWPM_NET_EVENT1* FwEvent
                )
  • 输入参数
    • FwContext:自定义指针,FwpmNetEventSubscribe0函数的context指针
    • FwEvent:订阅的事件
  • 输出参数

参考代码

void CALLBACK FuncFwpmNetEventCallback0(_Inout_ PVOID FwContext, _In_ const FWPM_NET_EVENT1* FwEvent)
{
	if (FwEvent == NULL)
	{
		return;
	}

	if (FwEvent->header.ipVersion == FWP_IP_VERSION_V4)
	{
		std::wstring wstrLocalIp = ip2str(FwEvent->header.localAddrV4);
		std::wstring wstrRemoteIp = ip2str(FwEvent->header.remoteAddrV4);

		std::wstring wstrProtocol;
		switch (FwEvent->header.ipProtocol)
		{
		case 1:
			wstrProtocol = L"ICMP";
			break;
		case 6:
			wstrProtocol = L"TCP";
			break;
		case 17:
			wstrProtocol = L"UDP";
			break;
		default:
			wstrProtocol = L"Other:";
			wstrProtocol.append(std::to_wstring(FwEvent->header.ipProtocol));
			break;
		}

		std::wcout << wstrLocalIp << "[" << FwEvent->header.localPort << "] ==> " << wstrRemoteIp << "[" << FwEvent->header.remotePort << "] protocol:" << wstrProtocol << std::endl;
	}
	return;
}


void subscribeEvents()
{
	FWPM_SESSION0 session = { 0 };
	ZeroMemory(&session, sizeof(session));
	session.displayData.name = demoSession;
	session.txnWaitTimeoutInMSec = INFINITE;
	session.flags = FWPM_SESSION_FLAG_DYNAMIC;

	HANDLE hEngine = NULL;
	DWORD dwResult = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &hEngine);
	if (dwResult != ERROR_SUCCESS || hEngine == NULL)
	{
		std::cout << "FwpmEngineOpen0 Failed. ec:" << GetLastError() << std::endl;
		return;
	}

	FWP_VALUE0 inValue = { FWP_EMPTY };
	inValue.type = FWP_UINT32;
	inValue.uint32 = 1;
	FwpmEngineSetOption0(hEngine, FWPM_ENGINE_COLLECT_NET_EVENTS, &inValue);

	HANDLE hSubScribeHandle = NULL;
	FWPM_NET_EVENT_ENUM_TEMPLATE0 eventTemplate = { 0 };
	eventTemplate.numFilterConditions = 0;

	FWPM_NET_EVENT_SUBSCRIPTION0 subscription = { 0 };
	subscription.enumTemplate = &eventTemplate;
	subscription.sessionKey = session.sessionKey;
	FwpmNetEventSubscribe0(hEngine, &subscription, FuncFwpmNetEventCallback0, NULL, &hSubScribeHandle);

	std::cout << "Event Subscribe Started....." << std::endl;
	getchar();

	FwpmNetEventUnsubscribe0(hEngine, hSubScribeHandle);

	if (hEngine)
	{
		FwpmEngineClose0(hEngine);
		hEngine = NULL;
	}
}

运行效果

相关推荐
FeelTouch Labs18 分钟前
Netty实现WebSocket Server是否开启压缩深度分析
网络·websocket·网络协议
可均可可30 分钟前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_011 小时前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj1 小时前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
-Even-1 小时前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
plmm烟酒僧2 小时前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
我是谁??2 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
发霉的闲鱼2 小时前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc