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

前言

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

需要注意的坑点

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

开启审计

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

  • 打开本地安全策略(开始——运行——secpol.msc),依次打开:安全设置——本地策略——审核策略如图
    在这里插入图片描述

  • 在右侧窗口中打开 审核对象 标签页,勾选 “成功” 复选框后,点击保存,即可开启网络访问的审计功能
    在这里插入图片描述

  • 右键单击 “我的电脑”——“管理”——“计算机管理”——“系统工具”——“事件查看器”——“Windows日志”——“安全”中,查看5156日志即可。
    在这里插入图片描述

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

使用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长度的字符串代表结束。字符串的顺序,和在事件查看器中看到的顺序一致,如图
      在这里插入图片描述

    • 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;
}

输出截图
在这里插入图片描述

使用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;}
}

输出截图
在这里插入图片描述

备注

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/101894.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

继承(C++)

继承 一、初识继承概念“登场”语法格式 继承方式九种继承方式组合小结&#xff08;对九种组合解释&#xff09; 二、继承的特性赋值转换 一一 切片 / 切割作用域 一一 隐藏 / 重定义 三、派生类的默认成员函数派生类的默认成员函数1. 构造函数2. 拷贝构造3. 赋值运算符重载4. …

【编织时空三:探究顺序表与链表的数据之旅】

本章重点 链表OJ题 1. 删除链表中等于给定值 val 的所有结点。 OJ链接 思路一&#xff1a;删除头结点时另做考虑&#xff08;由于头结点没有前一个结点&#xff09; struct ListNode* removeElements(struct ListNode* head, int val) {assert(head);struct ListNode* cur h…

Go:测试框架GoConvey 简介

快速开始 GoConvey是一个完全兼容官方Go Test的测试框架&#xff0c;一般来说这种第三方库都比官方的功能要强大、更加易于使用、开发效率更高&#xff0c;闲话少说&#xff0c;先看一个example&#xff1a; package utils import (. "github.com/smartystreets/goconvey…

【JVM】运行时数据区域

文章目录 说明程序计数器虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存 说明 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的区域随着虚拟机进程的启动而一直…

【广州华锐互动】牲畜养殖VR模拟实操系统为传统教育注入新的生命力

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐走进我们的生活。在农业领域&#xff0c;VR技术的应用也日益广泛&#xff0c;为现代农业人才培养提供了新的途径。 由广州华锐互动开发的“牲畜养殖VR模拟实操系统”引起了广泛关注&#xff0c;系统包含了鸡、猪、牛、马…

产品流程图是什么?怎么做?

产品流程图是什么&#xff1f; 产品流程图是一种图形化的表达方式&#xff0c;用于描述产品开发、制造、销售、使用等各个阶段中涉及的流程、步骤和关系。它通过图形符号、箭头、文本等元素&#xff0c;展示了产品的各个环节之间的关联和顺序&#xff0c;通常被用于可视化产…

STM32 F103C8T6学习笔记12:红外遥控—红外解码-位带操作

今日学习一下红外遥控的解码使用&#xff0c;红外遥控在日常生活必不可少&#xff0c;它的解码与使用也是学习单片机的一个小过程&#xff0c;我们将通过实践来实现它。 文章提供源码、测试工程下载、测试效果图。 目录 红外遥控原理&#xff1a; 红外遥控特点&#xff1a; …

Qt+C++串口调试接收发送数据曲线图

程序示例精选 QtC串口调试接收发送数据曲线图 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC串口调试接收发送数据曲线图>>编写代码&#xff0c;代码整洁&#xff0c;规则&…

探索GreatADM:图形化部署MGR的全新体验

摘要&#xff1a; 在DBA的日常工作中&#xff0c;快速部署数据库高可用架构&#xff0c;且标准化地入网部署数据库是一项重要的基础任务。本文将介绍常见的部署MGR的方式&#xff0c;并重点介绍万里数据库的GreatADM数据库管理平台进行图形化、可视化、标准化的部署过程&#x…

