private void ParseAttributes(byte[] data, uint maxLength, int offset) { Debug.Assert(Signature == "FILE"); int attribOffset = offset; for (int attribId = 0; ; attribId++) { AttributeType attributeType = Attribute.GetType(data, attribOffset); if (attributeType == AttributeType.EndOfAttributes) { break; } uint length = Attribute.GetTotalLength(data, attribOffset); Debug.Assert(attribOffset + length <= offset + maxLength); Attribute attrib = Attribute.ParseSingleAttribute(data, (int)length, attribOffset); attrib.OwningRecord = FileReference; _attributes.Add(attrib); attribOffset += attrib.TotalLength; } }
public void ParseNonResidentAttribute(Attribute attr) { if (attr.NonResidentFlag == ResidentFlag.NonResident && attr.NonResidentHeader.Fragments.Length > 0) { // Get data attr.ParseAttributeNonResidentBody(this); } }
public void ParseNonResidentAttribute(Attribute attr) { if (Provider.MftFileOnly) { // Nothing to do about this throw new InvalidOperationException("Provider indicates an MFT file is used. Cannot parse non-resident attributes."); } if (attr.NonResidentHeader.Fragments.Length > 0) { // Get data attr.ParseAttributeNonResidentBody(this); } }
private void InitializeNTFS() { // Read $BOOT if (Provider.MftFileOnly) { Boot = new BootSector(); Boot.OEMCode = "NTFS"; Boot.SectorsPrCluster = 2; // Small cluster Boot.BytesPrSector = 512; // Smallest possible sector // Get FileRecord size (read first record's size) byte[] mft_data = new byte[512]; Provider.ReadBytes(mft_data, 0, 0, mft_data.Length); Boot.MFTRecordSizeBytes = FileRecord.ParseAllocatedSize(mft_data, 0); mft_data = null; } else { byte[] drive_data = new byte[512]; Provider.ReadBytes(drive_data, 0, 0, 512); Boot = BootSector.ParseData(drive_data, 512, 0); drive_data = null; Debug.Assert(Boot.OEMCode == "NTFS"); } // Get FileRecord size BytesPrFileRecord = Boot.MFTRecordSizeBytes; _sectorsPrRecord = BytesPrFileRecord / BytesPrSector; Debug.WriteLine($"Updated BytesPrFileRecord, now set to {BytesPrFileRecord}"); // Prep cache MftRawCache = new RawDiskCache(0); // Read $MFT file record byte[] record_data = ReadMFTRecordData((uint)SpecialMFTFiles.MFT); FileMFT = ParseMFTRecord(record_data); record_data = null; Debug.Assert(FileMFT != null); Debug.Assert(FileMFT.Attributes.Count(s => s.Type == AttributeType.DATA) == 1); AttributeData fileMftData = FileMFT.Attributes.OfType <AttributeData>().Single(); Debug.Assert(fileMftData.NonResidentFlag == ResidentFlag.NonResident); Debug.Assert(fileMftData.DataFragments.Length >= 1); MftStream = OpenFileRecord(FileMFT); // Prep cache long maxLength = MftStream.Length; long toAllocateForCache = Math.Min(maxLength, _rawDiskCacheSizeRecords * BytesPrFileRecord); MftRawCache = new RawDiskCache((int)toAllocateForCache); // Get number of FileRecords FileRecordCount = (uint)((fileMftData.DataFragments.Sum(s => (float)s.Clusters)) * (BytesPrCluster * 1f / BytesPrFileRecord)); FileRecords = new WeakReference[FileRecordCount]; FileRecords[0] = new WeakReference(FileMFT); // Read $VOLUME file record FileRecord fileVolume = ReadMFTRecord(SpecialMFTFiles.Volume); // Get version Attribute versionAttrib = fileVolume.Attributes.SingleOrDefault(s => s.Type == AttributeType.VOLUME_INFORMATION); if (versionAttrib != null) { AttributeVolumeInformation attrib = (AttributeVolumeInformation)versionAttrib; NTFSVersion = new Version(attrib.MajorVersion, attrib.MinorVersion); } }
private static void PrettyPrintAttribute(NTFSParser parser, Options options, FileRecord record, Attribute attrib, int indentCount) { string indent = ""; for (int i = 0; i < indentCount; i++) { indent += SingleIndent; } AwesomeConsole.Write(indent + attrib.Id + ": "); PrintType(attrib.Type); AwesomeConsole.Write(" "); PrintName(attrib.AttributeName, true, true); if (attrib.NonResidentFlag == ResidentFlag.NonResident) { AwesomeConsole.Write(" (NonResident)", ConsoleColor.Red); } AwesomeConsole.WriteLine(); indent += SingleIndent; switch (attrib.Type) { case AttributeType.STANDARD_INFORMATION: AttributeStandardInformation standardInformation = (AttributeStandardInformation)attrib; AwesomeConsole.WriteLine(indent + "Creation Time: " + standardInformation.TimeCreated + " " + standardInformation.TimeCreated.Kind); AwesomeConsole.WriteLine(indent + "Modified Time: " + standardInformation.TimeModified + " " + standardInformation.TimeModified.Kind); AwesomeConsole.WriteLine(indent + "Accessed Time: " + standardInformation.TimeAccessed + " " + standardInformation.TimeAccessed.Kind); AwesomeConsole.WriteLine(indent + "Mft Modified : " + standardInformation.TimeMftModified + " " + standardInformation.TimeMftModified.Kind); break; case AttributeType.ATTRIBUTE_LIST: AttributeList list = (AttributeList)attrib; foreach (AttributeListItem listItem in list.Items) { AwesomeConsole.Write(indent + listItem.AttributeId + ": "); PrintType(listItem.Type); AwesomeConsole.Write(" "); PrintName(listItem.Name, true, true); AwesomeConsole.Write(" "); if (record.FileReference == listItem.BaseFile) { AwesomeConsole.Write("(this record)", ConsoleColor.DarkGray); } else { PrintReference(listItem.BaseFile); } AwesomeConsole.WriteLine(); } break; case AttributeType.FILE_NAME: AttributeFileName fileName = (AttributeFileName)attrib; using (AwesomeConsole.BeginSequentialWrite()) { AwesomeConsole.Write(indent + "Parent dir: "); AwesomeConsole.WriteLine(fileName.ParentDirectory, ConsoleColor.Cyan); } AwesomeConsole.WriteLine(indent + "Namespace: " + fileName.FilenameNamespace); AwesomeConsole.Write(indent + "Flags: "); PrintEnums(fileName.FileFlags); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "Name: "); PrintName(fileName.FileName, false, true); AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine(indent + "C Time: " + fileName.CTime + " " + fileName.CTime.Kind); AwesomeConsole.WriteLine(indent + "M Time: " + fileName.MTime + " " + fileName.MTime.Kind); AwesomeConsole.WriteLine(indent + "A Time: " + fileName.ATime + " " + fileName.ATime.Kind); AwesomeConsole.WriteLine(indent + "R Time: " + fileName.RTime + " " + fileName.RTime.Kind); break; case AttributeType.DATA: AttributeData data = (AttributeData)attrib; if (data.NonResidentFlag == ResidentFlag.Resident) { AwesomeConsole.WriteLine(indent + "Data length: {0:N0} Bytes", data.ResidentHeader.ContentLength); } else { AwesomeConsole.WriteLine(indent + "Data length: {0:N0} Bytes", data.NonResidentHeader.ContentSize); AwesomeConsole.Write(indent + "VCN: "); PrintRange(parser, options, data.NonResidentHeader.StartingVCN, data.NonResidentHeader.EndingVCN - data.NonResidentHeader.StartingVCN); AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine(indent + "Fragments: {0:N0}", data.NonResidentHeader.Fragments.Length); AwesomeConsole.WriteLine(indent + SingleIndent + "LCN-range, cluster count, VCN-range", ConsoleColor.DarkGray); foreach (DataFragment fragment in data.NonResidentHeader.Fragments) { AwesomeConsole.Write(indent + SingleIndent); PrintRange(parser, options, fragment.LCN, fragment.Clusters); AwesomeConsole.Write(SingleIndent); PrintSize(parser, options, fragment.Clusters); AwesomeConsole.Write(SingleIndent); PrintRange(parser, options, fragment.StartingVCN, fragment.Clusters); if (fragment.IsCompressed) { AwesomeConsole.Write(" (Compressed)"); } if (fragment.IsSparseFragment) { AwesomeConsole.Write(" (Sparse)"); } AwesomeConsole.WriteLine(); } } break; case AttributeType.OBJECT_ID: AttributeObjectId objectId = (AttributeObjectId)attrib; AwesomeConsole.Write(indent + "ObjectId : "); PrintGUID(objectId.ObjectId); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "BithVolumeId: "); PrintGUID(objectId.BithVolumeId); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "BithObjectId: "); PrintGUID(objectId.BithObjectId); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "DomainId : "); PrintGUID(objectId.DomainId); AwesomeConsole.WriteLine(); break; case AttributeType.SECURITY_DESCRIPTOR: AttributeSecurityDescriptor securityDescriptor = (AttributeSecurityDescriptor)attrib; AwesomeConsole.Write(indent + "SID: "); PrintSID(securityDescriptor.UserSID); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "GID: "); PrintSID(securityDescriptor.GroupSID); AwesomeConsole.WriteLine(); AwesomeConsole.Write(indent + "Flags: "); PrintEnums(securityDescriptor.ControlFlags); AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine(indent + "SACL: " + (securityDescriptor.SACL == null ? 0 : securityDescriptor.SACL.ACECount)); if (securityDescriptor.SACL == null) { AwesomeConsole.WriteLine(indent + SingleIndent + "Not present", ConsoleColor.Red); } else { foreach (ACE ace in securityDescriptor.SACL.ACEs) { PrintACE(indent, ace); } } AwesomeConsole.WriteLine(indent + "DACL: " + (securityDescriptor.DACL == null ? 0 : securityDescriptor.DACL.ACECount)); if (securityDescriptor.DACL == null) { AwesomeConsole.WriteLine(indent + SingleIndent + "Not present", ConsoleColor.Red); } else { foreach (ACE ace in securityDescriptor.DACL.ACEs) { PrintACE(indent, ace); } } break; case AttributeType.VOLUME_NAME: AttributeVolumeName volumeName = (AttributeVolumeName)attrib; AwesomeConsole.Write(indent + "Name: "); PrintName(volumeName.VolumeName); AwesomeConsole.WriteLine(); break; case AttributeType.VOLUME_INFORMATION: AttributeVolumeInformation volumeInformation = (AttributeVolumeInformation)attrib; AwesomeConsole.WriteLine(indent + "Reserved: " + volumeInformation.Reserved); AwesomeConsole.WriteLine(indent + "MajorVersion: " + volumeInformation.MajorVersion + "." + volumeInformation.MinorVersion); AwesomeConsole.Write(indent + "VolumeInformationFlag: "); PrintEnums(volumeInformation.VolumeInformationFlag); AwesomeConsole.WriteLine(); break; case AttributeType.INDEX_ROOT: AttributeIndexRoot indexRoot = (AttributeIndexRoot)attrib; AwesomeConsole.WriteLine(indent + "IndexType: " + indexRoot.IndexType); AwesomeConsole.WriteLine(indent + "CollationRule: " + indexRoot.CollationRule); AwesomeConsole.WriteLine(indent + "IndexAllocationSize: " + indexRoot.IndexAllocationSize); AwesomeConsole.WriteLine(indent + "ClustersPrIndexRecord: " + indexRoot.ClustersPrIndexRecord); AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine(indent + "SizeOfIndexTotal: " + indexRoot.SizeOfIndexTotal); AwesomeConsole.WriteLine(indent + "IndexFlags: " + indexRoot.IndexFlags); AwesomeConsole.WriteLine(indent + "Entries: " + indexRoot.Entries.Length); foreach (IndexEntry entry in indexRoot.Entries) { AwesomeConsole.Write(indent + SingleIndent); PrintReference(entry.FileRefence); if (entry.ChildFileName != null) { AwesomeConsole.Write(" "); PrintName(entry.ChildFileName.FileName, true); AwesomeConsole.Write(" "); PrintEnums(entry.ChildFileName.FileFlags); } AwesomeConsole.WriteLine(); } break; case AttributeType.INDEX_ALLOCATION: AttributeIndexAllocation indexAllocation = (AttributeIndexAllocation)attrib; AwesomeConsole.WriteLine(indent + "Chunks: " + indexAllocation.Indexes.Length); for (int i = 0; i < indexAllocation.Indexes.Length; i++) { IndexAllocationChunk chunk = indexAllocation.Indexes[i]; AwesomeConsole.WriteLine(indent + SingleIndent + string.Format("{0:N0}: {1:N0} of {2:N0} Bytes used", i, chunk.SizeOfIndexTotal, chunk.SizeOfIndexAllocated)); } AwesomeConsole.WriteLine(indent + "Entries: " + indexAllocation.Entries.Length); foreach (IndexEntry entry in indexAllocation.Entries) { AwesomeConsole.Write(indent + SingleIndent); PrintReference(entry.FileRefence); if (entry.ChildFileName != null) { AwesomeConsole.Write(" "); PrintName(entry.ChildFileName.FileName, true); AwesomeConsole.Write(" "); PrintEnums(entry.ChildFileName.FileFlags); } AwesomeConsole.WriteLine(); } break; case AttributeType.BITMAP: AttributeBitmap bitmap = (AttributeBitmap)attrib; AwesomeConsole.WriteLine(indent + "Bitfield Size: {0:N0} ({1:N0} bytes)", bitmap.Bitfield.Length, bitmap.Bitfield.Length / 8); // Print out 4 lines of 64 bits const int bitsPrLine = 64; for (int line = 0; line < 4; line++) { if (bitmap.Bitfield.Length <= line * bitsPrLine) { break; } AwesomeConsole.Write(indent + "{0,-6}", (line * bitsPrLine) + ":"); for (int offset = line * bitsPrLine; offset < line * bitsPrLine + bitsPrLine; offset += 8) { if (bitmap.Bitfield.Length <= offset) { break; } for (int j = offset; j < offset + 8; j++) { if (bitmap.Bitfield.Length <= j) { break; } AwesomeConsole.Write(bitmap.Bitfield[j] ? "1" : "0"); } AwesomeConsole.Write(" "); } AwesomeConsole.WriteLine(); } if (bitmap.Bitfield.Length > 256) { PrintError(indent + "Bitfield was longer than 256 bits, so the rest wasn't printed."); AwesomeConsole.WriteLine(); } break; case AttributeType.LOGGED_UTILITY_STREAM: AttributeLoggedUtilityStream loggedUtilityStream = (AttributeLoggedUtilityStream)attrib; AwesomeConsole.WriteLine(indent + "Data: {0:N0} Bytes", loggedUtilityStream.Data.Length); break; default: if (Debugger.IsAttached) { Debugger.Break(); } PrintError(attrib.Type + " not supported"); break; } }
private static void PerformCopy(NTFSWrapper wrapper, Options opts, uint mftId) { // Fetch record FileRecord record = wrapper.ReadMFTRecord(mftId); // Find attribute(s) List <Attribute> attribs = record.Attributes.Concat(record.ExternalAttributes).Where(s => s.Type == opts.SourceAttribute && s.AttributeName == opts.SourceName).ToList(); if (!attribs.Any()) { PrintError("Unable to find any attribute named \"" + opts.SourceName + "\" of type " + opts.SourceAttribute); AwesomeConsole.WriteLine(); return; } // Determine resident or non-resident data Stream fileStream = null; if (attribs.All(x => x.NonResidentFlag == ResidentFlag.NonResident)) { // Fetch fragments DataFragment[] fragments = attribs.SelectMany(s => s.NonResidentHeader.Fragments).OrderBy(s => s.StartingVCN).ToArray(); ushort compressionUnitSize = attribs[0].NonResidentHeader.CompressionUnitSize; ushort compressionClusterCount = (ushort)(compressionUnitSize == 0 ? 0 : Math.Pow(2, compressionUnitSize)); fileStream = new NtfsDiskStream(wrapper.GetDiskStream(), true, fragments, wrapper.BytesPrCluster, compressionClusterCount, (long)attribs.First().NonResidentHeader.ContentSize); } else { // Fetch data if (attribs.Count != 1) { PrintError("There were multiple attributes for this single file that matched, yet they were all Resident. This is an error."); AwesomeConsole.WriteLine(); return; } Attribute attrib = attribs.First(); if (attrib is AttributeGeneric) { AttributeGeneric generic = (AttributeGeneric)attrib; fileStream = new MemoryStream(generic.Data); } else if (attrib is AttributeData) { AttributeData data = (AttributeData)attrib; fileStream = new MemoryStream(data.DataBytes); } else { PrintError("Only certain resident attributes are supported, like $DATA"); AwesomeConsole.WriteLine(); return; } } // Perform copy using (AwesomeConsole.BeginSequentialWrite()) { AwesomeConsole.Write("Found data, copying "); AwesomeConsole.Write(fileStream.Length.ToString("N0"), ConsoleColor.Green); AwesomeConsole.Write(" bytes to "); AwesomeConsole.WriteLine(opts.Destination, ConsoleColor.Green); } using (FileStream fs = File.OpenWrite(opts.Destination)) { if (fs.CanSeek && fs.CanWrite) { // Pre-expand the destination, to help filesystems allocate files fs.SetLength(fileStream.Length); } byte[] buff = new byte[65535]; int lastProgressed = -1; for (long offset = 0; offset < fileStream.Length; offset += buff.Length) { int read = fileStream.Read(buff, 0, buff.Length); if (read == 0) { // Finished break; } fs.Write(buff, 0, read); int progressed = (int)((offset * 1f / fileStream.Length) * 20); if (read != buff.Length) { // Finished progressed = 20; } if (lastProgressed != progressed) { AwesomeConsole.Write("["); for (int i = 0; i < 20; i++) { if (i < progressed) { AwesomeConsole.Write("="); } else if (i == progressed) { AwesomeConsole.Write(">"); } else { AwesomeConsole.Write(" "); } } AwesomeConsole.Write("]"); Console.CursorLeft = 0; lastProgressed = progressed; } } AwesomeConsole.WriteLine(); AwesomeConsole.WriteLine("Done.", ConsoleColor.Green); } }