通过安全日志读取WFP防火墙放行日志

这篇具有很好参考价值的文章主要介绍了通过安全日志读取WFP防火墙放行日志。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

之前的文档中,描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档,着重描述如何读取操作系统中所有被放行的网络通信行为。
读取系统中放行的网络通信行为日志,在win10之后的操作系统上,也可以通过前一篇提到的读取阻断日志的方式进行读取(以FWPM_NET_EVENT0.type字段区分),但是在较老的系统中却不支持直接读取。为了保持系统兼容性,可以通过读取操作系统安全日志(EventId:5156)的方式进行网络通信日志的采集。

需要注意的坑点

  1. 查询放行日志时需要注意,每个网络通信行为在日志中只会出现一条放行记录,对应的筛选器ID,只会是首次对其进行审计的过滤器ID。因此,如果有其他权重更高的子层对网络连接进行了审计时,就无法通过筛选器ID匹配的方式获取。如果有这方面需求的话,解决方法只能是尽可能将自身子层的权重设为最高。
  2. 网络日志中读取连入行为时,WIN10/2016/2019源IP和目的IP字段与其它更早的操作系统相反,需要特殊处理。连出行为无异常。

开启审计

采用读取安全日志的方式进行网络事件获取,首先需要在系统中开启审计功能。在代码里面也有多种方式可以开启,之后会单开一篇文档进行描述,在这里先手动开启。

  • 打开本地安全策略(开始——运行——secpol.msc),依次打开:安全设置——本地策略——审核策略如图
    通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

  • 在右侧窗口中打开 审核对象 标签页,勾选 “成功” 复选框后,点击保存,即可开启网络访问的审计功能
    通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

  • 右键单击 “我的电脑”——“管理”——“计算机管理”——“系统工具”——“事件查看器”——“Windows日志”——“安全”中,查看5156日志即可。
    通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

网络通信日志默认情况下是开启状态,为了以防万一,每次获取之前需要使用代码开启一次。使用代码的开启方式下次单开文档分享。

使用WMI方式进行查询

使用ReadEventLog进行查询

优点:兼容性高,可支持XP/2003操作系统。读取性能高。
缺点:无法做过滤,在大量日志中提取少量日志时效率较低
使用ReadEventLog读取Windows的安全日志只需要三步即可,1、打开EventLog句柄;2、使用ReadEventLog循环读取日志;3、关闭EventLog句柄。具体API描述如下。

打开EventLog句柄

    HANDLE OpenEventLog( 
                LPCSTR lpUNCServerName, 
                LPCSTR lpSourceName 
            );
  • 输入参数
    • lpUNCServerName:远程服务器的名称。读取本地的话传入NULL即可。
    • lpSourceName:日志名称。这里读取安全日志传入“Serurity”。其他对应值:系统日志“System”,应用程序日志“Application”
  • 输出参数
    • 返回日志读取句柄。在ReadEventLog中使用,需要调用CloseEventLog手动关闭。

读取日志

BOOL ReadEventLog( 
                        HANDLE hEventLog, 
                        DWORD dwReadFlags, 
                        DWORD dwRecordOffset, 
                        LPVOID lpBuffer, 
                        DWORD nNumberOfBytesToRead, 
                        DWORD *pnBytesRead, 
                        DWORD *pnMinNumberOfBytesNeeded 
                );
  • 输入参数

    • hEventLog:需要读取日志的句柄,就是刚才OpenEventLog返回的那个
    • dwReadFlags:读取标志,可以选择从指定偏移读取(EVENTLOG_SEEK_READ)或按顺序读取(EVENTLOG_SEQUENTIAL_READ),也可以指定正序读取(EVENTLOG_FORWARDS_READ)或倒序读取(EVENTLOG_BACKWARDS_READ)
    • dwRecordOffset:当dwReadFlags中包含EVENTLOG_SEEK_READ时有效,表示开始的位置
    • lpBuffer:分配的缓冲区,由外部划分内存
    • nNumberOfBytesToRead:lpBuffer缓冲区的大小
    • pnBytesRead:返回接收字节数
    • pnMinNumberOfBytesNeeded:返回lpBuffer所需最小缓冲区大小。仅当lpBuffer过小时返回,可判断GetLastError()返回ERROR_INSUFFICIENT_BUFFER
      时有效。
  • 输出参数

    • 正常执行返回非0值,失败后返回0

    关闭EventLog句柄

