Insert bytes into middle of a file (in windows filesystem) without reading entire file (using File Allocation Table)?

前端 未结 8 1127
盖世英雄少女心
盖世英雄少女心 2020-12-13 19:04

I need a way to insert some file clusters into the middle of a file to insert some data.

Normally, I would just read the entire file and write it back out again with

8条回答
  •  夕颜
    夕颜 (楼主)
    2020-12-13 19:44

    [EDIT:]

    Blah - I'm going to say "this ain't doable, at least not via MFT modification, without a LOT of pain"; first off, the NTFS MFT structures themselves are not 100% "open", so I'm starting to delve into reverse-engineering-territory, which has legal repercussions I'm in no mood to deal with. Also, doing this in .NET is a hyper-tedious process of mapping and marshalling structures based on a lot of guesswork (and don't get me started on the fact that most of the MFT structures are compressed in strange ways). Short story, while I did learn an awful lot about how NTFS "works", I'm no closer to a solution to this problem.

    [/EDIT]

    Ugh...sooo much Marshalling nonsense....

    This struck me as "interesting", therefore I was compelled to poke around at the problem...it's still an "answer-in-progress", but wanted to post up what all I had to assist others in coming up with something. :)

    Also, I have a rough sense that this would be FAR easier on FAT32, but given I've only got NTFS to work with...

    So - lots of pinvoking and marshalling, so let's start there and work backwards:

    As one might guess, the standard .NET File/IO apis aren't going to help you much here - we need device-level access:

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern SafeFileHandle CreateFile(
        string lpFileName,
        [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
        [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
        IntPtr lpSecurityAttributes,
        [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
        [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
        IntPtr hTemplateFile);
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool ReadFile(
        SafeFileHandle hFile,      // handle to file
        byte[] pBuffer,        // data buffer, should be fixed
        int NumberOfBytesToRead,  // number of bytes to read
        IntPtr pNumberOfBytesRead,  // number of bytes read, provide NULL here
        ref NativeOverlapped lpOverlapped // should be fixed, if not null
    );
    
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool SetFilePointerEx(
        SafeFileHandle hFile,
        long liDistanceToMove,
        out long lpNewFilePointer,
        SeekOrigin dwMoveMethod);
    

    We'll use these nasty win32 beasts thusly:

    // To the metal, baby!
    using (var fileHandle = NativeMethods.CreateFile(
        // Magic "give me the device" syntax
        @"\\.\c:",
        // MUST explicitly provide both of these, not ReadWrite
        FileAccess.Read | FileAccess.Write,
        // MUST explicitly provide both of these, not ReadWrite
        FileShare.Write | FileShare.Read,
        IntPtr.Zero,
        FileMode.Open,
        FileAttributes.Normal,
        IntPtr.Zero))
    {
        if (fileHandle.IsInvalid)
        {
            // Doh!
            throw new Win32Exception();
        }
        else
        {
            // Boot sector ~ 512 bytes long
            byte[] buffer = new byte[512];
            NativeOverlapped overlapped = new NativeOverlapped();
            NativeMethods.ReadFile(fileHandle, buffer, buffer.Length, IntPtr.Zero, ref overlapped);
    
            // Pin it so we can transmogrify it into a FAT structure
            var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            try
            {
                // note, I've got an NTFS drive, change yours to suit
                var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(
                     handle.AddrOfPinnedObject(), 
                     typeof(BootSector_NTFS));
    

    Whoa, whoa whoa - what the heck is a BootSector_NTFS? It's a byte-mapped struct that fits as close as I can reckon to what the NTFS structure looks like (FAT32 included as well):

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=0)]
    public struct JumpBoot
    {
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U1, SizeConst=3)]
        public byte[] BS_jmpBoot;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
        public string BS_OEMName;
    }
    
    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 0, Size = 90)]
    public struct BootSector_NTFS
    {
        [FieldOffset(0)]
        public JumpBoot JumpBoot;
        [FieldOffset(0xb)]
        public short BytesPerSector;
        [FieldOffset(0xd)]
        public byte SectorsPerCluster;
        [FieldOffset(0xe)]
        public short ReservedSectorCount;
        [FieldOffset(0x10)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
        public byte[] Reserved0_MUSTBEZEROs;
        [FieldOffset(0x15)]
        public byte BPB_Media;
        [FieldOffset(0x16)]
        public short Reserved1_MUSTBEZERO;
        [FieldOffset(0x18)]
        public short SectorsPerTrack;
        [FieldOffset(0x1A)]
        public short HeadCount;
        [FieldOffset(0x1c)]
        public int HiddenSectorCount;
        [FieldOffset(0x20)]
        public int LargeSectors;
        [FieldOffset(0x24)]
        public int Reserved6;
        [FieldOffset(0x28)]
        public long TotalSectors;
        [FieldOffset(0x30)]
        public long MftClusterNumber;
        [FieldOffset(0x38)]
        public long MftMirrorClusterNumber;
        [FieldOffset(0x40)]
        public byte ClustersPerMftRecord;
        [FieldOffset(0x41)]
        public byte Reserved7;
        [FieldOffset(0x42)]
        public short Reserved8;
        [FieldOffset(0x44)]
        public byte ClustersPerIndexBuffer;
        [FieldOffset(0x45)]
        public byte Reserved9;
        [FieldOffset(0x46)]
        public short ReservedA;
        [FieldOffset(0x48)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] SerialNumber;
        [FieldOffset(0x50)]
        public int Checksum;
        [FieldOffset(0x54)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1AA)]
        public byte[] BootupCode;
        [FieldOffset(0x1FE)]
        public ushort EndOfSectorMarker;
    
        public long GetMftAbsoluteIndex(int recordIndex = 0)
        {
            return (BytesPerSector * SectorsPerCluster * MftClusterNumber) + (GetMftEntrySize() * recordIndex);
        }
        public long GetMftEntrySize()
        {
            return (BytesPerSector * SectorsPerCluster * ClustersPerMftRecord);
        }
    }
    
    
    // Note: dont have fat32, so can't verify all these...they *should* work, tho
    // refs:
    //    http://www.pjrc.com/tech/8051/ide/fat32.html
    //    http://msdn.microsoft.com/en-US/windows/hardware/gg463084
    [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto, Pack=0, Size=90)]
    public struct BootSector_FAT32
    {
        [FieldOffset(0)]
        public JumpBoot JumpBoot;    
        [FieldOffset(11)]
        public short BPB_BytsPerSec;
        [FieldOffset(13)]
        public byte BPB_SecPerClus;
        [FieldOffset(14)]
        public short BPB_RsvdSecCnt;
        [FieldOffset(16)]
        public byte BPB_NumFATs;
        [FieldOffset(17)]
        public short BPB_RootEntCnt;
        [FieldOffset(19)]
        public short BPB_TotSec16;
        [FieldOffset(21)]
        public byte BPB_Media;
        [FieldOffset(22)]
        public short BPB_FATSz16;
        [FieldOffset(24)]
        public short BPB_SecPerTrk;
        [FieldOffset(26)]
        public short BPB_NumHeads;
        [FieldOffset(28)]
        public int BPB_HiddSec;
        [FieldOffset(32)]
        public int BPB_TotSec32;
        [FieldOffset(36)]
        public FAT32 FAT;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct FAT32
    {
        public int BPB_FATSz32;
        public short BPB_ExtFlags;
        public short BPB_FSVer;
        public int BPB_RootClus;
        public short BPB_FSInfo;
        public short BPB_BkBootSec;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=12)]
        public byte[] BPB_Reserved;
        public byte BS_DrvNum;
        public byte BS_Reserved1;
        public byte BS_BootSig;
        public int BS_VolID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=11)] 
        public string BS_VolLab;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)] 
        public string BS_FilSysType;
    }
    

    So now we can map a whole mess'o'bytes back to this structure:

    // Pin it so we can transmogrify it into a FAT structure
    var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {            
            // note, I've got an NTFS drive, change yours to suit
            var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(
                  handle.AddrOfPinnedObject(), 
                  typeof(BootSector_NTFS));
            Console.WriteLine(
                "I think that the Master File Table is at absolute position:{0}, sector:{1}", 
                bootSector.GetMftAbsoluteIndex(),
                bootSector.GetMftAbsoluteIndex() / bootSector.BytesPerSector);
    

    Which at this point outputs:

    I think that the Master File Table is at 
    absolute position:3221225472, sector:6291456
    

    Let's confirm that quick using the OEM support tool nfi.exe:

    C:\tools\OEMTools\nfi>nfi c:
    NTFS File Sector Information Utility.
    Copyright (C) Microsoft Corporation 1999. All rights reserved.
    
    
    File 0
    Master File Table ($Mft)
        $STANDARD_INFORMATION (resident)
        $FILE_NAME (resident)
        $DATA (nonresident)
            logical sectors 6291456-6487039 (0x600000-0x62fbff)
            logical sectors 366267960-369153591 (0x15d4ce38-0x1600d637)
        $BITMAP (nonresident)
            logical sectors 6291448-6291455 (0x5ffff8-0x5fffff)
            logical sectors 7273984-7274367 (0x6efe00-0x6eff7f)
    

    Cool, looks like we're on the right track...onward!

                // If you've got LinqPad, uncomment this to look at boot sector
                bootSector.Dump();
    
        Console.WriteLine("Jumping to Master File Table...");
        long lpNewFilePointer;
        if (!NativeMethods.SetFilePointerEx(
                fileHandle, 
                bootSector.GetMftAbsoluteIndex(), 
                out lpNewFilePointer, 
                SeekOrigin.Begin))
        {
            throw new Win32Exception();
        }
        Console.WriteLine("Position now: {0}", lpNewFilePointer);
    
        // Read in one MFT entry
        byte[] mft_buffer = new byte[bootSector.GetMftEntrySize()];
        Console.WriteLine("Reading $MFT entry...calculated size: 0x{0}",
           bootSector.GetMftEntrySize().ToString("X"));
    
        var seekIndex = bootSector.GetMftAbsoluteIndex();
        overlapped.OffsetHigh = (int)(seekIndex >> 32);
        overlapped.OffsetLow = (int)seekIndex;
        NativeMethods.ReadFile(
              fileHandle, 
              mft_buffer, 
              mft_buffer.Length, 
              IntPtr.Zero, 
              ref overlapped);
        // Pin it for transmogrification
        var mft_handle = GCHandle.Alloc(mft_buffer, GCHandleType.Pinned);
        try
        {
            var mftRecords = (MFTSystemRecords)Marshal.PtrToStructure(
                  mft_handle.AddrOfPinnedObject(), 
                  typeof(MFTSystemRecords));
            mftRecords.Dump();
        }
        finally
        {
            // make sure we clean up
            mft_handle.Free();
        }
    }
    finally
    {
        // make sure we clean up
        handle.Free();
    }
    

    Argh, more native structures to discuss - so the MFT is arranged such that the first 16 or so entries are "fixed":

    [StructLayout(LayoutKind.Sequential)]
    public struct MFTSystemRecords
    {
        public MFTRecord Mft;
        public MFTRecord MftMirror;
        public MFTRecord LogFile;
        public MFTRecord Volume;
        public MFTRecord AttributeDefs;
        public MFTRecord RootFile;
        public MFTRecord ClusterBitmap;
        public MFTRecord BootSector;
        public MFTRecord BadClusterFile;
        public MFTRecord SecurityFile;
        public MFTRecord UpcaseTable;
        public MFTRecord ExtensionFile;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public MFTRecord[] MftReserved;
        public MFTRecord MftFileExt;
    }
    

    Where MFTRecord is:

    [StructLayout(LayoutKind.Sequential, Size = 1024)]
    public struct MFTRecord
    {
        const int BASE_RECORD_SIZE = 48;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
        public string Type;
        public short UsaOffset;
        public short UsaCount;
        public long Lsn;  /* $LogFile sequence number for this record. Changed every time the record is modified. */
        public short SequenceNumber; /* # of times this record has been reused */
        public short LinkCount;  /* Number of hard links, i.e. the number of directory entries referencing this record. */
        public short AttributeOffset; /* Byte offset to the first attribute in this mft record from the start of the mft record. */
        public short MftRecordFlags;
        public int BytesInUse;
        public int BytesAllocated;
        public long BaseFileRecord;
        public short NextAttributeNumber;
        public short Reserved;
        public int MftRecordNumber;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 976)]
        public byte[] Data;
        public byte[] SetData
        {
            get
            {
                return this.Data
                   .Skip(AttributeOffset - BASE_RECORD_SIZE)
                   .Take(BytesInUse - BASE_RECORD_SIZE)
                   .ToArray();
            }
        }
        public MftAttribute[] Attributes
        {
            get
            {
                var idx = 0;
                var ret = new List();
                while (idx < SetData.Length)
                {
                    var attr = MftAttribute.FromBytes(SetData.Skip(idx).ToArray());
                    ret.Add(attr);
                    idx += attr.Attribute.Length;
                    // A special "END" attribute denotes the end of the list
                    if (attr.Attribute.AttributeType == MftAttributeType.AT_END) break;
                }
                return ret.ToArray();
            }
        }
    }
    

    And...here's where I peter out for now; mainly because I want to eat dinner and such. I will come back to this, however!

    References (partially for my own memory, partially to assist other investigators)

    • http://ntfs.com/ntfs-mft.htm
    • http://technet.microsoft.com/en-us/library/cc781134%28WS.10%29.aspx
    • http://waynes-world-it.blogspot.com/2008/03/viewing-ntfs-information-with-nfi-and.html
    • http://en.wikipedia.org/wiki/NTFS
    • http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx#win32_device_namespaces
    • http://www.pjrc.com/tech/8051/ide/fat32.html
    • http://msdn.microsoft.com/en-us/library/aa364572(VS.85).aspx

    Full code dump a'following:

    All the native mappings I glazed over above (due to post size limitations, not a full rehash):

    public enum MftRecordFlags : ushort
    {
        MFT_RECORD_IN_USE = 0x0001,
        MFT_RECORD_IS_DIRECTORY = 0x0002,
        MFT_RECORD_IN_EXTEND = 0x0004,
        MFT_RECORD_IS_VIEW_INDEX = 0x0008,
        MFT_REC_SPACE_FILLER = 0xffff
    }
    public enum MftAttributeType : uint
    {
        AT_UNUSED = 0,
        AT_STANDARD_INFORMATION = 0x10,
        AT_ATTRIBUTE_LIST = 0x20,
        AT_FILENAME = 0x30,
        AT_OBJECT_ID = 0x40,
        AT_SECURITY_DESCRIPTOR = 0x50,
        AT_VOLUME_NAME = 0x60,
        AT_VOLUME_INFORMATION = 0x70,
        AT_DATA = 0x80,
        AT_INDEX_ROOT = 0x90,
        AT_INDEX_ALLOCATION = 0xa0,
        AT_BITMAP = 0xb0,
        AT_REPARSE_POINT = 0xc0,
        AT_EA_INFORMATION = 0xd0,
        AT_EA = 0xe0,
        AT_PROPERTY_SET = 0xf0,
        AT_LOGGED_UTILITY_STREAM = 0x100,
        AT_FIRST_USER_DEFINED_ATTRIBUTE = 0x1000,
        AT_END = 0xffffffff
    }
    
    public enum MftAttributeDefFlags : byte
    {
        ATTR_DEF_INDEXABLE = 0x02, /* Attribute can be indexed. */
        ATTR_DEF_MULTIPLE = 0x04, /* Attribute type can be present multiple times in the mft records of an inode. */
        ATTR_DEF_NOT_ZERO = 0x08, /* Attribute value must contain at least one non-zero byte. */
        ATTR_DEF_INDEXED_UNIQUE = 0x10, /* Attribute must be indexed and the attribute value must be unique for the attribute type in all of the mft records of an inode. */
        ATTR_DEF_NAMED_UNIQUE = 0x20, /* Attribute must be named and the name must be unique for the attribute type in all of the mft records of an inode. */
        ATTR_DEF_RESIDENT = 0x40, /* Attribute must be resident. */
        ATTR_DEF_ALWAYS_LOG = 0x80, /* Always log modifications to this attribute, regardless of whether it is resident or
                    non-resident.  Without this, only log modifications if the attribute is resident. */
    }
    
    [StructLayout(LayoutKind.Explicit)]
    public struct MftInternalAttribute
    {
        [FieldOffset(0)]
        public MftAttributeType AttributeType;
        [FieldOffset(4)]
        public int Length;
        [FieldOffset(8)]
        [MarshalAs(UnmanagedType.Bool)]
        public bool NonResident;
        [FieldOffset(9)]
        public byte NameLength;
        [FieldOffset(10)]
        public short NameOffset;
        [FieldOffset(12)]
        public int AttributeFlags;
        [FieldOffset(14)]
        public short Instance;
        [FieldOffset(16)]
        public ResidentAttribute ResidentAttribute;
        [FieldOffset(16)]
        public NonResidentAttribute NonResidentAttribute;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct ResidentAttribute
    {
        public int ValueLength;
        public short ValueOffset;
        public byte ResidentAttributeFlags;
        public byte Reserved;
    
        public override string ToString()
        {
            return string.Format("{0}:{1}:{2}:{3}", ValueLength, ValueOffset, ResidentAttributeFlags, Reserved);
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct NonResidentAttribute
    {
        public long LowestVcn;
        public long HighestVcn;
        public short MappingPairsOffset;
        public byte CompressionUnit;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
        public byte[] Reserved;
        public long AllocatedSize;
        public long DataSize;
        public long InitializedSize;
        public long CompressedSize;
        public override string ToString()
        {
            return string.Format("{0}:{1}:{2}:{3}:{4}:{5}:{6}:{7}", LowestVcn, HighestVcn, MappingPairsOffset, CompressionUnit, AllocatedSize, DataSize, InitializedSize, CompressedSize);
        }
    }
    
    public struct MftAttribute
    {
        public MftInternalAttribute Attribute;
    
        [field: NonSerialized]
        public string Name;
    
        [field: NonSerialized]
        public byte[] Data;
    
        [field: NonSerialized]
        public object Payload;
    
        public static MftAttribute FromBytes(byte[] buffer)
        {
            var hnd = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            try
            {
                var attr = (MftInternalAttribute)Marshal.PtrToStructure(hnd.AddrOfPinnedObject(), typeof(MftInternalAttribute));
                var ret = new MftAttribute() { Attribute = attr };
                ret.Data = buffer.Skip(Marshal.SizeOf(attr)).Take(attr.Length).ToArray();
                if (ret.Attribute.AttributeType == MftAttributeType.AT_STANDARD_INFORMATION)
                {
                    var payloadHnd = GCHandle.Alloc(ret.Data, GCHandleType.Pinned);
                    try
                    {
                        var payload = (MftStandardInformation)Marshal.PtrToStructure(payloadHnd.AddrOfPinnedObject(), typeof(MftStandardInformation));
                        ret.Payload = payload;
                    }
                    finally
                    {
                        payloadHnd.Free();
                    }
                }
                return ret;
            }
            finally
            {
                hnd.Free();
            }
        }
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct MftStandardInformation
    {
        public ulong CreationTime;
        public ulong LastDataChangeTime;
        public ulong LastMftChangeTime;
        public ulong LastAccessTime;
        public int FileAttributes;
        public int MaximumVersions;
        public int VersionNumber;
        public int ClassId;
        public int OwnerId;
        public int SecurityId;
        public long QuotaChanged;
        public long Usn;
    }
    
    // Note: dont have fat32, so can't verify all these...they *should* work, tho
    // refs:
    //    http://www.pjrc.com/tech/8051/ide/fat32.html
    //    http://msdn.microsoft.com/en-US/windows/hardware/gg463084
    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto, Pack = 0, Size = 90)]
    public struct BootSector_FAT32
    {
        [FieldOffset(0)]
        public JumpBoot JumpBoot;
        [FieldOffset(11)]
        public short BPB_BytsPerSec;
        [FieldOffset(13)]
        public byte BPB_SecPerClus;
        [FieldOffset(14)]
        public short BPB_RsvdSecCnt;
        [FieldOffset(16)]
        public byte BPB_NumFATs;
        [FieldOffset(17)]
        public short BPB_RootEntCnt;
        [FieldOffset(19)]
        public short BPB_TotSec16;
        [FieldOffset(21)]
        public byte BPB_Media;
        [FieldOffset(22)]
        public short BPB_FATSz16;
        [FieldOffset(24)]
        public short BPB_SecPerTrk;
        [FieldOffset(26)]
        public short BPB_NumHeads;
        [FieldOffset(28)]
        public int BPB_HiddSec;
        [FieldOffset(32)]
        public int BPB_TotSec32;
        [FieldOffset(36)]
        public FAT32 FAT;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct FAT32
    {
        public int BPB_FATSz32;
        public short BPB_ExtFlags;
        public short BPB_FSVer;
        public int BPB_RootClus;
        public short BPB_FSInfo;
        public short BPB_BkBootSec;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public byte[] BPB_Reserved;
        public byte BS_DrvNum;
        public byte BS_Reserved1;
        public byte BS_BootSig;
        public int BS_VolID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
        public string BS_VolLab;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string BS_FilSysType;
    }
    

    And the test harness:

    class Program
    {        
        static void Main(string[] args)
        {
            // To the metal, baby!
            using (var fileHandle = NativeMethods.CreateFile(
                // Magic "give me the device" syntax
                @"\\.\c:",
                // MUST explicitly provide both of these, not ReadWrite
                FileAccess.Read | FileAccess.Write,
                // MUST explicitly provide both of these, not ReadWrite
                FileShare.Write | FileShare.Read,
                IntPtr.Zero,
                FileMode.Open,
                FileAttributes.Normal,
                IntPtr.Zero))
            {
                if (fileHandle.IsInvalid)
                {
                    // Doh!
                    throw new Win32Exception();
                }
                else
                {
                    // Boot sector ~ 512 bytes long
                    byte[] buffer = new byte[512];
                    NativeOverlapped overlapped = new NativeOverlapped();
                    NativeMethods.ReadFile(fileHandle, buffer, buffer.Length, IntPtr.Zero, ref overlapped);
    
                    // Pin it so we can transmogrify it into a FAT structure
                    var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    try
                    {
                        // note, I've got an NTFS drive, change yours to suit
                        var bootSector = (BootSector_NTFS)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(BootSector_NTFS));
                        Console.WriteLine(
                            "I think that the Master File Table is at absolute position:{0}, sector:{1}",
                            bootSector.GetMftAbsoluteIndex(),
                            bootSector.GetMftAbsoluteIndex() / bootSector.BytesPerSector);
                        Console.WriteLine("MFT record size:{0}", bootSector.ClustersPerMftRecord * bootSector.SectorsPerCluster * bootSector.BytesPerSector);
    
                        // If you've got LinqPad, uncomment this to look at boot sector
                        bootSector.DumpToHtmlString();
    
                        Pause();
    
                        Console.WriteLine("Jumping to Master File Table...");
                        long lpNewFilePointer;
                        if (!NativeMethods.SetFilePointerEx(fileHandle, bootSector.GetMftAbsoluteIndex(), out lpNewFilePointer, SeekOrigin.Begin))
                        {
                            throw new Win32Exception();
                        }
                        Console.WriteLine("Position now: {0}", lpNewFilePointer);
    
                        // Read in one MFT entry
                        byte[] mft_buffer = new byte[bootSector.GetMftEntrySize()];
                        Console.WriteLine("Reading $MFT entry...calculated size: 0x{0}", bootSector.GetMftEntrySize().ToString("X"));
    
                        var seekIndex = bootSector.GetMftAbsoluteIndex();
                        overlapped.OffsetHigh = (int)(seekIndex >> 32);
                        overlapped.OffsetLow = (int)seekIndex;
                        NativeMethods.ReadFile(fileHandle, mft_buffer, mft_buffer.Length, IntPtr.Zero, ref overlapped);
                        // Pin it for transmogrification
                        var mft_handle = GCHandle.Alloc(mft_buffer, GCHandleType.Pinned);
                        try
                        {
                            var mftRecords = (MFTSystemRecords)Marshal.PtrToStructure(mft_handle.AddrOfPinnedObject(), typeof(MFTSystemRecords));
                            mftRecords.DumpToHtmlString();
                        }
                        finally
                        {
                            // make sure we clean up
                            mft_handle.Free();
                        }
                    }
                    finally
                    {
                        // make sure we clean up
                        handle.Free();
                    }
                }
            }
            Pause();
        }
    
        private static void Pause()
        {
            Console.WriteLine("Press enter to continue...");
            Console.ReadLine();
        }
    }
    
    
    public static class Dumper
    {
        public static string DumpToHtmlString(this T objectToSerialize)
        {
            string strHTML = "";
            try
            {
                var writer = LINQPad.Util.CreateXhtmlWriter(true);
                writer.Write(objectToSerialize);
                strHTML = writer.ToString();
            }
            catch (Exception exc)
            {
                Debug.Assert(false, "Investigate why ?" + exc);
            }
    
            var shower = new Thread(
                () =>
                    {
                        var dumpWin = new Window();
                        var browser = new WebBrowser();
                        dumpWin.Content = browser;
                        browser.NavigateToString(strHTML);
                        dumpWin.ShowDialog();                        
                    });
            shower.SetApartmentState(ApartmentState.STA);
            shower.Start();
            return strHTML;
        }
    
        public static string Dump(this object value)
        {
             return JsonConvert.SerializeObject(value, Formatting.Indented);
        }
    }
    

提交回复
热议问题