Skip to content

Instantly share code, notes, and snippets.

@little-apps
Created April 2, 2017 06:59
Show Gist options
  • Save little-apps/9fca69c0f758409376af45971b811411 to your computer and use it in GitHub Desktop.
Save little-apps/9fca69c0f758409376af45971b811411 to your computer and use it in GitHub Desktop.
Reads the boot sector from a NTFS volume
/**
* 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