Created
October 10, 2015 03:32
-
-
Save little-apps/3632a156cee5936d2441 to your computer and use it in GitHub Desktop.
This example loads the first 512 bytes of a NTFS partition using C#. You will need administrator privileges to use this program.
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
/* | |
This example loads the first 512 bytes of a NTFS partition using C#. You will need administrator privileges to use this program. | |
Author: Little Apps (https://www.little-apps.com) | |
License: GNU Lesser General Public License v3 | |
*/ | |
using System; | |
using System.ComponentModel; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading; | |
using Microsoft.Win32.SafeHandles; | |
namespace TestConsole | |
{ | |
class Program | |
{ | |
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
internal static extern SafeFileHandle CreateFile( | |
[MarshalAs(UnmanagedType.LPTStr)] string filename, | |
[MarshalAs(UnmanagedType.U4)] FileAccess access, | |
[MarshalAs(UnmanagedType.U4)] FileShare share, | |
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero | |
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, | |
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, | |
IntPtr templateFile); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
internal static extern bool ReadFile(SafeFileHandle hFile, [Out] IntPtr lpBuffer, uint nNumberOfBytesToRead, | |
out uint lpNumberOfBytesRead, [In] ref NativeOverlapped lpOverlapped); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
internal static extern bool GetVolumePathName(string lpszFileName, [Out] StringBuilder lpszVolumePathName, | |
uint cchBufferLength); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
internal static extern bool GetVolumeNameForVolumeMountPoint(string lpszVolumeMountPoint, | |
[Out] StringBuilder lpszVolumeName, uint cchBufferLength); | |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
public struct NtfsBootSector | |
{ | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public byte[] jmpInstruction; | |
[MarshalAs(UnmanagedType.U8, SizeConst = 8)] | |
public ulong oemId; | |
[MarshalAs(UnmanagedType.U2)] | |
public short bytesPerSector; | |
[MarshalAs(UnmanagedType.U1)] | |
public byte sectorsPerCluster; | |
[MarshalAs(UnmanagedType.U2)] | |
public short reservedSectors; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public byte[] alwaysZero1; | |
[MarshalAs(UnmanagedType.U2)] | |
public short notUsed1; | |
[MarshalAs(UnmanagedType.U1)] | |
public byte mediaDescriptor; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] | |
public byte[] alwaysZero2; | |
[MarshalAs(UnmanagedType.U2)] | |
public short sectorsPerTrack; | |
[MarshalAs(UnmanagedType.U2)] | |
public short numberOfHeads; | |
[MarshalAs(UnmanagedType.U4)] | |
public uint hiddenSectors; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] | |
public byte[] alwaysZero3; | |
[MarshalAs(UnmanagedType.U4)] | |
public uint ntfsSignature; | |
[MarshalAs(UnmanagedType.U8)] | |
public ulong totalSectors; | |
[MarshalAs(UnmanagedType.U8)] | |
public ulong lcnMFT; | |
[MarshalAs(UnmanagedType.U8)] | |
public ulong lcnMFTMirr; | |
[MarshalAs(UnmanagedType.U1)] | |
public byte clustersPerMFTRecord; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public byte[] notUsed6; | |
[MarshalAs(UnmanagedType.U1)] | |
public byte clustersPerIndexBuffer; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | |
public byte[] notUsed7; | |
[MarshalAs(UnmanagedType.U8)] | |
public ulong volumeSerialNumber; | |
[MarshalAs(UnmanagedType.U4)] | |
public uint ntfsChecksum; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 426)] | |
public byte[] bootStrapCode; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] | |
public byte[] signature; | |
} | |
static void Main(string[] args) | |
{ | |
const int bufferSize = 512; | |
uint bytesRead; | |
Console.Write("Enter path without trailing slash (ie: C:): "); | |
var path = Console.In.ReadLine().Trim(); | |
if (string.IsNullOrEmpty(path)) | |
{ | |
Console.WriteLine("No path entered. Exiting..."); | |
return; | |
} | |
var volumeName = GetVolumeNameFromPath(path); | |
if (string.IsNullOrEmpty(volumeName)) | |
return; | |
Console.WriteLine($"Opening volume name ({volumeName})..."); | |
var fileHandle = CreateFile(volumeName, FileAccess.Read, FileShare.Read | FileShare.Write, IntPtr.Zero, | |
FileMode.Open, 0, IntPtr.Zero); | |
if (fileHandle.IsInvalid) | |
{ | |
var errorCode = Marshal.GetLastWin32Error(); | |
Console.WriteLine($"Unable to open drive (code {errorCode}). Exiting..."); | |
return; | |
} | |
var overlapped = new NativeOverlapped(); | |
var bufferPtr = Marshal.AllocHGlobal(bufferSize); | |
if (!ReadFile(fileHandle, bufferPtr, bufferSize, out bytesRead, ref overlapped)) | |
{ | |
var errorCode = Marshal.GetLastWin32Error(); | |
Console.WriteLine($"There was an error reading the NTFS boot sector (code: {errorCode}). Exiting..."); | |
return; | |
} | |
if (bytesRead == 0 || bytesRead != bufferSize) | |
{ | |
Console.WriteLine($"Only {bytesRead} bytes out of {bufferSize} bytes were read. Exiting..."); | |
return; | |
} | |
var ntfsBootSector = (NtfsBootSector) Marshal.PtrToStructure(bufferPtr, typeof (NtfsBootSector)); | |
if (ntfsBootSector.oemId != 0x202020205346544E) | |
{ | |
Console.WriteLine("This drive is not NTFS. Exiting..."); | |
return; | |
} | |
Console.WriteLine("Got NTFS boot sector!"); | |
} | |
static string GetVolumeNameFromPath(string path) | |
{ | |
var mountPoint = new StringBuilder(path.Length); | |
var volumePathName = GetVolumePathName(path, mountPoint, (uint)mountPoint.Capacity) ? mountPoint.ToString() : string.Copy(path); | |
/* Make two versions of the MountPoint, one with a trailing backslash and one without. */ | |
var mountPointSlash = $"{volumePathName}\\"; | |
string volumeNameSlash; | |
try | |
{ | |
volumeNameSlash = GetVolumeName(mountPointSlash); | |
} | |
catch (Win32Exception ex) | |
{ | |
if (mountPointSlash.Length > 52 - 1 - 4) | |
{ | |
/* "Cannot find volume name for mountpoint '%s': %s" */ | |
var errorMsg = $"[{ex.NativeErrorCode}] {ex.Message}"; | |
Console.WriteLine("Cannot find volume name for mountpoint '{0}': {1}", mountPoint, errorMsg); | |
return string.Empty; | |
} | |
volumeNameSlash = $"\\\\.\\{mountPointSlash}"; | |
} | |
return volumeNameSlash.TrimEnd('\\'); | |
} | |
internal static string GetVolumeName(string mountPoint) | |
{ | |
const int maxVolumeNameLength = 512; | |
var sb = new StringBuilder(maxVolumeNameLength); | |
if (!GetVolumeNameForVolumeMountPoint(mountPoint, sb, maxVolumeNameLength)) | |
throw new Win32Exception(Marshal.GetLastWin32Error()); | |
return sb.ToString(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment