0x00 前言
Windows XML Event Log (EVTX)單條日誌清除系列文章的第二篇,介紹對指定evtx文件的單條日誌刪除方法,解決在程序設計上需要考慮的多個問題,開源實現代碼。
0x01 簡介
本文將要介紹以下內容:
-
對指定evtx文件單條日誌的刪除思路
-
程序實現細節
-
開源代碼
0x02 對指定evtx文件單條日誌的刪除思路
在上篇文章《Windows XML Event Log (EVTX)單條日誌清除(一)——刪除思路與實例》介紹了evtx日誌文件中刪除單條日誌的原理和一個實例,採用修改日誌長度的方法實現日誌刪除
實現思路如圖
注:
圖片來自https://blog.fox-it.com/2017/12/08/detection-and-recovery-of-nsas-covered-up-tracks/
這種方法在實現上相對簡單,但是需要考慮多種不同的情況:
刪除中間日誌
刪除最後一條日誌
刪除第一條日誌
0x03 刪除中間日誌
方法如下:
-
File header中的Next record identifier值減1
-
重新計算File header中的Checksum
-
重新計算前一日誌長度,共2個位置(偏移4和當前日誌的最後4字節)
-
後續日誌的Event record identifier依次減1
-
ElfChuk中的Last event record number減1
-
ElfChuk中的Last event record identifier減1
-
重新計算ElfChuk中Event records checksum
-
重新計算ElfChuk中Checksum
1、File header中的Next record identifier值減1
讀取日誌文件內容
定義日誌文件格式結構體,對日誌文件格式進行解析
Next record identifier值減1:
FileHeader->NextRecordIdentifier = FileHeader->NextRecordIdentifier-1
2、重新計算File header中的Checksum
計算CRC校驗和的c代碼如下:
unsigned int CRC32[256]; static void init_table() { int i, j; unsigned int crc; for (i = 0; i < 256; i++) { crc = i; for (j = 0; j < 8; j++) { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320; else crc = crc >> 1; } CRC32[i] = crc; } } unsigned int GetCRC32(unsigned char *buf, int len) { unsigned int ret = 0xFFFFFFFF; int i; static char init = 0; if (!init) { init_table(); init = 1; } for (i = 0; i < len; i++) { ret = CRC32[((ret & 0xFF) ^ buf[i])] ^ (ret >> 8); } ret = ~ret; return ret; }
計算File header前120字節的Checksum
代碼如下:
unsigned char *ChecksumBuf = new unsigned char[120]; memcpy(ChecksumBuf, (PBYTE)elfFilePtr, 120); crc32 = GetCRC32(ChecksumBuf, 120);
3、重新計算前一日誌長度,共2個位置(偏移4和當前日誌的最後4字節)
NewSize = CurrentRecord->Size + PrevRecord->Size
更新長度:
PrevRecord->Size = NewSize
(3) 定位後一日誌NextRecord
使用NewSize替換NextRecord起始點前的4字節:
*(PULONG)((PBYTE)NextRecord-4) = NewSize
4、後續日誌的Event record identifier依次減1
遍歷後續日誌,Event record identifier依次減1
需要修改兩個位置:
CurrentRecord->EventRecordIdentifier = CurrentRecord->EventRecordIdentifier-1 CurrentRecord->Template->EventRecordIdentifier = CurrentRecord->Template->EventRecordIdentifier-1
5、ElfChuk中的Last event record number減1
ElfChuk->LastEventRecordNumber = ElfChuk->LastEventRecordNumber-1
6、 ElfChuk中的Last event record identifier減1
ElfChuk->LastEventRecordIdentifier = ElfChuk->LastEventRecordIdentifier-1
7、重新計算ElfChuk中Event records checksum
unsigned char *ChecksumBuf = new unsigned char[currentChunk->FreeSpaceOffset - 512]; memcpy(ChecksumBuf, (PBYTE)currentChunk+512, currentChunk->FreeSpaceOffset - 512); crc32 = GetCRC32(ChecksumBuf, currentChunk->FreeSpaceOffset - 512);
8、 重新計算ElfChuk中Checksum
unsigned char *ChecksumBuf = new unsigned char[504]; memcpy(ChecksumBuf, (PBYTE)currentChunk, 120); memcpy(ChecksumBuf+120, (PBYTE)currentChunk+128, 384); crc32 = GetCRC32(ChecksumBuf, 504);
0x04 刪除最後一條日誌
刪除最後一條日誌在上篇文章《Windows XML Event Log (EVTX)單條日誌清除(一)——刪除思路與實例》做過實例演示,與刪除中間日誌的方法基本相同
區別如下:
-
後續日誌的Event record identifier不需要減1,因爲沒有後續日誌
-
需要重新計算ElfChuk中的Last event record data offset
程序細節如下:
-
重新計算ElfChuk中的Last event record data offset
ElfChuk->LastEventRecordDataOffset = ElfChuk->LastEventRecordDataOffset-LastRecord->Size
0x05 刪除第一條日誌
修改日誌長度的方法不適用於刪除第一條日誌,因爲沒有前一個日誌覆蓋當前日誌
如果想要依舊使用覆蓋長度的方法實現,需要對日誌的文件格式做進一步分析
我們知道,Event Record的內容以Binary XML格式保存
Binary XML格式可參考:
https://github.com/libyal/libevtx/blob/master/documentation/Windows%20XML%20Event%20Log%20(EVTX).asciidoc#4-binary-xml
通過修改Binary XML格式的內容實現合併日誌,需要修改以下內容:
-
Written date and time
-
Template definition Data size
-
Next template definition offset
注:
該方法同樣適用於修改中間日誌和最後一條日誌,所以說,只要理解了日誌格式,刪除的方法不唯一
其他實現的細節見開源代碼,地址如下:
https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecordofFile.cpp
代碼實現了讀取指定日誌文件c:\\test\\Setup.evtx,刪除單條日誌(EventRecordID=14),並保存爲新的日誌文件c:\\test\\SetupNew.evtx
注:在代碼的實現細節上我參考了看雪上的Demo代碼
地址如下:
https://bbs.pediy.com/thread-219313.htm
0x06 小結
本文介紹了刪除evtx文件單條日誌記錄的思路和程序實現細節,開源代碼。刪除單條日誌記錄的方法不唯一。接下來將會介紹刪除當前系統單條日誌記錄的多個方法。