-
-
Save little-apps/9fca69c0f758409376af45971b811411 to your computer and use it in GitHub Desktop.
Reads the boot sector from a NTFS volume
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Reads the boot sector of a NTFS volume and outputs information. | |
* License: Public Domain | |
* | |
* More information: https://www.little-apps.com/2017/04/marshalling-and-pinvoke/ | |
**/ | |
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
using Microsoft.Win32.SafeHandles; | |
class NTFSBootSector | |
{ | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
private 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", SetLastError = true)] | |
private static extern bool ReadFile( | |
SafeFileHandle hFile, // Used since CreateFile returns SafeFileHandle | |
IntPtr pBuffer, // Can be byte[], but for our purposes, it’s easier to read to a IntPtr | |
uint NumberOfBytesToRead, // Shouldn’t be int because this isn’t going to be negative | |
out uint pNumberOfBytesRead, // Could be IntPtr but easier to just use this | |
[In] ref NativeOverlapped lpOverlapped // Can also be IntPtr | |
); | |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
public struct NtfsBootSector | |
{ | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public readonly byte[] JMPInstruction; | |
public readonly ulong OEMID; | |
public readonly ushort BytesPerSector; | |
public readonly byte SectorsPerCluster; | |
public readonly ushort ReservedSectors; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public readonly byte[] AlwaysZero1; | |
public readonly ushort NotUsed1; | |
public readonly byte MediaDescriptor; | |
public readonly ushort AlwaysZero2; | |
public readonly ushort SectorsPerTrack; | |
public readonly ushort NumberOfHeads; | |
public readonly uint HiddenSectors; | |
public readonly uint NotUsed2; | |
public readonly uint NotUsed3; | |
public readonly ulong TotalSectors; | |
public readonly ulong MFTLCN; | |
public readonly ulong MFTMirrLCN; | |
public readonly byte ClustersPerMFTRecord; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public readonly byte[] NotUsed4; | |
public readonly uint ClustersPerIndexBuffer; | |
public readonly ulong VolumeSerialNumber; | |
public readonly uint NTFSChecksum; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 426)] | |
public readonly byte[] BootStrapCode; | |
public readonly ushort Signature; | |
} | |
static void Main(string[] args) | |
{ | |
// Open handle to volume using CreateFile | |
var handle = CreateFile( | |
"\\\\.\\C:", | |
FileAccess.Read, | |
FileShare.ReadWrite, | |
IntPtr.Zero, | |
FileMode.Open, | |
0, | |
IntPtr.Zero); | |
if (handle.IsClosed || handle.IsInvalid) | |
{ | |
Console.WriteLine("An error occurred trying to open file. Error code: {0}", Marshal.GetLastWin32Error()); | |
return; | |
} | |
// Read data with from volume with ReadFile | |
var bufferPtr = Marshal.AllocHGlobal(512); | |
var nativeOverlapped = new NativeOverlapped { OffsetLow = 0, OffsetHigh = 0 }; | |
if (!ReadFile(handle, bufferPtr, 512, out uint bytesRead, ref nativeOverlapped)) | |
{ | |
Console.WriteLine("Unable to read volume. Error code: {0}", Marshal.GetLastWin32Error()); | |
Marshal.FreeHGlobal(bufferPtr); | |
handle.Close(); | |
return; | |
} | |
// Parse data using Marshal | |
var bootSector = Marshal.PtrToStructure<NtfsBootSector>(bufferPtr); | |
// Is this a NTFS boot sector? | |
Debug.Assert(bootSector.OEMID == 0x202020205346544e); // Represents 'NTFS ' | |
// Output NTFS boot sector | |
Console.WriteLine("Bytes Per Sector:\t\t\t{0}", bootSector.BytesPerSector); | |
Console.WriteLine("Sectors Per Cluster:\t\t\t{0}", bootSector.SectorsPerCluster); | |
Console.WriteLine("Reserved Sectors:\t\t\t{0}", bootSector.ReservedSectors); | |
Console.WriteLine("Media Descriptor:\t\t\t{0}", bootSector.MediaDescriptor); | |
Console.WriteLine("Sectors Per Track:\t\t\t{0}", bootSector.SectorsPerTrack); | |
Console.WriteLine("Number Of Heads:\t\t\t{0}", bootSector.NumberOfHeads); | |
Console.WriteLine("Hidden Sectors:\t\t\t\t{0}", bootSector.HiddenSectors); | |
Console.WriteLine("Total Sectors:\t\t\t\t{0}", bootSector.TotalSectors); | |
Console.WriteLine("Logical Cluster Number of $MFT:\t\t{0}", bootSector.MFTLCN); | |
Console.WriteLine("Logical Cluster Number of $MFTMirr:\t{0}", bootSector.MFTMirrLCN); | |
Console.WriteLine("Clusters Per MFT Record:\t\t{0}", bootSector.ClustersPerMFTRecord); | |
Console.WriteLine("Clusters Per Index Buffer:\t\t{0}", bootSector.ClustersPerIndexBuffer); | |
Console.WriteLine("Signature:\t\t\t\t0x{0:X}", bootSector.Signature); | |
Console.WriteLine(); | |
// Calculate the bytes in a cluster | |
var clusterSize = bootSector.BytesPerSector * bootSector.SectorsPerCluster; | |
Console.WriteLine("Bytes Per Cluster:\t\t\t{0}", clusterSize); | |
// Multiplying the LCN (Logical Cluster Number) by the cluster size will given the offset on the hard drive | |
// Determines the offset of the $MFT | |
var mftOffset = bootSector.MFTLCN * (ulong) clusterSize; | |
Console.WriteLine("Offset of $MFT:\t\t\t\t0x{0:X}", mftOffset); | |
Marshal.FreeHGlobal(bufferPtr); | |
handle.Close(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment