添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Windows XML Event Log (EVTX)单条日志清除系列文章的第四篇,介绍第二种删除当前系统单条日志记录的方法:获得日志服务Eventlog对应进程中指定日志文件的句柄,通过Dll注入获得该句柄的操作权限,利用该句柄实现日志文件的修改

0x01 简介

本文将要介绍以下内容:

  • 枚举日志服务Eventlog对应进程的所有句柄,获得指定日志文件的句柄
  • 通过Dll注入获得该句柄的操作权限
  • 进程间消息传递的方法
  • 0x02 利用思路

    系统启动日志服务Eventlog后,会以独占模式打开日志文件,导致其他进程无法打开该日志文件,也就无法进行修改操作

    那么,如果我们通过Dll注入进入进程的内存,接着获得指定日志文件的句柄,能否获得该日志文件的操作权限呢?

    0x03 枚举日志服务Eventlog对应进程的所有句柄,获得指定日志文件的句柄

    1、利用工具processhacker获得指定日志文件的句柄

    下载地址:

    https://processhacker.sourceforge.io/

    (1)获得日志服务Eventlog对应进程的pid

    执行如下powershell代码:

    Get-WmiObject -Class win32_service -Filter "name = 'eventlog'" | select -exp ProcessId
    

    (2)运行processhacker

    根据pid找到进程,查看Properties->Handles

    能够获得当前进程的所有句柄信息

    可以看到C:\Windows\System32\winevt\Logs\Security.evtx对应的Handle值为0x1c8

    2、通过c++程序实现获得指定日志文件的句柄

    查看processhacker的源码,寻找实现方法

    代码位置:

    https://github.com/processhacker/processhacker/blob/e2d793289dede80f6e3bda26d6478dc58b20b7f8/ProcessHacker/hndlprv.c#L307

    获得参考资料:

  • On Windows 8 and later, NtQueryInformationProcess with ProcessHandleInformation is the most efficient method.
  • On Windows XP and later, NtQuerySystemInformation with SystemExtendedHandleInformation.
  • Otherwise, NtQuerySystemInformation with SystemHandleInformation can be used.
  • 于是,挑选第三个方法尝试实现

    经测试,第三个方法适用于Win7和更新版本的操作系统

    利用NtQuerySystemInformation查询SystemHandleInformation能够获得所有进程的句柄信息

    挑选出日志进程中的所有句柄

    接着通过NtDuplicateObject获取句柄的名称和具体的数值信息

    最后筛选出想要查找的句柄,输出Handle值

    完整实现代码已开源,下载地址如下:

    https://github.com/3gstudent/Homework-of-C-Language/blob/master/GetPIDandHandle(evtx).cpp

    代码实现了根据输入的关键词进行搜索,获得对应的句柄名称和Handle值

    测试如下图

    成功获得日志服务Eventlog对应进程的pid,security.evtx对应的Handle值为0x1c8

    0x04 通过Dll注入获得该句柄的操作权限

    通过NtCreateThreadEx + LdrLoadDll实现Dll注入的代码可参考:

    https://github.com/3gstudent/Homework-of-C-Language/blob/master/NtCreateThreadEx%20%2B%20LdrLoadDll.cpp

    注入成功后需要FreeDll

    注入成功后,使用获得到的Handle值作为函数CreateFileMapping()的第一个参数,创建一个文件映射内核对象

    然后调用函数MapViewOfFile()将文件数据映射到进程的地址空间

    接下来修改内存中的数据,删除单条日志记录

    最后调用函数FlushViewOfFile(),将内存数据写入磁盘

    0x05 进程间消息传递的方法

    在实际使用过程中,整个实现日志记录删除功能的代码要放在Dll中,而通过CreateRemoteThread无法向Dll传入参数,这就导致无法删除指定EventlogRecordId的日志

    这里可以借助进程间的消息传递

    实现方法有多种,例如信号、管道、消息队列和共享内存,甚至是读写文件

    由于在0x04部分使用了函数CreateFileMapping()创建一个文件映射内核对象,所以进程间消息传递也使用内存映射的方式

    创建一个共享内存,代码可参考:

    https://github.com/3gstudent/Homework-of-C-Language/blob/master/OpenFileMapping.cpp

    代码中创建了两个内存映射对象,并且指定了函数CreateFileMapping()的访问权限为允许任何人访问该对象,即函数CreateFileMapping()的第二个参数

    通常情况下,该值设置为NULL,表示默认访问权限,但在注入的Dll中必须设置为允许任何人访问该对象,否则提示拒绝访问

    原因如下:

    Dll注入svchost.exe后,权限为System,默认访问权限无法访问由用户创建的内存映射文件对象,必须指定为允许任何人访问该对象

    当然,如果是两个用户权限的进程进行消息传递,函数CreateFileMapping()的第二个参数为NULL即可

    读取指定共享内存,代码可参考:

    https://github.com/3gstudent/Homework-of-C-Language/blob/master/OpenFileMapping.cpp

    代码中读取这两个内存映射对象,添加了数据类型转换的功能(字符串转int)

    0x06 程序实现流程

    1、自己解析格式,实现日志删除

    删除的关键代码如下:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecordofFile.cpp

    代码实现了删除文件c:\test\Setup.evtx中的一条日志(EventRecordID=14),新文件保存为c:\test\SetupNew.evtx

    整个实现流程分成两部分:

  • 注入的Dll
  • 1.启动程序(Loader-rewriting.cpp)

    代码地址:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Loader-rewriting.cpp

    流程如下:

  • 获得日志服务Eventlog对应进程的pid
  • 枚举日志服务Eventlog对应进程的的所有句柄,获得指定日志文件的句柄
  • 创建两个内存映射,用于向dll传递日志文件的句柄和需要删除日志的EventRecordID
  • 向日志服务Eventlog对应的进程注入Dll
  • 释放Dll
  • 关闭内存映射
  • 2.注入的Dll(Dll-rewriting.cpp)

    代码地址:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Dll-rewriting.cpp

    流程如下:

  • 分别从两个内存映射读取消息,将读取到的内容从字符串转换成int类型,获得日志文件的句柄及需要删除日志的EventRecordID
  • 调用函数CreateFileMapping(),传入日志文件的句柄,创建一个文件映射内核对象
  • 调用函数MapViewOfFile()将文件数据映射到进程的地址空间
  • 修改内存数据,删除指定日志
  • 调用函数FlushViewOfFile(),将内存数据写入磁盘
  • 关闭日志文件的内存映射
  • 测试如下图

    2、使用WinAPI EvtExportLog,过滤出想要删除的内容

    可供参考的代码:

    https://github.com/360-A-Team/EventCleaner/blob/master/EventCleaner/EventCleaner.cpp#L528

    我按照这个思路写的代码:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecordofFileEx.cpp

    代码实现了调用Windows API EvtExportLog对日志文件进行筛选,去除指定日志后,将剩下的日志内容保存为新文件temp.evtx

    整个实现流程分成三部分:

  • 日志删除程序
  • 注入的Dll
  • 1.日志删除程序(DeleteRecord-EvtExportLog.cpp)

    代码地址:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecord-EvtExportLog.cpp

    流程如下:

  • 指定日志文件和需要删除日志的EventRecordID
  • 生成新的日志文件temp.evtx
  • 2.启动程序(Loader-EvtExportLog.cpp)

    代码地址:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Loader-EvtExportLog.cpp

    流程如下:

  • 获得日志服务Eventlog对应进程的pid
  • 枚举日志服务Eventlog对应进程的的所有句柄,获得指定日志文件的句柄
  • 创建三个内存映射,用于向dll传递日志文件的句柄、新日志文件的长度和新日志文件的内容
  • 向日志服务Eventlog对应的进程注入Dll
  • 释放Dll
  • 关闭内存映射
  • 3.注入的Dll(Dll-EvtExportLog.cpp)

    代码地址:

    https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Dll-EvtExportLog.cpp

    流程如下:

  • 从第一个内存映射获得日志文件的句柄,将读取到的内容从字符串转换成int类型
  • 从第二个内存映射获得新日志文件的长度
  • 根据新日志文件的长度调整内存映射的读取长度,从第三个内存映射获得新日志文件的内容
  • 调用函数CreateFileMapping(),传入日志文件的句柄,创建一个文件映射内核对象
  • 调用函数MapViewOfFile()将文件数据映射到进程的地址空间
  • 修改内存数据,覆盖为新日志文件的内容
  • 调用函数FlushViewOfFile(),将内存数据写入磁盘
  • 关闭日志文件的内存映射
  • 测试如下图

    对于以上两种方法,删除setup.evtx是没有问题的,删除system.evtxsecurity.evtx会存在因为竞争条件导致删除失败的情况

    0x07 小结

    本文介绍了第二种删除当前系统单条日志记录的方法:获得日志服务Eventlog对应进程中指定日志文件的句柄,通过Dll注入获得权限,利用该句柄实现日志文件的修改 某些情况下,dll注入会失败,那么是否还有删除当前系统单条日志记录的方法呢?下一篇文章将会介绍

    LEAVE A REPLY