BOOL CloseEventLog( 
                            HANDLE hEventLog 
                    );
  • 输入参数
    • hEventLog:事件句柄,由OpenEventLog返回
  • 输出参数
    • 成功与否,这玩意没啥好判断的。

关联结构体

typedef struct _EVENTLOGRECORD { 
                        DWORD Length; 
                        DWORD Reserved; 
                        DWORD RecordNumber; 
                        DWORD TimeGenerated; 
                        DWORD TimeWritten; 
                        DWORD EventID; 
                        WORD EventType; 
                        WORD NumStrings; 
                        WORD EventCategory; 
                        WORD ReservedFlags; 
                        DWORD ClosingRecordNumber; 
                        DWORD StringOffset; 
                        DWORD UserSidLength; 
                        DWORD UserSidOffset; 
                        DWORD DataLength; 
                        DWORD DataOffset; 
                        } EVENTLOGRECORD, *PEVENTLOGRECORD;
  • 参数说明
    • Length:当前结构体的长度,由于ReadEventLog是以内存块的方式返回,单次返回的内存块中可能包含多个,特别是为了节省资源,可能会在代码中刻意一次读取大量的EventLogRecord结构体。这些结构体在内存块中,就以Length参数作为分界线来进行分割

    • Reserved:保留,没有可以研究过用于啥

    • RecordNumber:日志序号,可配合ReadEventLog函数中的dwReadFlags参数和dwReadOffset参数设置读取的偏移地址

    • TimeGenerated:事件时间,转time_t就可以

    • TimeWrittern:日志写入时间,time_t格式

    • EventID:事件ID,最高两位代表严重性,第三位代表是否为系统事件,低16位代表在安全日志中可见的ID。

    • EventType:事件类型,

    • NumStrings:包含字符串数目

    • EventCategory:事件类别

    • ReservedFlags:保留

    • ClosingRecordNumber:保留

    • StringOffset:事件包含字符串起始地址的偏移。字符串按顺序依次在内存中存储,0长度的字符串代表结束。字符串的顺序,和在事件查看器中看到的顺序一致,如图
      通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

    • UserSidLength/UserSidOffset: 用户SID的长度及偏移,与字符串类似

    • DataLength/DataOffset:数据部分的长度及偏移

参考代码

#include <iostream>
#include <windows.h>
#include <string>
#include <vector>

