Windows Event Viewer Log (EVT) Single Log Deletion (Part 2) – Program Implementation for Deleting Log Records within a Specified Time Range from EVT Files

Onedaysec
5 min read
0 views
Windows Event Viewer Log (EVT) Single Log Deletion (Part 2) – Program Implementation for Deleting Log Records within a Specified Time Range from EVT Files — One Day Sec default thumbnail

0x00 Preface

---

The second article in the Windows Event Viewer Log (EVT) Single Log Deletion series. It introduces the approach for deleting log records within a specified time range from an EVT file, addresses multiple design considerations in the program implementation, and provides open-source code.

0x01 Introduction

---

This article will cover the following topics:

  • Approach for deleting log records within a specified time range from a given EVT file
  • Program implementation details
  • Open-source code

0x02 Approach for Deleting Log Records within a Specified Time Range from an EVT File

---

Compared to the single log deletion method for EVTX files mentioned in previous articles, the same approach cannot be applied to EVT files.

This is because the EVT file structure does not include a unique value like EventRecordID, making it impossible to locate a specific log entry.

Through analysis, it was found that the log creation time can be used as an input parameter. By specifying a start date and an end date, the log content within that time range can be deleted.

The format of the log creation time is of type time_t, requiring consideration of the conversion between the time_t type and Greenwich Mean Time (GMT).

In terms of program implementation, the approach is as follows:

  • Traverse all logs, filter out those meeting deletion criteria, and save the remaining log content.
  • After filtering, subtract the number of deleted log entries from the Record numbers of subsequent logs.
  • Update the End of file record offset, Last (newest) record number, and Maximum file size in the file header.
  • Update the End of file record offset and Last (newest) record number in the end of file record.

0x03 Conversion between time_t type and Greenwich Mean Time (GMT).

---

Calendar Time

Calendar time, represented by the time_t data type.

Represents 'relative time,' which avoids being affected by time zones; calendar time is the same across different time zones.

time_t type:

Essentially a long integer, representing the number of seconds from 1970-01-01 00:00:00 to the current time.

Defined as follows:

struct tm
{
int tm_sec; // seconds after the minute - [0, 60] including leap second
int tm_min; // minutes after the hour - [0, 59]
int tm_hour; // hours since midnight - [0, 23]
int tm_mday; // day of the month - [1, 31]
int tm_mon; // months since January - [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday - [0, 6]
int tm_yday; // days since January 1 - [0, 365]
int tm_isdst; // daylight savings time flag
};

Note that the year is relative to 1900

Coordinated Universal Time (UTC)

Coordinated Universal Time, also known as World Standard Time, i.e., Greenwich Mean Time (GMT)

There are time zone differences; calculating local time requires considering the time difference

Example C code for type conversion:

Convert Calendar Time to GMT:

#include
#include
int main()
{
__int64 CalTime = 1531788377;
struct tm GmTime;
char GmBuf[26];
_gmtime64_s(&GmTime, &CalTime);
strftime(GmBuf, 26, "%m/%d/%Y %r", &GmTime);
printf("GmTime :%s\n", GmBuf);
return 0;
}

Convert Calendar Time to local time (considering time difference):

#include
#include
int main()
{
__int64 CalTime = 1531788377;
struct tm LocalTime;
char LocalBuf[26];
_localtime64_s(&LocalTime, &CalTime);
strftime(LocalBuf, 26, "%m/%d/%Y %r", &LocalTime);
printf("LocalTime:%s\n",LocalBuf);
return 0;
}

Convert time to Calendar Time:

