[EDIT:]
Bla - je vais dire "ce n'est pas faisable, au moins pas via MFT modification, sans BEAUCOUP de douleur"; tout d'abord, le NTFS MFT structures elles-mêmes ne sont pas à 100% "ouvert", donc je commence à plonger dans le reverse-engineering-territoire, ce qui a des conséquences juridiques, je suis pas d'humeur à traiter. Aussi, faire dans .NET est un hyper-processus fastidieux de la cartographie et de l'ordonnancement des structures basées sur beaucoup de conjecture (et ne me lancez pas sur le fait que la plupart de la MFT structures sont compressés dans des voies détournées). Histoire courte, alors que j'ai appris énormément de choses sur la façon NTFS "œuvres", je suis pas près de trouver une solution à ce problème.
[/EDIT]
Pouah...sooo beaucoup Ordonnancement des bêtises....
Ce qui m'a frappé comme une "intéressant", donc j'ai été obligé de fouiner sur le problème...c'est toujours une "réponse en cours", mais je voulais poster ce que j'avais à aider les autres à venir avec quelque chose. :)
Aussi, j'ai une idée que ce serait BEAUCOUP plus facile sur FAT32, mais étant donné que je n'ai que NTFS pour travailler avec...
Soi - beaucoup de pinvoking et de l'ordonnancement, donc nous allons commencer par là et de travailler à l'envers:
Comme on peut le deviner, la norme .Fichier NET/IO api ne va pas beaucoup vous aider ici - nous avons besoin au niveau de l'appareil d'accès:
[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);
Nous allons utiliser ces vilaines win32 bêtes ainsi:
// 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 - ce que le diable est un BootSector_NTFS
? C'est un byte-mappé struct
qui s'adapte aussi près que je peux compter à ce que la structure NTFS ressemble (FAT32 inclus en tant que bien):
[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;
}
Alors maintenant, nous pouvons tracer un gâchis o'bytes pour en revenir à cette 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);
Qui, à ce point de sorties:
I think that the Master File Table is at
absolute position:3221225472, sector:6291456
Nous allons confirmer que rapide à l'aide de l'OEM outil de soutien à la 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, on dirait que nous sommes sur la bonne voie...en avant!
// 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, plus de structures natives de discuter, de sorte que la MFT sont disposées de telle façon que les 16 premiers ou si les entrées sont "fixes":
[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;
}
Où MFTRecord
est:
[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<MftAttribute>();
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();
}
}
}
Et...voici où j'en pierre pour le moment, principalement parce que j'ai envie de manger le dîner, par exemple. Je reviendrai sur cela, cependant!
Références (partiellement pour ma propre mémoire, en partie pour aider les autres enquêteurs)
Code complet de vidage'following:
Tous les natifs mappages je vitrée sur le dessus (en raison de poster des limitations de taille, n'est pas complet resucée):
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;
}
Et le harnais de test:
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<T>(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);
}
}