#define MAX_EVENTLOG_READONCE   2048
void ReadLogsByReadEventLogAPI()
{
    LPBYTE pEventLogBuffer = new(std::nothrow) BYTE[MAX_EVENTLOG_READONCE];
    if (pEventLogBuffer == NULL)
    {
        return;
    }
    ZeroMemory(pEventLogBuffer, MAX_EVENTLOG_READONCE);

    HANDLE hEventLog = OpenEventLog(NULL, L"Security");
    if (hEventLog == NULL)
    {
        delete[] pEventLogBuffer;
        pEventLogBuffer = NULL;
        return;
    }

    DWORD dwMemoryLen = MAX_EVENTLOG_READONCE;
    DWORD dwReaded = 0, dwMiniMemoryNeeded = 0;
    while (true)//  -_-|||
    {
        BOOL isSucc = ReadEventLog(hEventLog, EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ, 0, pEventLogBuffer, dwMemoryLen, &dwReaded, &dwMiniMemoryNeeded);
        if (isSucc == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER && dwMiniMemoryNeeded)
        {
            delete[] pEventLogBuffer;
            pEventLogBuffer = NULL;

            pEventLogBuffer = new (std::nothrow) BYTE[dwMiniMemoryNeeded];
            if (pEventLogBuffer == NULL)
            {
                break;
            }
            ZeroMemory(pEventLogBuffer, dwMiniMemoryNeeded);
            dwMemoryLen = dwMiniMemoryNeeded;
            isSucc = ReadEventLog(hEventLog, EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ, 0, pEventLogBuffer, dwMemoryLen, &dwReaded, &dwMiniMemoryNeeded);
        }

        if (isSucc == FALSE)
        {
            break;
        }

        int pos = 0;
        do 
        {
            EVENTLOGRECORD* pTempRecord = (EVENTLOGRECORD*)(pEventLogBuffer + pos);
            __time32_t occurTime = pTempRecord->TimeWritten;
            int nEventId = pTempRecord->EventID & 0xffff;
            if (nEventId != 5156)
            {
                pos = pos + pTempRecord->Length;
                continue;
            }

            std::vector<std::wstring> vecEventParam;

            LPBYTE pTempBuffer = (LPBYTE)pTempRecord + pTempRecord->StringOffset;
            int strCount = 0;
            while ((pTempBuffer < ((LPBYTE)pTempRecord + pTempRecord->Length)) && (strCount < pTempRecord->NumStrings))
            {
                int len = wcslen((wchar_t*)pTempBuffer);
                if (len == 0)
                {
                    break;
                }

                vecEventParam.push_back((wchar_t*)pTempBuffer);

                pTempBuffer = pTempBuffer + (len + 1) * sizeof(wchar_t);
                strCount++;
            }

            if (vecEventParam.size() > 10)
            {
                std::wcout << "\n\n=========================================" << std::endl;
                std::wcout << L"Source:\t" << vecEventParam[3] << L"[" << vecEventParam[4] << L"]" << std::endl;
                std::wcout << L"Destination:\t" << vecEventParam[5] << L"[" << vecEventParam[6] << L"]" << std::endl;
                std::wcout << L"Protocol Code:\t" << vecEventParam[7] << std::endl;
                std::wcout << L"Process Id:\t" << vecEventParam[0] << std::endl;
                std::wcout << L"Process Name:\t" << vecEventParam[1] << std::endl;
                std::wcout << "=========================================\n\n" << std::endl;
            }
            pos = pos + pTempRecord->Length;
        } while (pos < dwReaded);
    }

    CloseEventLog(hEventLog);
    
    delete[] pEventLogBuffer;
    pEventLogBuffer = NULL;
}

输出截图
通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

使用Evt系列API进行查询

优点:性能高,可自由配置过滤条件,方便在海量日志中检索,可读取的内容相当丰富
缺点:不支持xp/2003操作系统
Evt系列API涉及到的功能较多,如果只需要读取系统日志的话,只需要枚举、遍历、读取、关闭四步即可完成。

枚举当前日志

EVT_HANDLE EvtQuery( 
                                EVT_HANDLE Session, 
                                LPCWSTR Path, 
                                LPCWSTR Query, 
                                DWORD Flags 
                        );

遍历日志,获取下一条

BOOL EvtNext( 
                                    EVT_HANDLE ResultSet, 
                                    DWORD EventsSize, 
                                    PEVT_HANDLE Events, 
                                    DWORD Timeout, 
                                    DWORD Flags, 
                                    PDWORD Returned 
                                );

读取日志内容

BOOL EvtRender( 
                                    EVT_HANDLE Context, 
                                    EVT_HANDLE Fragment, 
                                    DWORD Flags, 
                                    DWORD BufferSize, 
                                    PVOID Buffer, 
                                    PDWORD BufferUsed, 
                                    PDWORD PropertyCount 
                            );
  • 输入参数
    • Context:
    • Fragment:
    • Flags:
    • BufferSize:
    • Buffer:
    • BufferUsed:
    • PropertyCount:
  • 输出参数
    *

关闭枚举句柄

BOOL EvtClose( 
                            EVT_HANDLE Object 
                        );

实例代码