#include
#include
time_t StringToDatetime(char *str)
{
tm tm_;
int year, month, day, hour, minute, second;
sscanf_s(str, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
tm_.tm_year = year - 1900;
tm_.tm_mon = month - 1;
tm_.tm_mday = day;
tm_.tm_hour = hour-1;
tm_.tm_min = minute;
tm_.tm_sec = second;
tm_.tm_isdst = 0;
time_t t_ = mktime(&tm_);
return t_;
}
int main()
{
time_t sec = StringToDatetime("2018-7-16 17:46:17");
printf("\n%ld\n", sec);
return 0;
}

0x04 Program Implementation Details

---

1. Structure Definitions

File header definition can be referenced at:

https://technet.microsoft.com/zh-cn/library/bb309024

Event records definition can be referenced at:

https://technet.microsoft.com/zh-cn/library/aa363646

End of file record definition can be referenced at:

https://technet.microsoft.com/zh-cn/library/bb309022

Note:

In the program implementation, to avoid redefinition, I modified the structure name of event records

typedef struct _EVTLOGRECORD {
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;
} EVTLOGRECORD, *PEVTLOGRECORD;

2. Filter Conditions

The TimeGenerated field of the end of file record has a fixed structure with the value 0x33333333

During traversal, if TimeGenerated equals 0x33333333, it indicates the end of file record has been located and traversal should terminate

3. Traversal Method

while (currentRecordPtr->TimeGenerated != 0x33333333)
{
if (currentRecordPtr->TimeGeneratedTimeGenerated>EndTimeNum)
{
//not selected evt record,copy it
}
else
{
//delete record
}
currentRecordPtr = nextRecordPtr;
nextRecordPtr = (PEVTLOGRECORD)((PBYTE)nextRecordPtr + nextRecordPtr->Length);
}

4. Log Preservation

Obtain the complete content of the log file by reading the file and save it in an array

If deleting intermediate log content, it is necessary to remove a certain segment from the middle of the array

Here, the approach is to define a new array and, during traversal, only copy logs that meet the conditions

I chose to use memcpy; its advantage is that the first parameter can specify the starting address

5. Deleted Log Count

Count the total number of deleted logs, and subtract the total number of deleted logs from the Record number of subsequent logs

Subtract the total number of deleted logs from the Last (newest) record number of event records and the end of file record

The complete code has been open-sourced, download address:

`An open-source project

sys1.evt download address:

`An open-source project

The program reads the file sys1.evt, deletes logs within the specified time range from 2018-7-16 17:46:17 to 2018-7-16 17:46:40, totaling 4 entries

Generates files sys2.evt and sys3.evt

sys2.evt does not remove trailing empty values

sys3.evt removes trailing empty values

0x05 Summary

---

This article introduces the approach and implementation details for deleting log records within a specified time range in evt files, with open-source code, which differs significantly from the deletion methods for evtx files

Moreover, the method for deleting evt log records within a specified time range on the current system also differs greatly from that for evtx, which will be detailed in the next article

Related Questions & Answers

Why is the TimeGenerated field value 0x33333333 significant in EVT file parsing?

In EVT files, the end‑of‑file record has a fixed structure where the `TimeGenerated` field is set to the constant 0x33333333. During traversal, when the program encounters this value, it knows that the end of valid log records has been reached and can stop processing. This marker is essential because EVT files do not store an explicit record count in a simple way, making the sentinel value necessary for correct iteration.

How does the deletion process for EVT files differ from that for EVTX files, and what will the next article cover?

EVT deletion relies on a time range because of the lack of a unique record identifier, while EVTX deletion can target individual logs using `EventRecordID`. Additionally, the method for deleting EVT log records on the *current system* (active log) differs significantly from the offline file approach. These differences are explored in [Windows Event Viewer Log (EVT) Single Log Deletion (Part 3) — Deleting EVT Log Records for a Specified Time Period on the Current System](/news/windows-event-viewer-log-evt-single-log-deletion-part-3-deleting-evt-log-records-for-a-specified-time-period-on-the-current-system). The EVTX counterpart is covered in [Windows XML Event Log (EVTX) Single Log Deletion (Part 2) – Program Implementation for Deleting Single Log Records in EVTX Files](/news/windows-xml-event-log-evtx-single-log-deletion-part-2-program-implementation-for-deleting-single-log-records-in-evtx-files).

What are the key steps in the program implementation for deleting EVT logs within a specified time range?

The program traverses all log records in the EVT file until it reaches the end‑of‑file record (identified by `TimeGenerated == 0x33333333`). For each record, it checks whether `TimeGenerated` falls within the user‑specified time range; matching records are deleted. The remaining logs are copied into a new array using `memcpy`. After filtering, the record numbers of subsequent logs are decremented by the number of deleted entries, and the file header fields (End of file record offset, Last record number, Maximum file size) and the end‑of‑file record fields are updated accordingly. This approach is documented in the [Windows Event Viewer Log (EVT) Single Log Deletion (Part 2)](/news/windows-event-viewer-log-evt-single-log-deletion-part-2-program-implementation-for-deleting-log-records-within-a-specified-time-range-from-evt-files).

How is the time parameter handled when deleting EVT log records within a time range?

The log creation time in EVT files is stored as a time_t value (calendar time), which represents seconds since 1970-01-01 00:00:00 UTC. The program requires conversion between user‑supplied date/time strings (e.g., "2018-7-16 17:46:17") and time_t using functions like `mktime` and `_gmtime64_s`. Care must be taken to account for time zone differences; local time can be obtained via `_localtime64_s`. These conversions ensure the start and end times correctly match the `TimeGenerated` field in each log record.

Why can't the same single log deletion method used for EVTX files be applied to EVT files?

The EVT file structure does not include a unique identifier like EventRecordID, which is present in EVTX files. Without this unique value, it's impossible to locate and delete a specific log entry directly. Instead, the approach for EVT files uses the log creation time (time_t) as an input parameter, allowing deletion of all logs within a specified time range. This method is detailed in the [Windows Event Viewer Log (EVT) Single Log Deletion (Part 2) – Program Implementation for Deleting Log Records within a Specified Time Range from EVT Files](/news/windows-event-viewer-log-evt-single-log-deletion-part-2-program-implementation-for-deleting-log-records-within-a-specified-time-range-from-evt-files) article.

Continue Reading