Click here to Skip to main content
Licence CPOL
First Posted 23 Aug 2006
Views 70,733
Bookmarked 49 times

Parsing event log(*.evt) file

By | 23 Aug 2006 | Article
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:

[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.

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";
}
[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.

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern int GetTimeZoneInformation(out TimeZoneInformation 
                                                 lpTimeZoneInformation);
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)

About the Author

J a a n s

Software Developer (Senior)

India India

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionTo open .evt files use Event Viewer from Windows PinmemberSergey Chepurin22:57 6 Jul '11  
QuestionHelpful Pinmemberyaron klodovski2:23 23 Jun '11  
GeneralNice work PinmemberBlumen1:12 17 Mar '10  
GeneralRe: Nice work Pinmemberzhbs200219:10 22 Dec '10  
GeneralGreat Information Pinmemberdineshkulhari19:47 26 Jul '09  
GeneralformatMessage Pinmembernikos749:40 19 Jun '09  
GeneralGood PinmemberPortatofe8:43 2 Oct '08  
NewsStructure definitions PinmemberAndreas Saurwein Franci Gonalves11:31 21 Jun '08  
GeneralReally useful Thanks Pinmemberfedwards219:46 6 Jun '08  
Generalnice but does not parse correctly [modified] Pinmembertigerharry3:41 28 Feb '08  
GeneralRe: nice but does not parse correctly PinmemberJ@@NS22:33 2 Mar '08  
GeneralRe: nice but does not parse correctly Pinmembertigerharry23:28 2 Mar '08  
GeneralRe: nice but does not parse correctly Pinmembertigerharry1:45 12 Jun '08  
NewsMSDN updated its contents PinmemberJ@@NS21:53 21 Jan '08  
GeneralCool! PinmemberDaniel Carvalho Liedke13:20 5 Nov '07  
GeneralVISTA Pinmemberyuyamire4:25 26 Aug '07  
GeneralParsing event log(*.evt) file in ASP.net Pinmemberjoesanchez11:24 13 Apr '07  
GeneralRe: Parsing event log(*.evt) file in ASP.net PinmemberJ@@NS8:32 17 Apr '07  
GeneralParsing event log(*.evt) file Pinmemberpradeep.c.v20:29 26 Nov '06  
GeneralRe: Parsing event log(*.evt) file Pinmemberjoesanchez11:23 13 Apr '07  
GeneralRe: Parsing event log(*.evt) file Pinmemberstome22:55 2 May '07  
GeneralEventID wrongly parsed Pinmemberjackraz@gmail.com20:30 14 Sep '06  
GeneralRe: EventID wrongly parsed PinmemberTelsum19:08 3 Oct '06  
I've found with the event id is parsed incorrectly, but a simple enough fix.
In the new object for the event record change
e.rec.EventID,
to
e.rec.EventID & 0xffff,
 
This will show you the same event ID's you see in windows event viewer.
GeneralRe: EventID wrongly parsed PinmemberJ@@NS3:01 9 Oct '06  
GeneralRe: EventID wrongly parsed PinmemberTelsum17:09 9 Oct '06  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120529.1 | Last Updated 23 Aug 2006
Article Copyright 2006 by J a a n s
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid