public FileRecord(byte[] rawBytes, int offset) { Offset = offset; var sig = BitConverter.ToInt32(rawBytes, 0); if ((sig != _fileSig) && (sig != _baadSig) && (sig != 0x0)) { Logger.Fatal($"Invalid signature! 0x{sig:X}"); return; //throw new Exception("Invalid signature!"); } if (sig == _baadSig) { Logger.Warn($"Bad signature at offset 0x{offset:X}"); return; } Attributes = new List <Attribute>(); FixupOffset = BitConverter.ToInt16(rawBytes, 2); FixupEntryCount = BitConverter.ToInt16(rawBytes, 4); LogSequenceNumber = BitConverter.ToInt64(rawBytes, 0x8); SequenceNumber = BitConverter.ToInt16(rawBytes, 0x10); ReferenceCount = BitConverter.ToInt16(rawBytes, 0x12); FirstAttributeOffset = BitConverter.ToInt16(rawBytes, 0x14); EntryFlags = (EntryFlag)BitConverter.ToInt16(rawBytes, 0x16); Logger.Trace($"Entry flags: {EntryFlags}"); 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); FirstAvailableAttribueId = BitConverter.ToInt16(rawBytes, 0x28); EntryNumber = BitConverter.ToInt32(rawBytes, 0x2c); var fixupExpectedBytes = new byte[2]; var fixupActual1 = new byte[2]; var fixupActual2 = new byte[2]; Buffer.BlockCopy(rawBytes, 0x30, fixupExpectedBytes, 0, 2); Buffer.BlockCopy(rawBytes, 0x32, fixupActual1, 0, 2); Buffer.BlockCopy(rawBytes, 0x34, fixupActual2, 0, 2); //verify this record looks ok based on fixup bytes //0x1FE and 0x3fe var expectedFixupVal = BitConverter.ToInt16(fixupExpectedBytes, 0); var x1FeValue = BitConverter.ToInt16(rawBytes, 0x1FE); var x3FeValue = BitConverter.ToInt16(rawBytes, 0x3FE); if ((x1FeValue != expectedFixupVal) && ((EntryFlags & EntryFlag.FileRecordSegmentInUse) == EntryFlag.FileRecordSegmentInUse)) { Logger.Warn( $"FILE record at offset 0x{offset:X}! Fixup values do not match at 0x1FE. Expected: {expectedFixupVal}, actual: {x1FeValue}, EntryFlags: {EntryFlags}"); } if ((x3FeValue != expectedFixupVal) && ((EntryFlags & EntryFlag.FileRecordSegmentInUse) == EntryFlag.FileRecordSegmentInUse)) { Logger.Warn( $"FILE record at offset 0x{offset:X}! Fixup values do not match at 0x3FE. Expected: {expectedFixupVal}, actual: {x3FeValue}, EntryFlags: {EntryFlags}"); } //header is done, replace fixup bytes with actual bytes //0x1fe and 0x3fe should contain fixup bytes Buffer.BlockCopy(fixupActual1, 0, rawBytes, 0x1fe, 2); Buffer.BlockCopy(fixupActual2, 0, rawBytes, 0x3fe, 2); //start attribute processing at FirstAttributeOffset var index = (int)FirstAttributeOffset; while (index < ActualRecordSize) { var attrType = BitConverter.ToInt32(rawBytes, index); var attrSize = BitConverter.ToInt32(rawBytes, index + 4); // Logger.Trace( // $"ActualRecordSize: {ActualRecordSize} attrType: 0x{attrType:X}, size: {attrSize}, index: {index}, offset: 0x{offset:x}, i+o: 0x{index + offset:X}"); if ((attrSize == 0) || (attrType == -1)) { index += 8; //skip -1 type and 0 size if (EntryFlags == 0) //this is a free record { break; } continue; } var rawAttr = new byte[attrSize]; Buffer.BlockCopy(rawBytes, index, rawAttr, 0, attrSize); switch ((AttributeType)attrType) { case AttributeType.StandardInformation: var si = new StandardInfo(rawAttr); Attributes.Add(si); SILastAccessedOn = si.LastAccessedOn; SICreatedOn = si.CreatedOn; SIRecordModifiedOn = si.RecordModifiedOn; SIContentModifiedOn = si.ContentModifiedOn; break; case AttributeType.FileName: var fi = new FileName(rawAttr); Attributes.Add(fi); if ((fi.FileInfo.NameType & NameTypes.Windows) == NameTypes.Windows) { FName = fi.FileInfo.FileName; } //if (fi.FileInfo.LastAccessedOn.UtcDateTime != SILastAccessedOn.UtcDateTime) //{ FNLastAccessedOn = fi.FileInfo.LastAccessedOn; //} //if (fi.FileInfo.CreatedOn.UtcDateTime != SICreatedOn.UtcDateTime) //{ FNCreatedOn = fi.FileInfo.CreatedOn; //} //if (fi.FileInfo.RecordModifiedOn.UtcDateTime != SIRecordModifiedOn.UtcDateTime) //{ FNRecordModifiedOn = fi.FileInfo.RecordModifiedOn; //} //if (fi.FileInfo.ContentModifiedOn.UtcDateTime != SIContentModifiedOn.UtcDateTime) //{ FNContentModifiedOn = fi.FileInfo.ContentModifiedOn; //} break; case AttributeType.Data: var data = new Data(rawAttr); Attributes.Add(data); 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: var rp = new ReparsePoint(rawAttr); Attributes.Add(rp); break; case AttributeType.AttributeList: var al = new AttributeList(rawAttr); Attributes.Add(al); break; case AttributeType.Ea: //TODO Finish this var ea = new ExtendedAttribute(rawAttr); Attributes.Add(ea); break; case AttributeType.EaInformation: var eai = new ExtendedAttributeInformation(rawAttr); Attributes.Add(eai); break; default: Logger.Warn($"Unhandled attribute type! Add me: {(AttributeType) attrType}"); throw new Exception($"Add me: {(AttributeType) attrType}"); break; } index += attrSize; } SlackStartOffset = index; //rest is slack. handle here? Logger.Trace($"Slack starts at {index} i+o: 0x{index + offset:X}"); }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <returns></returns> public static IndexEntry Get(string path) { string[] paths = path.TrimEnd('\\').Split('\\'); // Determine Volume Name string volume = Helper.GetVolumeFromPath(path); // Test volume path Helper.getVolumeName(ref volume); int index = -1; List <IndexEntry> indexEntryList = new List <IndexEntry>(); for (int i = 0; i < paths.Length; i++) { if (index == -1) { index = 5; } else { bool match = false; foreach (IndexEntry entry in indexEntryList) { if (entry.Entry.Filename.ToUpper() == paths[i].ToUpper()) { index = (int)entry.RecordNumber; match = true; } } if (!(match)) { throw new Exception("Path " + path + " not found."); } } FileRecord record = FileRecord.Get(volume, index, false); indexEntryList.Clear(); if (i < paths.Length - 1) { foreach (FileRecordAttribute attr in record.Attribute) { if (attr.Name == FileRecordAttribute.ATTR_TYPE.INDEX_ROOT) { foreach (IndexEntry entry in (attr as IndexRoot).Entries) { if (entry.Entry.Namespace != 0x02) { indexEntryList.Add(entry); } } } else if (attr.Name == FileRecordAttribute.ATTR_TYPE.INDEX_ALLOCATION) { // Get INDEX_ALLOCATION bytes IndexAllocation IA = new IndexAllocation(attr as NonResident, volume); foreach (IndexEntry entry in IA.Entries) { if (entry.Entry.Namespace != 0x02) { indexEntryList.Add(entry); } } } } } else { return(new IndexEntry(record)); } } throw new Exception("The IndexEntry object for the specified path could not be found."); }
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}"); }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <returns></returns> public static IndexEntry[] GetInstances(string path) { string[] paths = path.TrimEnd('\\').Split('\\'); // Determine Volume Name string volume = Helper.GetVolumeFromPath(path); // Test volume path Helper.getVolumeName(ref volume); int index = -1; List <IndexEntry> indexEntryList = new List <IndexEntry>(); for (int i = 0; i < paths.Length; i++) { if (index == -1) { index = 5; } else { bool match = false; foreach (IndexEntry entry in indexEntryList) { if (entry.Entry.Filename.ToUpper() == paths[i].ToUpper()) { index = (int)entry.RecordNumber; match = true; } } if (!(match)) { throw new Exception("Path " + path + " not found."); } } FileRecord record = FileRecord.Get(volume, index, true); indexEntryList.Clear(); if (record.Directory) { foreach (FileRecordAttribute attr in record.Attribute) { if (attr.Name == FileRecordAttribute.ATTR_TYPE.INDEX_ROOT) { try { foreach (IndexEntry entry in (attr as IndexRoot).Entries) { if (entry.Entry.Namespace != 0x02) { StringBuilder sb = new StringBuilder(); sb.Append(path.TrimEnd('\\')); sb.Append("\\"); sb.Append(entry.Filename); entry.FullName = sb.ToString(); indexEntryList.Add(entry); } } } catch { return(null); } } else if (attr.Name == FileRecordAttribute.ATTR_TYPE.INDEX_ALLOCATION) { // Get INDEX_ALLOCATION bytes IndexAllocation IA = new IndexAllocation(attr as NonResident, volume); foreach (IndexEntry entry in IA.Entries) { if (entry.Entry.Namespace != 0x02) { StringBuilder sb = new StringBuilder(); sb.Append(path.TrimEnd('\\')); sb.Append("\\"); sb.Append(entry.Filename); entry.FullName = sb.ToString(); indexEntryList.Add(entry); } } } } } else { IndexEntry[] indexArray = new IndexEntry[1]; indexArray[0] = new IndexEntry(record); return(indexArray); } } return(indexEntryList.ToArray()); }