void parseEventXML(std::wstring wstrXML)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
    std::string utfXML = cv.to_bytes(wstrXML);

    tinyxml2::XMLDocument rootXML;
    if (rootXML.Parse(utfXML.c_str()) != tinyxml2::XML_SUCCESS)
    {
        return;
    }

    tinyxml2::XMLNode* rootNode = rootXML.FirstChild();
    if (rootNode == NULL)
    {
        return;
    }

    std::string processId;
    std::string processName;
    std::string sourceAddress;
    std::string sourcePort;
    std::string destAddress;
    std::string destPort;
    std::string protocol;

    tinyxml2::XMLNode* eventNode = rootXML.FirstChildElement("Event");
    if (eventNode)
    {
        tinyxml2::XMLElement* eventDataNode = eventNode->FirstChildElement("EventData");
        if (eventDataNode == NULL)
        {
            return;
        }
        tinyxml2::XMLElement* dataNode = eventDataNode->FirstChildElement("Data");
        do 
        {
            std::string strName = dataNode->Attribute("Name");
            if (_stricmp(strName.c_str(), "ProcessID") == 0)
            {
                processId = dataNode->GetText();
            }
			else if (_stricmp(strName.c_str(), "Application") == 0)
			{
				processName = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "SourceAddress") == 0)
			{
				sourceAddress = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "SourcePort") == 0)
			{
				sourcePort = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "DestAddress") == 0)
			{
				destAddress = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "DestPort") == 0)
			{
				destPort = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "Protocol") == 0)
			{
				protocol = dataNode->GetText();
			}

            dataNode = dataNode->NextSiblingElement("Data");
        } while (dataNode);

		std::cout << "\n\n=========================================" << std::endl;
		std::cout << "Source:\t" << sourceAddress << "[" << sourcePort << "]" << std::endl;
		std::cout << "Destination:\t" << destAddress << "[" << destPort << "]" << std::endl;
		std::cout << "Protocol Code:\t" << protocol << std::endl;
		std::cout << "Process Id:\t" << processId << std::endl;
		std::cout << "Process Name:\t" << processName << std::endl;
		std::cout << "=========================================\n\n" << std::endl;
    }
}

