Click here to Skip to main content
15,171,993 members
Articles / Programming Languages / C#
Article
Posted 23 Aug 2006

Stats

152.7K views
4.6K downloads
50 bookmarked

Parsing event log(*.evt) file

Rate me:
Please Sign up or sign in to vote.
4.44/5 (21 votes)
23 Aug 2006CPOL3 min read
An article on parsing/opening event log files(*.evt) using c#

Sample Image - EventLogParser.jpg

Introduction

Some days back, I was searching for code snippets for opening an event log file. Even though there are a lot of samples available for getting the event log of a local system, there was no help for opening a *.evt file. So, I thought of coming up with this article.

Using the code

The code included uses unsafe methods. So while compiling, use the /unsafe switch.

Reading the event log file

The structure of an event log file is a little complex. The file has a 48 byte header which we can use to validate it. The bytes 5 - 8 will hold the signature of the file, which is a uint (DWORD) value that is always set to ELF_LOG_SIGNATURE (the value is 0x654c664c), which is ASCII for eLfL. Bytes 25 - 28 will store the index for the next event log entry. The uint value from bytes 21 - 24 will have the position where the next entry will be recorded.

Each event log record entered in the log file will have the following structure:

C#
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct EventLogRecord
{
    public uint Length;
    public uint Reserved;
    public uint RecordNumber;
    public uint TimeGenerated;
    public uint TimeWritten;
    public uint EventID;

    public ushort EventType;
    public ushort NumStrings;
    public ushort EventCategory;
    public ushort ReservedFlags;
    public uint ClosingRecordNumber;
    public uint StringOffset;
    public uint UserSidLength;
    public uint UserSidOffset;
    public uint DataLength;
    public uint DataOffset;

    // 
    // Followed by: 
    // 
    // String SourceName 
    // String Computername 
    // SID   UserSid 
    // String[] Description 
    // byte[]  Data 
    // char[]  Pad
    // uint Length 
    // 
}

Length is the size of this event record, in bytes. TimeGenerated is the time at which this entry was submitted. This time is measured in the number of seconds elapsed since 00:00:00 January 1, 1970, Universal Coordinated Time. TimeWritten is the time at which this entry was received by the service to be written to the log. This time is measured in the number of seconds elapsed since 00:00:00 January 1, 1970, Universal Coordinated Time.

We can clearly see how the structure would be saved in the log file, but this structure is different to many others. Since this particular structure doesn't have a fixed size, it has a fixed part and a variable part. The fixed part occupies 56 bytes, and the variable part has a size which is the difference of the 'Length' property and the fixed part.

The property 'SourceName' starts at position 57, but we don't know where it ends. The end of the data is marked by the character '\0'. The next property 'Computername' also works the same way as the previous one, ending with the character '\0'. Now, if we observe how the property "UserSid" is made up, we can see that it starts at the offset defined in the property 'UserSidOffset' and the length is marked by 'UserSidLength', which marks the number of characters that have to be counted from the starting position. The property 'Description' uses the same model as 'UserSid', with the difference that there is nobody to tell it how many characters should be counted from the beginning, and neither would it be valid to count characters until a "\0" is encountered, since there can be as many of these as there are strings found to save in the event. To be able to find the end of this property, we need to subtract 'DataOffset' from the property 'StringOffset'.

The SID obtained has to be converted to UserName using the following function, with the help of the LookupAccountSid API.

C#
private string GetUserInfo(byte[] buff)
{
    StringBuilder name = new StringBuilder();
    uint cchName = (uint)name.Capacity;
    StringBuilder referencedDomainName = new StringBuilder();
    uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
    SID_NAME_USE sidUse;

    int err = NO_ERROR;
    if (!LookupAccountSid(null, buff, name, ref cchName, 
         referencedDomainName, ref cchReferencedDomainName, out sidUse))
    {
        err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
        if (err == ERROR_INSUFFICIENT_BUFFER)
        {
            name.EnsureCapacity((int)cchName);
            referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);
            err = NO_ERROR;
            if (!LookupAccountSid(null, buff, name, ref cchName, 
                                  referencedDomainName, 
                                  ref cchReferencedDomainName, 
                                  out sidUse))
                err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
        }
    }
    if (err == 0)
        return String.Format(@"{0}\{1}", 
               referencedDomainName.ToString(), name.ToString());
    else
        return @"N\A";
}
C#
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
    string lpSystemName,
    [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
    System.Text.StringBuilder lpName,
    ref uint cchName,
    System.Text.StringBuilder ReferencedDomainName,
    ref uint cchReferencedDomainName,
    out SID_NAME_USE peUse);

