public O(Stream fileStream) { var logger = LogManager.GetLogger("O"); var pageSize = 0x1000; var rawBytes2 = new byte[fileStream.Length]; fileStream.Read(rawBytes2, 0, (int)fileStream.Length); var sig = 0x58444E49; var index2 = 0x0; while (index2 < rawBytes2.Length) { var index = 0x0; var rawBytes = new byte[pageSize]; Buffer.BlockCopy(rawBytes2, index2, rawBytes, 0, pageSize); var headerBytes = new byte[4]; fileStream.Seek(0, SeekOrigin.Begin); fileStream.Read(headerBytes, 0, 4); var sigActual = BitConverter.ToInt32(headerBytes, 0); if (sig != sigActual) { { throw new Exception("Invalid header! Expected 'INDX' Signature."); } } Entries = new List <OEntry>(); index += 4; var fixupOffset = BitConverter.ToInt16(rawBytes, index); index += 2; var numFixupPairs = BitConverter.ToInt16(rawBytes, index); index += 2; var logFileSequenceNumber = BitConverter.ToInt64(rawBytes, index); index += 8; var virtualClusterNumber = BitConverter.ToInt64(rawBytes, index); index += 8; var dataStartPosition = index; var indexValOffset = BitConverter.ToInt32(rawBytes, index); index += 4; var indexNodeSize = BitConverter.ToInt32(rawBytes, index); index += 4; var indexAllocatedSize = BitConverter.ToInt32(rawBytes, index); index += 4; var indexFlags = BitConverter.ToInt32(rawBytes, index); index += 4; var fixupTotalLength = numFixupPairs * 2; var fixupBuffer = new byte[fixupTotalLength]; Buffer.BlockCopy(rawBytes, fixupOffset, fixupBuffer, 0, fixupTotalLength); var fixupData = new FixupData(fixupBuffer); var fixupOk = true; //fixup verification var counter = 512; foreach (var bytese in fixupData.FixupActual) { //adjust the offset to where we need to check var fixupOffset1 = counter - 2; var expected = BitConverter.ToInt16(rawBytes, fixupOffset1); if (expected != fixupData.FixupExpected) { fixupOk = false; logger.Warn( $"Fixup values do not match at 0x{fixupOffset1:X}. Expected: 0x{fixupData.FixupExpected:X2}, actual: 0x{expected:X2}"); } //replace fixup expected with actual bytes. bytese has actual replacement values in it. Buffer.BlockCopy(bytese, 0, rawBytes, fixupOffset1, 2); counter += 512; } index += fixupTotalLength; while (index % 8 != 0) { index += 1; } logger.Trace($"Overall offset: 0x{index2:X} Starting new INDEX ENTRY AREA at subindex {index:X}"); while (index < rawBytes.Length) { //var offsetToData = BitConverter.ToUInt16(rawBytes, index); //var sizeOfData = BitConverter.ToUInt16(rawBytes, index+2); var sizeOfIndexEntry = BitConverter.ToUInt16(rawBytes, index + 8); //var sizeOfIndexKey = BitConverter.ToUInt16(rawBytes, index+10); var flags = BitConverter.ToUInt16(rawBytes, index + 12); if (sizeOfIndexEntry == 0x10) { sizeOfIndexEntry = 0x58; } if (flags == 3 || sizeOfIndexEntry == 0 || index + sizeOfIndexEntry > rawBytes.Length) { break; } var buff = new byte[sizeOfIndexEntry]; Buffer.BlockCopy(rawBytes, index, buff, 0, 0x58); var oe = new OEntry(buff, index2 + index); index += sizeOfIndexEntry; if (oe.MftReference.MftEntryNumber == 0 && oe.MftReference.MftSequenceNumber == 0) { continue; } Entries.Add(oe); } index2 += pageSize; } }
public I30(Stream fileStream) { var logger = LogManager.GetLogger("I30"); var pageSize = 0x1000; // var rawBytes2 = new byte[fileStream.Length]; // fileStream.Read(rawBytes2, 0, (int) fileStream.Length); var sig = 0x58444E49; Entries = new List <IndexEntry>(); var pages = new List <byte[]>(); using (var br = new BinaryReader(fileStream)) { while (br.BaseStream.Position < br.BaseStream.Length) { pages.Add(br.ReadBytes(pageSize)); } } var pageNumber = 0; foreach (var page in pages) { //INDX pages are 4096 bytes each, so process them accordingly logger.Debug($"Procesing page 0x{pageNumber:X}"); using (var br = new BinaryReader(new MemoryStream(page))) { var sigActual = br.ReadInt32(); if (sig != sigActual) { throw new Exception("Invalid header! Expected 'INDX' Signature."); } var fixupOffset = br.ReadInt16(); var numFixupPairs = br.ReadInt16(); var logFileSequenceNumber = br.ReadInt64(); var virtualClusterNumber = br.ReadInt64(); var dataStartOffset = br.ReadInt32(); var dataSize = br.ReadInt32(); var dataSizeAllocated = br.ReadInt32(); var isLeafNode = br.ReadInt32() == 0; //this gets us by padding too var fixupTotalLength = numFixupPairs * 2; var fixupBuffer = new byte[fixupTotalLength]; fixupBuffer = br.ReadBytes(fixupTotalLength); while (br.BaseStream.Position % 8 != 0) { br.ReadByte(); //gets us past padding } //since we need to change bytes for the index entries based on fixup, get an array of those bytes var rawBytes = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); var fixupData = new FixupData(fixupBuffer); var fixupOk = true; //fixup verification var counter = 512 - dataStartOffset - 0x18; //datastartOffset is relative, so we need to account for where it begins, at 0x18 foreach (var bytese in fixupData.FixupActual) { //adjust the offset to where we need to check var fixupOffset1 = counter - 2; var expected = BitConverter.ToInt16(rawBytes, fixupOffset1); if (expected != fixupData.FixupExpected) { fixupOk = false; logger.Warn( $"Fixup values do not match at 0x{fixupOffset1:X}. Expected: 0x{fixupData.FixupExpected:X2}, actual: 0x{expected:X2}"); } //replace fixup expected with actual bytes. bytese has actual replacement values in it. Buffer.BlockCopy(bytese, 0, rawBytes, fixupOffset1, 2); counter += 512; } //rawbytes contains the data from the current page we need to parse to get to indexes //datasize includes startoffset plus fixup, etc, so subtract data offset from size for the active index allocations //valid data is allocated - dataoffset //after that is slack var activeSpace = new byte[dataSize - dataStartOffset]; Buffer.BlockCopy(rawBytes, 0, activeSpace, 0, activeSpace.Length); var slackSpace = new byte[rawBytes.Length - activeSpace.Length]; Buffer.BlockCopy(rawBytes, dataSize - dataStartOffset, slackSpace, 0, slackSpace.Length); //absolute offset is page # * 0x1000 + 0x18 + datastartoffset //for slack, add activespace.len using (var binaryReader = new BinaryReader(new MemoryStream(activeSpace))) { while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length) { var absoluteOffset = pageNumber * 0x1000 + 0x18 + dataStartOffset + binaryReader.BaseStream.Position; logger.Trace($"IN ACTIVE LOOP: Absolute offset: 0x{absoluteOffset:X} brActive.BaseStream.Position: 0x{binaryReader.BaseStream.Position:X}"); binaryReader.ReadInt64(); //mft info var indexSize = binaryReader.ReadInt16(); binaryReader.BaseStream.Seek(-10, SeekOrigin.Current); //go back to start of the index data var indxBuffer = binaryReader.ReadBytes(indexSize); var ie = new IndexEntry(indxBuffer, absoluteOffset, pageNumber, false); if (ie.MftReferenceSelf.MftEntryNumber != 0) { //its ok logger.Info(ie); Entries.Add(ie); } } } using (var binaryReader = new BinaryReader(new MemoryStream(slackSpace))) { var startOffset = 0; while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length) { var absoluteOffset = pageNumber * 0x1000 + 0x18 + dataStartOffset + binaryReader.BaseStream.Position + activeSpace.Length; logger.Info($"IN SLACK LOOP: Absolute offset: 0x{absoluteOffset:X} brActive.BaseStream.Position: 0x{binaryReader.BaseStream.Position:X}");
public FileRecord(byte[] rawBytes, int offset) { Offset = offset; var sig = BitConverter.ToInt32(rawBytes, 0); switch (sig) { case FileSig: break; case BaadSig: _logger.Debug($"Bad signature at offset 0x{offset:X}"); IsBad = true; return; default: //not initialized _logger.Debug($"Uninitialized entry (no signature) at offset 0x{offset:X}"); IsUninitialized = true; return; } _logger.Debug($"Processing FILE record at offset 0x{offset:X}"); Attributes = new List <Attribute>(); FixupOffset = BitConverter.ToInt16(rawBytes, 0x4); FixupEntryCount = BitConverter.ToInt16(rawBytes, 0x6); //to build fixup info, take FixupEntryCount x 2 bytes as each are 2 bytes long var fixupTotalLength = FixupEntryCount * 2; var fixupBuffer = new byte[fixupTotalLength]; Buffer.BlockCopy(rawBytes, FixupOffset, fixupBuffer, 0, fixupTotalLength); //pull this early so we can check if its free in our fix up value messages EntryFlags = (EntryFlag)BitConverter.ToInt16(rawBytes, 0x16); FixupData = new FixupData(fixupBuffer); FixupOk = true; //fixup verification var counter = 512; foreach (var bytese in FixupData.FixupActual) { //adjust the offset to where we need to check var fixupOffset = counter - 2; var expected = BitConverter.ToInt16(rawBytes, fixupOffset); if (expected != FixupData.FixupExpected && EntryFlags != 0x0) { FixupOk = false; _logger.Warn( $"Offset: 0x{Offset:X} Entry/seq: 0x{EntryNumber:X}/0x{SequenceNumber:X} Fixup values do not match at 0x{fixupOffset:X}. Expected: 0x{FixupData.FixupExpected:X2}, actual: 0x{expected:X2}"); } //replace fixup expected with actual bytes. bytese has actual replacement values in it. Buffer.BlockCopy(bytese, 0, rawBytes, fixupOffset, 2); counter += 512; } LogSequenceNumber = BitConverter.ToInt64(rawBytes, 0x8); SequenceNumber = BitConverter.ToUInt16(rawBytes, 0x10); ReferenceCount = BitConverter.ToInt16(rawBytes, 0x12); FirstAttributeOffset = BitConverter.ToInt16(rawBytes, 0x14); ActualRecordSize = BitConverter.ToInt32(rawBytes, 0x18); AllocatedRecordSize = BitConverter.ToInt32(rawBytes, 0x1c); var entryBytes = new byte[8]; Buffer.BlockCopy(rawBytes, 0x20, entryBytes, 0, 8); MftRecordToBaseRecord = new MftEntryInfo(entryBytes); FirstAvailablAttribueId = BitConverter.ToInt16(rawBytes, 0x28); EntryNumber = BitConverter.ToUInt32(rawBytes, 0x2c); //start attribute processing at FirstAttributeOffset var index = (int)FirstAttributeOffset; while (index < ActualRecordSize) { var attrType = (AttributeType)BitConverter.ToInt32(rawBytes, index); var attrSize = BitConverter.ToInt32(rawBytes, index + 4); if (attrSize == 0 || attrType == AttributeType.EndOfAttributes) { index += 8; //skip -1 type and 0 size if (index != ActualRecordSize) { _logger.Warn($"Slack space found in entry/seq: 0x{EntryNumber:X}/0x{SequenceNumber:X}"); } //TODO process slack here? break; } _logger.Debug( $"Found Attribute Type {attrType.ToString()} at absolute offset: 0x{index + offset:X}"); _logger.Trace( $"ActualRecordSize: 0x{ActualRecordSize:X}, size: 0x{attrSize:X}, index: 0x{index:X}"); var rawAttr = new byte[attrSize]; Buffer.BlockCopy(rawBytes, index, rawAttr, 0, attrSize); switch (attrType) { case AttributeType.StandardInformation: var si = new StandardInfo(rawAttr); Attributes.Add(si); break; case AttributeType.FileName: var fi = new FileName(rawAttr); Attributes.Add(fi); break; case AttributeType.Data: var d = new Data(rawAttr); Attributes.Add(d); break; case AttributeType.IndexAllocation: var ia = new IndexAllocation(rawAttr); Attributes.Add(ia); break; case AttributeType.IndexRoot: var ir = new IndexRoot(rawAttr); Attributes.Add(ir); break; case AttributeType.Bitmap: var bm = new Bitmap(rawAttr); Attributes.Add(bm); break; case AttributeType.VolumeVersionObjectId: var oi = new ObjectId_(rawAttr); Attributes.Add(oi); break; case AttributeType.SecurityDescriptor: var sd = new SecurityDescriptor(rawAttr); Attributes.Add(sd); break; case AttributeType.VolumeName: var vn = new VolumeName(rawAttr); Attributes.Add(vn); break; case AttributeType.VolumeInformation: var vi = new VolumeInformation(rawAttr); Attributes.Add(vi); break; case AttributeType.LoggedUtilityStream: var lus = new LoggedUtilityStream(rawAttr); Attributes.Add(lus); break; case AttributeType.ReparsePoint: try { var rp = new ReparsePoint(rawAttr); Attributes.Add(rp); } catch (Exception) { var l = LogManager.GetLogger("ReparsePoint"); l.Error( $"There was an error parsing a ReparsePoint in FILE record at offset 0x{Offset:X}. Please extract via --dd and --do and send to [email protected]"); } break; case AttributeType.AttributeList: var al = new AttributeList(rawAttr); Attributes.Add(al); break; case AttributeType.Ea: var ea = new ExtendedAttribute(rawAttr); Attributes.Add(ea); break; case AttributeType.EaInformation: var eai = new ExtendedAttributeInformation(rawAttr); Attributes.Add(eai); break; default: throw new Exception($"Add me: {attrType} (0x{attrType:X})"); } index += attrSize; } //rest is slack. handle here? _logger.Trace($"Slack starts at 0x{index:X} Absolute offset: 0x{index + offset:X}"); }