void ReadLogsByEvtAPI()
{
    DWORD dwEventLogBufferLen = MAX_EVENTLOG_READONCE;
	LPBYTE pEventLogBuffer = new(std::nothrow) BYTE[MAX_EVENTLOG_READONCE];
	if (pEventLogBuffer == NULL)
	{
		return;
	}
	ZeroMemory(pEventLogBuffer, MAX_EVENTLOG_READONCE);

    std::wstring wstrQuery = std::wstring(
        L"<QueryList>"
        L"	<Query>"
        L"		<Select>Event/System[EventID=5156]</Select>"
        L"	</Query>"
        L"</QueryList>");

    EVT_HANDLE hResult = EvtQuery(NULL, L"Security", wstrQuery.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection);
    if (hResult == NULL)
    {
        delete[] pEventLogBuffer;
        pEventLogBuffer = NULL;
        return;
    }

    while (true) // -_-
    {

		EVT_HANDLE hEventArrs[MAX_PATH] = { 0 };
		DWORD dwReturnEvents = 0;
		BOOL isSucc = EvtNext(hResult, MAX_PATH, hEventArrs, INFINITE, 0, &dwReturnEvents);
		if (isSucc && dwReturnEvents)
		{
			for (int eventPos = 0; eventPos < dwReturnEvents; eventPos++)
			{
				DWORD dwBufferUsed = 0;
				DWORD dwPropertyCount = 0;
				int nBufferSize = dwEventLogBufferLen;
				ZeroMemory(pEventLogBuffer, nBufferSize);
				isSucc = EvtRender(NULL, hEventArrs[eventPos], EvtRenderEventXml, nBufferSize, pEventLogBuffer, &dwBufferUsed, &dwPropertyCount);
				if (isSucc == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
				{
					delete[] pEventLogBuffer;
					pEventLogBuffer = NULL;

					dwEventLogBufferLen = dwBufferUsed + 2;
					pEventLogBuffer = new(std::nothrow) BYTE[dwEventLogBufferLen];
					if (pEventLogBuffer == NULL)
					{
						break;
					}

					nBufferSize = dwEventLogBufferLen;
					ZeroMemory(pEventLogBuffer, dwEventLogBufferLen);
					isSucc = EvtRender(NULL, hEventArrs[eventPos], EvtRenderEventXml, nBufferSize, pEventLogBuffer, &dwBufferUsed, &dwPropertyCount);
				}

				if (isSucc)
				{
					std::wstring strEventXML = std::wstring((wchar_t*)pEventLogBuffer);
					parseEventXML(strEventXML);
				}
			}
		}
    }

    if (pEventLogBuffer)
    {
		delete[] pEventLogBuffer;
		pEventLogBuffer = NULL;
    }
}

输出截图
通过安全日志读取WFP防火墙放行日志,技术心得分享,c++,windows,网络

备注

实例代码中解析xml使用的是tinyxml2库,对应github地址为:https://github.com/leethomason/tinyxml2/tree/master
当前最新版(9.0.0)版本下载地址:https://download.csdn.net/download/QQ1113130712/88235095文章来源地址https://www.toymoban.com/news/detail-661596.html

到了这里,关于通过安全日志读取WFP防火墙放行日志的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 自动化防火墙放行目标域名IP

    Allow from : 这是一个固定的文本字符串,表示规则中的注释内容开始部分。 domain: 这是一个变量,其中存储了您要匹配的注释内容。在命令中使用-v选项将该变量传递给awk命令。 ~操作符: 在awk中,~操作符用于进行模式匹配。 $0: 这是awk中的一个特殊变量,表示整个输入行。 ~操

    2024年02月10日
    浏览(32)
  • centos7中设置端口放行(centos7防火墙配置端口放行)

    说明:执行之后一定要执行第四步命令:firewall-cmd --reload    

    2024年02月06日
    浏览(51)
  • 防火墙日志记录和监控在网络安全中的重要性

    防火墙监视进出网络的流量,并保护部署网络的网络免受恶意流量的侵害。它是一个网络安全系统,根据一些预定义的规则监控传入和传出的流量。它以日志的形式记录有关如何管理流量的信息。日志数据包含流量的源和目标 IP 地址、端口号、协议等。为了有效地保护您的网

    2024年02月07日
    浏览(50)
  • 防火墙日志及日志分析

    防火墙监视进出网络的流量,并保护部署网络的网络免受恶意流量的侵害。它是一个网络安全系统,根据一些预定义的规则监控传入和传出的流量。它以日志的形式记录有关如何管理流量的信息。日志数据包含流量的源和目标 IP 地址、端口号、协议等。为了有效地保护您的网

    2023年04月08日
    浏览(18)
  • Linux下通过AnySetup配置防火墙

      软件 AnySetup 主要功能 主要功能是对Linux操作系统下的基本配置进行管理、多种服务配置进行管理、安全配置进行管理等。如:操作系统的升级管理,软件包的安装、更新和卸载管理,软件仓库源的管理,系统时间和时区的管理,系统语言环境的管理,网络环境的配置管理,

    2024年02月05日
    浏览(40)
  • 网络安全入门:什么是防火墙,防火墙有哪些功能

    网络安全领域是安全行业最基本的领域,研究的技术范畴主要围绕防火墙/NGFW/UTM、网闸技术、入侵检测/防御、VPN网关(IPsec/SSL)、抗DDOS、上网行为管理、负载均衡/应用交付、流量分析、漏洞扫描等。以下主要介绍什么是防火墙,防火墙有哪些功能? 什么是防火墙? 所谓“

    2024年02月09日
    浏览(58)
  • 网络设备(防火墙、路由器、交换机)日志分析监控

    外围网络设备(如防火墙、路由器、交换机等)是关键组件,因为它们控制进出公司网络的流量。因此,监视这些设备的活动有助于 IT 管理员解决操作问题,并保护网络免受攻击者的攻击。通过收集和分析这些设备的日志来监控这些设备是修复操作问题、发现和缓解入侵以及

    2024年02月12日
    浏览(53)
  • Windows通过命令开启、关闭、删除防火墙规则

    #关闭端口 #开启端口 #删除规则 参考: https://blog.51fuli.info/windows%e9%98%b2%e7%81%ab%e5%a2%99-netsh-advfirewall-%e8%ae%be%e7%bd%ae/

    2024年02月16日
    浏览(53)
  • [C#] 允许当前应用程序通过防火墙

            通常在一台装有防火墙的电脑上运行程序的场合,往往会弹出对话框提示:是否允许执行该应用程序。          我们在开发软件的时候,可以事先在软件里面设置当前软件为防火墙允许通过的软件。这样,用户在使用时就可以避开前面提到的弹框了。          

    2024年02月09日
    浏览(50)
  • ENSP防火墙综合实验(GRE、IPSec、NAT通信)【防火墙安全策略】

    注:所有的通信使用静态路由来保证。 HQ: Partner Branch HQ Partner Branch HQ Partner HQ Branch HQ Branch HQ HQ Partner Branch 注:此为FTP服务器设置 注:客户端Client3成功访问FTP服务器的截图 注:通过抓包抓取FTP的流量(筛选ftp),可以看到有多个ftp的包,点开其中一个流量,可以清晰看到

    2023年04月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包