Pointers and Protected Memory in C#

2

I am working on a C# project to search the contents of windows event log files. It needs to be compatible with .evt and .evtx files.

I copied the code from http://www.codeproject.com/Articles/15288/Parsing-event-log-evt-file that reads .evt files and in the case of some files (not all) when I run it, I get the error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt.". This happens on the line:

*((int*)pd) = *((int*)ps);

The entire listing is:

namespace EventLogParser
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;

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

    internal class EventLogParser
    {
        //Constants for api call
        private const int NoError = 0;
        private const int ErrorInsufficientBuffer = 122;
        private readonly uint _offset;

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

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

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool LookupAccountSid(
            string lpSystemName,
            [MarshalAs(UnmanagedType.LPArray)] byte[] sid,
            StringBuilder lpName,
            ref uint cchName,
            StringBuilder referencedDomainName,
            ref uint cchReferencedDomainName,
            out SidNameUse peUse);

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

        // Ctor

        // Parse the file
        public unsafe void Parse(string filename)
        {
            //try
            //{
            // Open the file
            using (var fs = new FileStream(filename, FileMode.Open))
            {
                // Use BinaryReader to read the file
                using (var br = new BinaryReader(fs))
                {
                    //Read the header of the file
                    var header = new byte[sizeof(EventLogHeader)];
                    br.Read(header, 0, header.Length);
                    var eventLogHeader = new EventLogHeader(header);
                    // Validate the file
                    if (!eventLogHeader.IsValid())
                    {
                        OnAction("Invalid file format.");
                        return;
                    }
                    //
                    var totalEvents = (int)(eventLogHeader.NextIndex - 1);
                    OnAction(String.Format("Found {0} events", totalEvents));
                    // Read the items
                    var cnt = 0;
                    var offset = eventLogHeader.FooterOffset;
                    while (true)
                    {
                        var buff = ReadEntry(br, ref offset);
                        var e = ReadEntry(buff);
                        if (e == null)
                            continue;
                        cnt++;
                        var dt = GetTime(e.Rec.TimeGenerated);
                        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) OnProgress(cnt, totalEvents);
                        if (offset == 48)
                            break;
                    }
                }
            }
            //}
            //catch (Exception ex)
            //{
            //    OnAction(String.Format("Error Occurred! {0}", ex.Message));
            //}
        }

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

            if (br.BaseStream.Position >= br.BaseStream.Length)
            {
                return new byte[0];
            }

            var length = br.ReadUInt32();
            endPoint -= length;
            br.BaseStream.Seek(endPoint, SeekOrigin.Begin);
            var 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 { Rec = new EventLogRecord(buff) };
                    // Read SourceName
                    var 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
                    var 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;
        }

        // Read string from the byte[]
        private unsafe string ReadString(byte* ptr, ref uint start)
        {
            var result = new StringBuilder();
            ptr += start;
            while (true)
            {
                var 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)
        {
            var result = new StringBuilder(count);
            ptr += start;
            for (; count > 0; count--)
            {
                var 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)
        {
            var name = new StringBuilder();
            var cchName = (uint)name.Capacity;
            var referencedDomainName = new StringBuilder();
            var cchReferencedDomainName = (uint)referencedDomainName.Capacity;
            SidNameUse sidUse;

            int err = NoError;
            if (
                !LookupAccountSid(null, buff, name, ref cchName, referencedDomainName, ref cchReferencedDomainName,
                    out sidUse))
            {
                err = Marshal.GetLastWin32Error();
                if (err == ErrorInsufficientBuffer)
                {
                    name.EnsureCapacity((int)cchName);
                    referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);
                    err = NoError;
                    if (
                        !LookupAccountSid(null, buff, name, ref cchName, referencedDomainName,
                            ref cchReferencedDomainName, out sidUse))
                        err = Marshal.GetLastWin32Error();
                }
            }
            if (err == 0)
                return String.Format(@"{0}\{1}", referencedDomainName, name);
            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)
            {
                var 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)
        {
            var output = new DateTime(1970, 1, 1, 0, 0, 0);
            time = time - _offset;
            output = output.AddSeconds(time);
            return output;
        }

        public class EventLogEntry
        {
            public string Computername;
            public byte[] Data;
            public string SourceName;
            public string Strings;
            public string UserSid;
            public EventLogRecord Rec;
        }

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

            /// <summary>
            /// Checks whether the evt file is a valid one.
            /// </summary>
            public bool IsValid()
            {
                return HeaderLength == 0x00000030 &&
                       Signature == 0x654C664c &&
                       Unknown1 == 0x00000001 &&
                       Unknown2 == 0x00000001;
            }
        }

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

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

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

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

Does anyone know what could be done to fix this?

Any help would be greatly appreciated.

c#
.net
pointers
memory
asked on Stack Overflow Oct 30, 2013 by Ryan Penfold • edited Oct 30, 2013 by Chirag Desai

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0