Search This Blog

Friday, July 16, 2010

Reading .Evt Files in c#

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.IO;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Collections;
using System.Diagnostics;

namespace EventLogParser
{
    // Show the progress
    public delegate void ProgressHandler(int val, int max);
    // Send any message to UI
    public delegate void MessageHandler(string msg);
    // Parsed a new event.
    public delegate void NewEventFoundHandler(object[] items);

  public  class EventLogParser
    {
        //Constants for api call
        const int NO_ERROR = 0;
        const int ERROR_INSUFFICIENT_BUFFER = 122;

        public event ProgressHandler OnProgress;
        public event MessageHandler OnAction;
        public event NewEventFoundHandler OnFoundRecord;

        uint offset;

        enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public unsafe struct EventLogHeader
        {
            public uint HeaderLength; // length
            public uint Signature; // signature
            public uint Unknown1;
            public uint Unknown2;
            public uint Unknown3;
            public uint FooterOffset;
            public uint NextIndex;
            public uint FileLength; // always wont give correct value
            public uint Unknown6;
            public uint Unknown7;
            public uint Unknown8;
            public uint EndHeaderLength;

            public EventLogHeader(byte[] data)
            {
                fixed (byte* pData = data)
                {
                    this = *(EventLogHeader*)pData;
                }
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public unsafe struct EventLogFooter
        {
            public uint FooterLength; // length
            public uint Unknown0; // 0x11111111
            public uint Unknown1; // 0x22222222
            public uint Unknown2; // 0x33333333
            public uint Unknown3; // 0x44444444
            public uint Unknown4;
            public uint FooterOffset;
            public uint NextIndex;
            public uint Unknown7;
            public uint EndFooterLength;

            public EventLogFooter(byte[] data)
            {
                fixed (byte* pData = data)
                {
                    this = *(EventLogFooter*)pData;
                }
            }
        }

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

            public EventLogRecord(byte[] data)
            {
                fixed (byte* pData = data)
                {
                    this = *(EventLogRecord*)pData;
                }
            }

        }

        [StructLayout(LayoutKind.Sequential)]
        private struct SystemTime
        {
            public short year;
            public short month;
            public short dayOfWeek;
            public short day;
            public short hour;
            public short minute;
            public short second;
            public short milliseconds;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct TimeZoneInformation
        {
            public int bias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string standardName;
            public SystemTime standardDate;
            public int standardBias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string daylightName;
            public SystemTime daylightDate;
            public int daylightBias;
        }

        // Class for holding an event log entry
        public class EventLogEntry
        {
            public EventLogRecord rec;
            public string SourceName;
            public string Computername;
            public string UserSid;
            public string Strings;
            public byte[] Data;
        }

        [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);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern int GetTimeZoneInformation(out TimeZoneInformation lpTimeZoneInformation);

        // Ctor
        public EventLogParser()
        {
            TimeZoneInformation tzi;
            GetTimeZoneInformation(out tzi);
            offset = (uint)(tzi.bias * 60) - (uint)(tzi.daylightBias * 60);
        }

        // Parse the file
        public unsafe void Parse(string filename)
        {
            try
            {
                // Open the file
                using (FileStream fs = new FileStream(filename, FileMode.Open))
                {
                    // Use BinaryReader to read the file
                    using (BinaryReader br = new BinaryReader(fs))
                    {
                        //Read the header of the file
                        byte[] header = new byte[sizeof(EventLogHeader)];
                        br.Read(header, 0, header.Length);
                        EventLogHeader _h = new EventLogHeader(header);
                        // Validate the file
                        if (!Validate(_h))
                        {
                            this.OnAction("Invalid file format.");
                            return;
                        }
                        //
                        int totalEvents = (int)(_h.NextIndex - 1);
                        this.OnAction(String.Format("Found {0} events", totalEvents));
                        // Read the items
                        EventLogEntry e;
                        int cnt = 0;
                        uint offset = _h.FooterOffset;
                        while (true)
                        {
                            byte[] buff = ReadEntry(br, ref offset);
                            e = ReadEntry(buff);
                            cnt++;
                            DateTime dt = GetTime(e.rec.TimeGenerated);
                            this.OnFoundRecord(
                                new object[] {
                                    Enum.GetName(typeof(EventLogEntryType),e.rec.EventType),
                                    dt.ToShortDateString(),
                                    dt.ToShortTimeString(),
                                    e.SourceName,
                                    e.Strings,
                                    e.rec.EventCategory,
                                    e.rec.EventID,
                                    e.UserSid,
                                    e.Computername});
                            if (cnt % 200 == 0) this.OnProgress(cnt, totalEvents);
                            if (offset == 48)
                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.OnAction(String.Format("Error Occured! {0}", ex.Message));
            }
            return;
        }

        // Read an event log record as byte[] from the file.
        private byte[] ReadEntry(BinaryReader br, ref uint endPoint)
        {
            br.BaseStream.Seek(endPoint - 4, SeekOrigin.Begin);
            uint length = br.ReadUInt32();
            endPoint -= length;
            br.BaseStream.Seek(endPoint, SeekOrigin.Begin);
            byte[] buff = new byte[length];
            br.Read(buff, 0, buff.Length);
            return buff;
        }

        // Parse the byte[] as an event log record
        private unsafe EventLogEntry ReadEntry(byte[] buff)
        {
            EventLogEntry entry;
            try
            {
                fixed (byte* ptr = buff)
                {
                    entry = new EventLogEntry();
                    entry.rec = new EventLogRecord(buff);
                    // Read SourceName
                    uint start = (uint)sizeof(EventLogRecord);
                    // Get the Source Name
                    entry.SourceName = ReadString(ptr, ref start);
                    // Get the Computer Name
                    entry.Computername = ReadString(ptr, ref start);
                    // Get the User Name
                    byte[] uname = new byte[entry.rec.UserSidLength];
                    Copy(ptr, ref start, uname, uname.Length);
                    entry.UserSid = GetUserInfo(uname);
                    // read the strings
                    entry.Strings = ReadString(ptr, ref start, (int)(entry.rec.DataOffset - entry.rec.StringOffset) / 2);
                    // read the data
                    entry.Data = new byte[(int)entry.rec.DataLength];
                    Copy(ptr, ref start, entry.Data, entry.Data.Length);
                    //
                }
            }
            catch (Exception)
            {
                entry = null;
            }
            return entry;
        }

        // Check whether the evt file is a valid one.
        private bool Validate(EventLogHeader header)
        {
            if (!(header.HeaderLength == 0x00000030 &&
                    header.Signature == 0x654C664c &&
                    header.Unknown1 == 0x00000001 &&
                    header.Unknown2 == 0x00000001))
                return false;
            return true;
        }

        // Read string from the byte[]
        private unsafe string ReadString(byte* ptr, ref uint start)
        {
            StringBuilder result = new StringBuilder();
            char temp;
            ptr += start;
            while (true)
            {
                temp = (char)*((ushort*)ptr);
                ptr += 2;
                start += 2;
                if (temp == '\0')
                    break;
                result.Append(temp);
            }
            return result.ToString();
        }

        // Read the Description according to the length specified.
        private unsafe string ReadString(byte* ptr, ref uint start, int count)
        {
            StringBuilder result = new StringBuilder(count);
            char temp;
            ptr += start;
            for (; count > 0; count--)
            {
                temp = (char)*((ushort*)ptr);
                ptr += 2;
                start += 2;
                result.Append(temp);
            }
            return result.ToString();
        }

        // Get the user name from SID
        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";
        }

        // copy the byte[]
        private unsafe void Copy(byte* pSrc, ref uint srcIndex, byte[] dst, int count)
        {
            if (count == 0)
                return;
            fixed (byte* pDst = dst)
            {
                byte* ps = pSrc;
                ps += srcIndex;
                byte* pd = pDst;

                // Loop over the count in blocks of 4 bytes, copying an
                // integer (4 bytes) at a time:
                for (int n = 0; n < count / 4; n++)
                {
                    *((int*)pd) = *((int*)ps);
                    pd += 4;
                    ps += 4;
                }

                // Complete the copy by moving any bytes that weren't
                // moved in blocks of 4:
                for (int n = 0; n < count % 4; n++)
                {
                    *pd = *ps;
                    pd++;
                    ps++;
                }

                srcIndex += (uint)count;
            }
        }

        // Convert to seconds to date time format
        private DateTime GetTime(uint time)
        {
            DateTime output = new DateTime(1970, 1, 1, 0, 0, 0);
            time = time - offset;
            output = output.AddSeconds(time);
            return output;
        }
    }
}

No comments:

Post a Comment