'TimeWritten' has to be converted to a valid DateTime format. With the help of the GetTimeZoneInformation API, we can achieve this.

C#
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern int GetTimeZoneInformation(out TimeZoneInformation 
                                                 lpTimeZoneInformation);
C#
private DateTime GetTime(uint time)
{
    TimeZoneInformation tzi;
    uint offset;
    GetTimeZoneInformation(out tzi);
    offset = (uint)(tzi.bias * 60) - (uint)(tzi.daylightBias * 60);
    DateTime output = new DateTime(1970, 1, 1, 0, 0, 0);
    time = time - offset;
    output = output.AddSeconds(time);
    return output;
}

Points of interest

If you have any suggestions or questions, you are welcome!

TODO: Use the FormatMessage API or suitable methods for formatting the description.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

J a a n s
Software Developer (Senior) NA
United States United States
No Biography provided

Comments and Discussions

 
QuestionHow to Get LogName from EVTX file Pin
Elvin Rejimone19-Nov-19 23:59
MemberElvin Rejimone19-Nov-19 23:59 
Questioncan not open .evtx Pin
Member 126259608-Jul-16 16:42
MemberMember 126259608-Jul-16 16:42 
Questioncannot open evt files Pin
Member 1246821018-Apr-16 8:31
MemberMember 1246821018-Apr-16 8:31 
Question.evtx parser Pin
selvan from Chennai, Tamil Nadu19-Aug-14 1:48
Memberselvan from Chennai, Tamil Nadu19-Aug-14 1:48 
AnswerRe: .evtx parser Pin
Klaps5-Nov-14 13:05
MemberKlaps5-Nov-14 13:05 
QuestionConverting .evtx files Pin
Member 104927743-Jul-14 22:18
MemberMember 104927743-Jul-14 22:18 
QuestionThis is parsing from bottom going up. Pin
Flip Booth9-Jun-14 8:15
MemberFlip Booth9-Jun-14 8:15 
QuestionNeed Sample .evt File Pin
email4abhay24-May-14 10:26
Memberemail4abhay24-May-14 10:26 
QuestionTo open .evt files use Event Viewer from Windows Pin
Sergey Chepurin6-Jul-11 23:57
MemberSergey Chepurin6-Jul-11 23:57 
QuestionHelpful Pin
Crazy Joe Devola23-Jun-11 3:23
MemberCrazy Joe Devola23-Jun-11 3:23 
GeneralNice work Pin
Blumen17-Mar-10 2:12
MemberBlumen17-Mar-10 2:12 
GeneralRe: Nice work Pin
zhbs200222-Dec-10 20:10
Memberzhbs200222-Dec-10 20:10 
GeneralGreat Information Pin
dineshkulhari26-Jul-09 20:47
Memberdineshkulhari26-Jul-09 20:47 
GeneralformatMessage Pin
nikos7419-Jun-09 10:40
Membernikos7419-Jun-09 10:40 
GeneralGood Pin
Portatofe2-Oct-08 9:43
MemberPortatofe2-Oct-08 9:43 
NewsStructure definitions Pin
Andreas Saurwein21-Jun-08 12:31
MemberAndreas Saurwein21-Jun-08 12:31 
GeneralReally useful Thanks Pin
fedwards26-Jun-08 20:46
Memberfedwards26-Jun-08 20:46 
Generalnice but does not parse correctly [modified] Pin
Binkle@JAM28-Feb-08 4:41
MemberBinkle@JAM28-Feb-08 4:41 
GeneralRe: nice but does not parse correctly Pin
J a a n s2-Mar-08 23:33
professionalJ a a n s2-Mar-08 23:33 
GeneralRe: nice but does not parse correctly Pin
Binkle@JAM3-Mar-08 0:28
MemberBinkle@JAM3-Mar-08 0:28 
GeneralRe: nice but does not parse correctly Pin
Binkle@JAM12-Jun-08 2:45
MemberBinkle@JAM12-Jun-08 2:45 
NewsMSDN updated its contents Pin
J a a n s21-Jan-08 22:53
professionalJ a a n s21-Jan-08 22:53 
GeneralCool! Pin
Daniel Carvalho Liedke5-Nov-07 14:20
MemberDaniel Carvalho Liedke5-Nov-07 14:20 
GeneralVISTA Pin
yuyamire26-Aug-07 5:25
Memberyuyamire26-Aug-07 5:25 
GeneralParsing event log(*.evt) file in ASP.net Pin
joesanchez13-Apr-07 12:24
Memberjoesanchez13-Apr-07 12:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.