public byte[] GetBytes() { List <byte> data = new List <byte>(); data.Add((byte)Type); data.AddRange(BitConverter.GetBytes(SequenceNumber)); var epBytes = Encoding.UTF8.GetBytes(EndPoint); if (epBytes.Length > byte.MaxValue) { throw new FormatException("EndPoint string is too long."); } data.Add((byte)epBytes.Length); data.AddRange(epBytes); var payload = GetPayload(); if (payload.Length > ushort.MaxValue) { throw new FormatException("Payload size is too big"); } data.AddRange(NumberConverter.GetBytes((ushort)payload.Length)); data.AddRange(payload); byte[] bytes = new byte[data.Count + 4]; data.CopyTo(bytes); Crc32Algorithm.ComputeAndWriteToEnd(bytes); return(bytes); }
public static byte[] PrepareBodyToSend(byte[] someData) { byte[] someDataWithCrc = new byte[someData.Length + 4]; someData.CopyTo(someDataWithCrc, 0); Crc32Algorithm.ComputeAndWriteToEnd(someDataWithCrc); return(someDataWithCrc); }
public static byte[] HeaderByteFormat(int messageCode, int length, int packetNum) { var memoryStream = new MemoryStream(); var binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(messageCode); binaryWriter.Write(length); binaryWriter.Write(packetNum); binaryWriter.Write(0); // for crc binaryWriter.Close(); byte[] data = memoryStream.ToArray(); uint crc = Crc32Algorithm.ComputeAndWriteToEnd(data); return(data); }
public EventLog(Stream fileStream) { _stream = fileStream; var headerBytes = new byte[4096]; fileStream.Read(headerBytes, 0, 4096); if (BitConverter.ToInt64(headerBytes, 0) != EventSignature) { throw new Exception("Invalid signature! Expected 'ElfFile'"); } FirstChunkNumber = BitConverter.ToInt64(headerBytes, 0x8); LastChunkNumber = BitConverter.ToInt64(headerBytes, 0x10); NextRecordId = BitConverter.ToInt64(headerBytes, 0x18); var unusedSize = BitConverter.ToInt32(headerBytes, 0x20); MinorVersion = BitConverter.ToInt16(headerBytes, 0x24); MajorVersion = BitConverter.ToInt16(headerBytes, 0x26); var unusedHeaderSize = BitConverter.ToInt16(headerBytes, 0x28); ChunkCount = BitConverter.ToUInt16(headerBytes, 0x2A); Flags = (EventLogFlag)BitConverter.ToInt32(headerBytes, 0x78); Crc = BitConverter.ToInt32(headerBytes, 0x7C); var inputArray = new byte[120 + 4]; Buffer.BlockCopy(headerBytes, 0, inputArray, 0, 120); Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC CalculatedCrc = BitConverter.ToInt32(inputArray, inputArray.Length - 4); ErrorRecords = new Dictionary <long, string>(); }
public ChunkInfo(byte[] chunkBytes, long absoluteOffset, int chunkNumber) { var l = LogManager.GetLogger("ChunkInfo"); l.Trace( $"\r\n-------------------------------------------- NEW CHUNK at 0x{absoluteOffset:X} ------------------------------------------------\r\n"); ChunkBytes = chunkBytes; AbsoluteOffset = absoluteOffset; ChunkNumber = chunkNumber; ErrorRecords = new Dictionary <long, string>(); EventIdMetrics = new Dictionary <long, int>(); EventRecords = new List <EventRecord>(); FirstEventRecordNumber = BitConverter.ToInt64(chunkBytes, 0x8); LastEventRecordNumber = BitConverter.ToInt64(chunkBytes, 0x10); FirstEventRecordIdentifier = BitConverter.ToInt64(chunkBytes, 0x18); LastEventRecordIdentifier = BitConverter.ToInt64(chunkBytes, 0x20); if (FirstEventRecordIdentifier == -1) { return; } var tableOffset = BitConverter.ToUInt32(chunkBytes, 0x28); LastRecordOffset = BitConverter.ToUInt32(chunkBytes, 0x2C); FreeSpaceOffset = BitConverter.ToUInt32(chunkBytes, 0x30); //TODO how to calculate this? across what data? all event records? var crcEventRecordsData = BitConverter.ToUInt32(chunkBytes, 0x34); Crc = BitConverter.ToInt32(chunkBytes, 0x7c); var inputArray = new byte[120 + 384 + 4]; Buffer.BlockCopy(chunkBytes, 0, inputArray, 0, 120); Buffer.BlockCopy(chunkBytes, 128, inputArray, 120, 384); Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC CalculatedCrc = BitConverter.ToInt32(inputArray, inputArray.Length - 4); var index = 0; var tableData = new byte[0x100]; Buffer.BlockCopy(chunkBytes, (int)tableOffset, tableData, 0, 0x100); StringTableEntries = new Dictionary <uint, StringTableEntry>(); var stringOffsets = new List <uint>(); while (index < tableData.Length) { var stringOffset = BitConverter.ToUInt32(tableData, index); index += 4; if (stringOffset == 0) { continue; } stringOffsets.Add(stringOffset); } foreach (var stringOffset in stringOffsets) { GetStringTableEntry(stringOffset); } l.Trace("String table entries"); foreach (var stringTableEntry in StringTableEntries.Keys.OrderBy(t => t)) { l.Trace(StringTableEntries[stringTableEntry]); } l.Trace(""); var templateTableData = new byte[0x80]; Buffer.BlockCopy(chunkBytes, 0x180, templateTableData, 0, 0x80); var tableTemplateOffsets = new List <uint>(); index = 0; while (index < templateTableData.Length) { var templateOffset = BitConverter.ToUInt32(templateTableData, index); index += 4; if (templateOffset == 0) { continue; } //the actual table definitions live at this Offset + 0x1000 for header, - 10 bytes for some reason. //This is where the 0xc op code will be tableTemplateOffsets.Add(templateOffset); } Templates = new Dictionary <int, Template>(); //to get all the templates and cache them foreach (var tableTemplateOffset in tableTemplateOffsets.OrderBy(t => t)) { var actualOffset = absoluteOffset + tableTemplateOffset - 10; //yea, -10 index = (int)tableTemplateOffset - 10; l.Trace( $"Chunk absoluteOffset: 0x{AbsoluteOffset:X} tableTemplateOffset: 0x{tableTemplateOffset:X} actualOffset: 0x {actualOffset:X} chunkBytes[index]: 0x{chunkBytes[index]:X} LastRecordOffset 0x{LastRecordOffset:X} FreeSpaceOffset 0x{FreeSpaceOffset:X}"); var template = GetTemplate(index); if (template == null) { l.Trace( $"Implausable template at actual offset: 0x{actualOffset} tableTemplateOffset 0x{tableTemplateOffset:X} FreeSpaceOffset: 0x{FreeSpaceOffset} chunk absolute offset: 0x{AbsoluteOffset:X}"); continue; } if (Templates.ContainsKey(template.TemplateOffset) == false) { Templates.Add(template.TemplateOffset - 0x18, template); } if (template.NextTemplateOffset <= 0) { continue; } var nextTemplateId = template.NextTemplateOffset; while (nextTemplateId > 0) { var bbb = GetTemplate(nextTemplateId - 10); nextTemplateId = bbb.NextTemplateOffset; if (Templates.ContainsKey(bbb.TemplateOffset) == false) { Templates.Add(bbb.TemplateOffset - 0x18, bbb); } } } l.Trace("Template definitions"); foreach (var esTemplate in Templates.OrderBy(t => t.Key)) { l.Trace($"key: 0x{esTemplate.Key:X4} {esTemplate.Value}"); } l.Trace(""); index = (int)tableOffset + 0x100 + 0x80; //get to start of event Records l.Debug($"\r\nChunk data before processing records: {this}"); const int recordSig = 0x2a2a; while (index < chunkBytes.Length) { var sig = BitConverter.ToInt32(chunkBytes, index); if (sig != recordSig) { l.Trace( $"Found an invalid signature at 0x{absoluteOffset + index:X}"); break; } var recordOffset = index; //do not read past the last known defined record if (recordOffset - absoluteOffset > LastRecordOffset) { l.Trace( "Reached last record offset. Stopping"); break; } var recordSize = BitConverter.ToUInt32(chunkBytes, index + 4); var recordNumber = BitConverter.ToInt64(chunkBytes, index + 8); try { if (recordNumber < FirstEventRecordIdentifier || recordNumber > LastEventRecordIdentifier) { //outside known good range, so ignore l.Debug( $"Record at offset 0x{AbsoluteOffset + recordOffset:X} falls outside valid record identifier range. Skipping"); break; } var ms = new MemoryStream(chunkBytes, index, (int)recordSize); var br = new BinaryReader(ms, Encoding.UTF8); index += (int)recordSize; var er = new EventRecord(br, recordOffset, this); l.Debug(er); EventRecords.Add(er); if (EventIdMetrics.ContainsKey(er.EventId) == false) { EventIdMetrics.Add(er.EventId, 0); } EventIdMetrics[er.EventId] += 1; } catch (Exception e) { l.Trace( $"First event record ident-num: {FirstEventRecordIdentifier}-{FirstEventRecordNumber} Last event record ident-num: {LastEventRecordIdentifier}-{LastEventRecordNumber} last record offset 0x{LastRecordOffset:X}"); l.Error( $"Record error at offset 0x{AbsoluteOffset + recordOffset:X}, record #: {recordNumber} error: {e.Message}"); if (ErrorRecords.ContainsKey(recordNumber) == false) { ErrorRecords.Add(recordNumber, e.Message); } } } }
private void start() { int numBytesRead = 0; int msgOffset = 0; while (_isProcessing) { var n = ReadFromStream(prelude); numBytesRead += n; n = ReadFromStream(preludeCRC); var preludeCRCBytes = preludeCRC.ToArray(); if (BitConverter.IsLittleEndian) { Array.Reverse(preludeCRCBytes); } numBytesRead += n; var inputArray = new byte[prelude.Length + 4]; System.Buffer.BlockCopy(prelude, 0, inputArray, 0, prelude.Length); // write real data to inputArray Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC // transferring data or writing reading, and checking as final operation if (!Crc32Algorithm.IsValidWithCrcAtEnd(inputArray)) { throw new ArgumentException("invalid prelude CRC"); } if (!Enumerable.SequenceEqual(inputArray.Skip(prelude.Length).Take(4), preludeCRCBytes)) { throw new ArgumentException("Prelude CRC Mismatch"); } var bytes = prelude.Take(4).ToArray(); if (BitConverter.IsLittleEndian) { Array.Reverse(bytes); } int totalLength = BitConverter.ToInt32(bytes, 0); bytes = prelude.Skip(4).Take(4).ToArray(); if (BitConverter.IsLittleEndian) { Array.Reverse(bytes); } int headerLength = BitConverter.ToInt32(bytes, 0); int payloadLength = totalLength - headerLength - 16; var headers = new byte[headerLength]; var payload = new byte[payloadLength]; int num = ReadFromStream(headers); if (num != headerLength) { throw new IOException("insufficient data"); } num = ReadFromStream(payload); if (num != payloadLength) { throw new IOException("insufficient data"); } numBytesRead += num; num = ReadFromStream(messageCRC); var messageCRCBytes = messageCRC.ToArray(); if (BitConverter.IsLittleEndian) { Array.Reverse(messageCRCBytes); } // now verify message CRC inputArray = new byte[totalLength]; System.Buffer.BlockCopy(prelude, 0, inputArray, 0, prelude.Length); System.Buffer.BlockCopy(preludeCRC, 0, inputArray, prelude.Length, preludeCRC.Length); System.Buffer.BlockCopy(headers, 0, inputArray, prelude.Length + preludeCRC.Length, headerLength); System.Buffer.BlockCopy(payload, 0, inputArray, prelude.Length + preludeCRC.Length + headerLength, payloadLength); // write real data to inputArray Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC // transferring data or writing reading, and checking as final operation if (!Crc32Algorithm.IsValidWithCrcAtEnd(inputArray)) { throw new ArgumentException("invalid message CRC"); } if (!Enumerable.SequenceEqual(inputArray.Skip(totalLength - 4).Take(4), messageCRCBytes)) { throw new ArgumentException("message CRC Mismatch"); } Dictionary <String, String> headerMap = extractHeaders(headers); string value = null; if (headerMap.TryGetValue(":message-type", out value)) { if (value.Equals(":error")) { string errorCode = null; string errorMessage = null; headerMap.TryGetValue(":error-code", out errorCode); headerMap.TryGetValue(":error-message", out errorMessage); throw new SelectObjectContentException(errorCode + ":" + errorMessage); } } if (headerMap.TryGetValue(":event-type", out value)) { if (value.Equals("End")) { // throw new UnexpectedShortReadException("Insufficient data"); this._isProcessing = false; break; } if (value.Equals("Cont") || payloadLength < 1) { continue; } if (value.Equals("Progress")) { ProgressMessage progress = new ProgressMessage(); using (var stream = new MemoryStream(payload)) progress = (ProgressMessage) new XmlSerializer(typeof(ProgressMessage)).Deserialize(stream); this.Progress = progress; } if (value.Equals("Stats")) { Console.WriteLine("payload|" + Encoding.UTF7.GetString(payload)); StatsMessage stats = new StatsMessage(); using (var stream = new MemoryStream(payload)) stats = (StatsMessage) new XmlSerializer(typeof(StatsMessage)).Deserialize(stream); this.Stats = stats; } if (value.Equals("Records")) { this.Payload.Write(payload, 0, payloadLength); continue; } } } this._isProcessing = false; this.Payload.Seek(0, SeekOrigin.Begin); this.payloadStream.Close(); }
public EventLog(Stream fileStream) { const long eventSignature = 0x00656c6946666c45; const long chunkSignature = 0x6B6E6843666C45; var headerBytes = new byte[4096]; fileStream.Read(headerBytes, 0, 4096); if (BitConverter.ToInt64(headerBytes, 0) != eventSignature) { throw new Exception("Invalid signature! Expected 'ElfFile'"); } FirstChunkNumber = BitConverter.ToInt64(headerBytes, 0x8); LastChunkNumber = BitConverter.ToInt64(headerBytes, 0x10); NextRecordId = BitConverter.ToInt64(headerBytes, 0x18); var unusedSize = BitConverter.ToInt32(headerBytes, 0x20); MinorVersion = BitConverter.ToInt16(headerBytes, 0x24); MajorVersion = BitConverter.ToInt16(headerBytes, 0x26); var unusedHeaderSize = BitConverter.ToInt16(headerBytes, 0x28); ChunkCount = BitConverter.ToInt16(headerBytes, 0x2A); Flags = (EventLogFlag)BitConverter.ToInt32(headerBytes, 0x78); Crc = BitConverter.ToInt32(headerBytes, 0x7C); var inputArray = new byte[120 + 4]; Buffer.BlockCopy(headerBytes, 0, inputArray, 0, 120); Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC CalculatedCrc = BitConverter.ToInt32(inputArray, inputArray.Length - 4); //we are at offset 0x1000 and ready to start //chunk size == 65536, or 0x10000 var chunkBuffer = new byte[0x10000]; Chunks = new List <ChunkInfo>(); var chunkOffset = fileStream.Position; var bytesRead = fileStream.Read(chunkBuffer, 0, 0x10000); EventIdMetrics = new Dictionary <long, int>(); Logger.Trace($"Event Log data before processing chunks:\r\n{this}"); var chunkNumber = 0; while (bytesRead > 0) { var chunkSig = BitConverter.ToInt64(chunkBuffer, 0); Logger.Trace( $"chunk offset: 0x{chunkOffset:X}, sig: {Encoding.ASCII.GetString(chunkBuffer, 0, 8)} signature val: 0x{chunkSig:X}"); if (chunkSig == chunkSignature) { var ci = new ChunkInfo(chunkBuffer, chunkOffset, chunkNumber); Chunks.Add(ci); TotalEventLogs += ci.EventRecords.Count; } else { Logger.Trace($"Skipping chunk at 0x{chunkOffset:X} as it does not have correct signature"); } chunkOffset = fileStream.Position; bytesRead = fileStream.Read(chunkBuffer, 0, 0x10000); chunkNumber += 1; } ErrorRecords = new Dictionary <long, string>(); foreach (var chunkInfo in Chunks) { foreach (var eventIdMetric in chunkInfo.EventIdMetrics) { if (EventIdMetrics.ContainsKey(eventIdMetric.Key) == false) { EventIdMetrics.Add(eventIdMetric.Key, 0); } EventIdMetrics[eventIdMetric.Key] += eventIdMetric.Value; } foreach (var chunkInfoErrorRecord in chunkInfo.ErrorRecords) { ErrorRecords.Add(chunkInfoErrorRecord.Key, chunkInfoErrorRecord.Value); } } }
public ChunkInfo(byte[] chunkBytes, long absoluteOffset, int chunkNumber) { var l = LogManager.GetLogger("ChunkInfo"); l.Trace( $"\r\n-------------------------------------------- NEW CHUNK at 0x{absoluteOffset:X} ------------------------------------------------\r\n"); ChunkBytes = chunkBytes; AbsoluteOffset = absoluteOffset; ChunkNumber = chunkNumber; ErrorRecords = new Dictionary <long, string>(); EventRecords = new List <EventRecord>(); FirstEventRecordNumber = BitConverter.ToInt64(chunkBytes, 0x8); LastEventRecordNumber = BitConverter.ToInt64(chunkBytes, 0x10); FirstEventRecordIdentifier = BitConverter.ToInt64(chunkBytes, 0x18); LastEventRecordIdentifier = BitConverter.ToInt64(chunkBytes, 0x20); if (FirstEventRecordIdentifier == -1) { return; } var tableOffset = BitConverter.ToUInt32(chunkBytes, 0x28); LastRecordOffset = BitConverter.ToUInt32(chunkBytes, 0x2C); FreeSpaceOffset = BitConverter.ToUInt32(chunkBytes, 0x30); //TODO how to calculate this? across what data? all event records? var crcEventRecordsData = BitConverter.ToUInt32(chunkBytes, 0x34); Crc = BitConverter.ToInt32(chunkBytes, 0x7c); var inputArray = new byte[120 + 384 + 4]; Buffer.BlockCopy(chunkBytes, 0, inputArray, 0, 120); Buffer.BlockCopy(chunkBytes, 128, inputArray, 120, 384); Crc32Algorithm.ComputeAndWriteToEnd(inputArray); // last 4 bytes contains CRC CalculatedCrc = BitConverter.ToInt32(inputArray, inputArray.Length - 4); var index = 0; var tableData = new byte[0x100]; Buffer.BlockCopy(chunkBytes, (int)tableOffset, tableData, 0, 0x100); StringTableEntries = new Dictionary <uint, StringTableEntry>(); var stringOffsets = new List <uint>(); var ticksForTimeDelta = 10000000 * EventLog.TimeDiscrepancyThreshold; //10000000 == ticks in a second while (index < tableData.Length) { var stringOffset = BitConverter.ToUInt32(tableData, index); index += 4; if (stringOffset == 0) { continue; } stringOffsets.Add(stringOffset); } foreach (var stringOffset in stringOffsets) { GetStringTableEntry(stringOffset); } l.Trace("String table entries"); foreach (var stringTableEntry in StringTableEntries.Keys.OrderBy(t => t)) { l.Trace(StringTableEntries[stringTableEntry]); } l.Trace(""); var templateTableData = new byte[0x80]; Buffer.BlockCopy(chunkBytes, 0x180, templateTableData, 0, 0x80); var tableTemplateOffsets = new List <uint>(); index = 0; while (index < templateTableData.Length) { var templateOffset = BitConverter.ToUInt32(templateTableData, index); index += 4; if (templateOffset == 0) { continue; } //the actual table definitions live at this Offset + 0x1000 for header, - 10 bytes for some reason. //This is where the 0xc op code will be tableTemplateOffsets.Add(templateOffset); } Templates = new Dictionary <int, Template>(); //to get all the templates and cache them foreach (var tableTemplateOffset in tableTemplateOffsets.OrderBy(t => t)) { var actualOffset = absoluteOffset + tableTemplateOffset - 10; //yea, -10 index = (int)tableTemplateOffset - 10; l.Trace( $"Chunk absoluteOffset: 0x{AbsoluteOffset:X} tableTemplateOffset: 0x{tableTemplateOffset:X} actualOffset: 0x {actualOffset:X} chunkBytes[index]: 0x{chunkBytes[index]:X} LastRecordOffset 0x{LastRecordOffset:X} FreeSpaceOffset 0x{FreeSpaceOffset:X}"); var template = GetTemplate(index); if (template == null) { l.Trace( $"Implausable template at actual offset: 0x{actualOffset} tableTemplateOffset 0x{tableTemplateOffset:X} FreeSpaceOffset: 0x{FreeSpaceOffset} chunk absolute offset: 0x{AbsoluteOffset:X}"); continue; } if (Templates.ContainsKey(template.TemplateOffset) == false) { Templates.Add(template.TemplateOffset - 0x18, template); } if (template.NextTemplateOffset <= 0) { continue; } var nextTemplateId = template.NextTemplateOffset; while (nextTemplateId > 0) { var bbb = GetTemplate(nextTemplateId - 10); nextTemplateId = bbb.NextTemplateOffset; if (Templates.ContainsKey(bbb.TemplateOffset) == false) { Templates.Add(bbb.TemplateOffset - 0x18, bbb); } } } l.Trace("Template definitions"); foreach (var esTemplate in Templates.OrderBy(t => t.Key)) { l.Trace($"key: 0x{esTemplate.Key:X4} {esTemplate.Value}"); } l.Trace(""); index = (int)tableOffset + 0x100 + 0x80; //get to start of event Records l.Trace($"\r\nChunk data before processing records: {this}"); const int recordSig = 0x2a2a; long lastRecordNumber = 0; while (index < chunkBytes.Length) { var sig = BitConverter.ToInt32(chunkBytes, index); if (sig != recordSig) { l.Trace( $"Found an invalid signature at 0x{absoluteOffset + index:X}"); break; } var recordOffset = index; //do not read past the last known defined record if (recordOffset - absoluteOffset > LastRecordOffset) { l.Trace( "Reached last record offset. Stopping"); break; } var recordSize = BitConverter.ToUInt32(chunkBytes, index + 4); var recordNumber = BitConverter.ToInt64(chunkBytes, index + 8); try { if (recordNumber < FirstEventRecordIdentifier || recordNumber > LastEventRecordIdentifier) { //outside known good range, so ignore l.Trace( $"Record at offset 0x{AbsoluteOffset + recordOffset:X} falls outside valid record identifier range. Skipping"); break; } var ms = new MemoryStream(chunkBytes, index, (int)recordSize); var br = new BinaryReader(ms, Encoding.UTF8); index += (int)recordSize; var er = new EventRecord(br, recordOffset, this); EventRecords.Add(er); lastRecordNumber = er.RecordNumber; if (er.ExtraDataOffset > 0) { try { //hidden data! recordSize = BitConverter.ToUInt32(ms.ToArray(), (int)er.ExtraDataOffset + 4); recordNumber = BitConverter.ToInt64(ms.ToArray(), (int)er.ExtraDataOffset + 8); if (recordNumber != lastRecordNumber) { ms = new MemoryStream(ms.ToArray(), (int)er.ExtraDataOffset, (int)recordSize); br = new BinaryReader(ms, Encoding.UTF8); er = new EventRecord(br, (int)(recordOffset + er.ExtraDataOffset), this); er.HiddenRecord = true; l.Warn($"Record #: {er.RecordNumber} (timestamp: {er.TimeCreated:yyyy-MM-dd HH:mm:ss.fffffff}): Warning! A hidden record was found! Possible DanderSpritz use detected!"); EventRecords.Add(er); } } catch (Exception e) { //oh well, we tried //l.Warn($"Error when attemping to recover possible hidden record: {e.Message}"); } } //ticksForTimeDelta == totalticks for discrepancy value if (EventLog.LastSeenTicks > 0 && EventLog.LastSeenTicks - ticksForTimeDelta > er.TimeCreated.Ticks) { l.Warn($"Record #: {er.RecordNumber} (timestamp: {er.TimeCreated:yyyy-MM-dd HH:mm:ss.fffffff}): Warning! Time just went backwards! Last seen time before change: {new DateTimeOffset(EventLog.LastSeenTicks,TimeSpan.Zero).ToUniversalTime():yyyy-MM-dd HH:mm:ss.fffffff}"); } EventLog.LastSeenTicks = er.TimeCreated.Ticks; } catch (Exception e) { l.Trace( $"First event record ident-num: {FirstEventRecordIdentifier}-{FirstEventRecordNumber} Last event record ident-num: {LastEventRecordIdentifier}-{LastEventRecordNumber} last record offset 0x{LastRecordOffset:X}"); l.Error( $"Record error at offset 0x{AbsoluteOffset + recordOffset:X}, record #: {recordNumber} error: {e.Message}"); if (ErrorRecords.ContainsKey(recordNumber) == false) { ErrorRecords.Add(recordNumber, e.Message); } } } }