vue 学习笔记 简单实验

1.代码(html) <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"counter">Counter: {{ counter }} </div> <script> const Counter {data() {return {counter: 5}} } Vue.cr…

二、pikachu之SQL注入(2)

文章目录 1、delete注入2、http header注入3、布尔盲注4、时间盲注 4、宽字节注入 1、delete注入 &#xff08;1&#xff09;寻找传参页面&#xff0c;在删除留言的时候&#xff0c;发现是get传参&#xff1b; &#xff08;2&#xff09;判断是否存在注入点&#xff0c;命令&…

Shell语法揭秘:深入探讨常见Linux Shell之间的语法转换

深入探讨常见Linux Shell之间的语法转换 一、引言二、Linux常用Shell&#xff1a;Bash、Zsh、Ksh、Csh、Tcsh和Fish的简介2.1、Bash、Zsh、Ksh、Csh、Tcsh和Fish的特点和用途2.2、语法差异是常见Shell之间的主要区别 三、变量和环境设置的语法差异3.1、变量定义和使用的不同语法…

Redis——set类型详解

概要 Set&#xff08;集合&#xff09;&#xff0c;将一些有关联的数据放到一起&#xff0c;集合中的元素是无序的&#xff0c;并且集合中的元素是不能重复的 之前介绍的list就是有序的&#xff0c;对于列表来说[1, 2, 3] 和 [2, 1, 3]是两个不同的列表&#xff0c;而对于集合…

GraphScope,开源图数据分析引擎的领航者

文章首发地址 GraphScope是一个开源的大规模图数据分析引擎&#xff0c;由Aliyun、阿里巴巴集团和华为公司共同开发。GraphScope旨在为大规模图数据处理和分析提供高性能、高效率的解决方案。 Github地址&#xff1a; https://github.com/alibaba/GraphScope GraphScope 的重…

开发第一个gPRC的开发

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

网络综合布线实训室方案(2023版)

综合布线实训室概述 随着智慧城市的蓬勃发展,人工智能、物联网、云计算、大数据等新兴行业也随之崛起,网络布线系统作为现代智慧城市、智慧社区、智能建筑、智能家居、智能工厂和现代服务业的基础设施和神经网络,发挥着重要作用。实践表明,网络系统故障的70%发生在布线系统,直接…

苹果手机桌面APP带云图标有个箭头,过一段时间经常要下载才能使用APP

环境&#xff1a; IPhone 11 IOS13.0 问题描述&#xff1a; 苹果手机桌面APP带云图标有个箭头&#xff0c;过一段时间经常要下载才能使用APP 解决方案&#xff1a; 1.打开设置&#xff0c;往下找到iTunes Store与App Store 2.找到下面卸载未使用的APP 关闭按钮

最优的家电设备交互方式是什么?详解家电设备交互的演进之旅

家电&#xff0c;在人们的日常生活中扮演着不可或缺的角色&#xff0c;也是提升人们幸福感的重要组成部分&#xff0c;那你了解家电的发展史吗&#xff1f; 70年代 结婚流行“四大件”&#xff1a;手表、自行车、缝纫机&#xff0c;收音机&#xff0c;合成“三转一响”。 80年…

问题描述:在Windows下没有预装ImageMagick工具

问题描述:在Windows下没有预装ImageMagick工具 # WInR输入cmd回车进入命令行,执行以下命令查看版本信息 magick --version没有预装ImageMagick工具 解决方案&#xff1a;下载安装ImageMagick 官网下载:ImageMagick-7.1.1-15-Q16-x64-dll.exe 下载之后&#xff0c;一路下一步…

MySQL表的约束

MySQL表的约束 约束的概念空属性默认值列描述zerofill主键自增长唯一键外键 约束的概念 在正式谈MySQL表的约束之前&#xff0c;我们先来简单理解一下约束这个概念; 约束&#xff1a;意思是指带有束缚、限制、管束等意思&#xff1b; 大白话就是说:规定了那些事情你不能干&…