Skip to content

Commit

Permalink
imp - Windows' hard disk prober detects ALL partitions
Browse files Browse the repository at this point in the history
---

The hard disk prober for Windows now not only detects the logical partitions (those with a drive letter), but also all the MBR and the GPT partitions (those with and without a drive letter, such as your EFI ESP partition). This will bring the Windows hard disk prober to a 1:1 relationship with the Linux and the macOS hard disk prober.

Part of the code is taken from https://github.com/t00/TestCrypt/blob/master/TestCrypt/PhysicalDrive.cs

---

Type: imp
Breaking: False
Doc Required: False
Part: 1/1
  • Loading branch information
AptiviCEO committed Nov 29, 2023
1 parent 430b2e5 commit 877c3fe
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 58 deletions.
136 changes: 87 additions & 49 deletions SpecProbe/Hardware/Probers/HardDiskProber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,63 +262,101 @@ public BaseHardwarePartInfo[] GetBaseHardwarePartsWindows()

try
{
// First, get the list of logical partitions so that we can get the letters agnostically
var drives = DriveInfo.GetDrives();
List<(string letter, long partSize)> parts = [];
foreach (var drive in drives)
{
if (drive.IsReady)
{
// Drive is ready. Now, check the type, as this prober only checks for HDD
if (drive.DriveType == DriveType.Fixed)
parts.Add(("\\\\.\\" + drive.Name[..(drive.Name.Length - 1)], drive.TotalSize));
}
}
// First, get the list of physical partitions up to \\.\PHYSICALDRIVE15
int maxDrives = 15;
List<string> drives = [];
for (int drvIdx = 0; drvIdx <= maxDrives; drvIdx++)
drives.Add(@$"\\.\PHYSICALDRIVE{drvIdx}");

// Then, open the file handle to these drives for us to be able to get the hard drive geometry
List<IntPtr> driveHandles = [];
foreach (var (letter, partSize) in parts)
// Then, open the file handle to the physical drives for us to be able to filter them for partitions
List<IntPtr> finalDrives = [];
foreach (string drive in drives)
{
IntPtr driveHandle = PlatformWindowsInterop.CreateFile(letter, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.System, IntPtr.Zero);
IntPtr driveHandle = PlatformWindowsInterop.CreateFile(drive, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.System, IntPtr.Zero);
if (driveHandle != new IntPtr(-1))
driveHandles.Add(driveHandle);
}

// Enumerate through the handles to try to get their drive geometry
for (int i = 0; i < driveHandles.Count; i++)
{
// Try to get drive storage info
IntPtr drive = driveHandles[i];
bool result = PlatformWindowsInterop.DeviceIoControl(drive, PlatformWindowsInterop.EIOControlCode.DiskGetDriveGeometry, null, 0, out PlatformWindowsInterop.DISK_GEOMETRY drvGeom, Marshal.SizeOf<PlatformWindowsInterop.DISK_GEOMETRY>(), out _, IntPtr.Zero);
if (!result)
continue;
{
maxDrives++;
finalDrives.Add(driveHandle);

// Get the hard disk size and number
bool partResult = PlatformWindowsInterop.DeviceIoControl(drive, PlatformWindowsInterop.EIOControlCode.StorageGetDeviceNumber, null, 0, out PlatformWindowsInterop.STORAGE_DEVICE_NUMBER devNumber, Marshal.SizeOf<PlatformWindowsInterop.STORAGE_DEVICE_NUMBER>(), out _, IntPtr.Zero);
if (!partResult)
continue;
ulong hardDiskSize = (ulong)(drvGeom.Cylinders * drvGeom.TracksPerCylinder * drvGeom.SectorsPerTrack * drvGeom.BytesPerSector);
int diskNum = devNumber.DeviceNumber;
// Get the hard drive number
bool result = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.DiskGetDriveGeometry, null, 0, out PlatformWindowsInterop.DISK_GEOMETRY drvGeom, Marshal.SizeOf<PlatformWindowsInterop.DISK_GEOMETRY>(), out _, IntPtr.Zero);
if (!result)
continue;
bool numResult = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.StorageGetDeviceNumber, null, 0, out PlatformWindowsInterop.STORAGE_DEVICE_NUMBER devNumber, Marshal.SizeOf<PlatformWindowsInterop.STORAGE_DEVICE_NUMBER>(), out _, IntPtr.Zero);
if (!numResult)
continue;
ulong hardDiskSize = (ulong)(drvGeom.Cylinders * drvGeom.TracksPerCylinder * drvGeom.SectorsPerTrack * drvGeom.BytesPerSector);
int diskNum = devNumber.DeviceNumber;

// Install the initial hard disk part
if (diskParts.Count == 0 || !diskParts.Any((part) => part.HardDiskNumber == diskNum))
{
partitions.Clear();
diskParts.Add(new HardDiskPart
// Create a hard drive instance
var hdd = new HardDiskPart()
{
HardDiskSize = hardDiskSize,
});
}
HardDiskNumber = diskNum,
};

// Now, deal with making partition info classes
int partNum = devNumber.PartitionNumber;
partitions.Add(new HardDiskPart.PartitionPart
{
PartitionNumber = partNum,
PartitionSize = parts[i].partSize,
});
diskParts[^1].HardDiskNumber = diskNum;
diskParts[^1].Partitions = [.. partitions];
// Check the type
if (drvGeom.MediaType == PlatformWindowsInterop.MEDIA_TYPE.FixedMedia)
{
// Get the partitions
var parts = new List<HardDiskPart.PartitionPart>();
IntPtr driveLayoutPtr = IntPtr.Zero;
int buffSize = 1024;
int error;
do
{
// Allocate the drive layout pointer
error = 0;
driveLayoutPtr = Marshal.AllocHGlobal(buffSize);

// Try to get the drive layout
bool partResult = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.DiskGetDriveLayoutEx, IntPtr.Zero, 0, driveLayoutPtr, (uint)buffSize, out _, IntPtr.Zero);
if (partResult)
{
// Get all the partitions
PlatformWindowsInterop.DRIVE_LAYOUT_INFORMATION_EX driveLayout = Marshal.PtrToStructure<PlatformWindowsInterop.DRIVE_LAYOUT_INFORMATION_EX>(driveLayoutPtr);
for (uint partIdx = 0; partIdx < driveLayout.PartitionCount; partIdx++)
{
// Make a pointer to a partition info instance
IntPtr ptr = new(driveLayoutPtr.ToInt64() + Marshal.OffsetOf(typeof(PlatformWindowsInterop.DRIVE_LAYOUT_INFORMATION_EX), "PartitionEntry").ToInt64() + (partIdx * Marshal.SizeOf(typeof(PlatformWindowsInterop.PARTITION_INFORMATION_EX))));
PlatformWindowsInterop.PARTITION_INFORMATION_EX partInfo = Marshal.PtrToStructure<PlatformWindowsInterop.PARTITION_INFORMATION_EX>(ptr);

// Check to see if this partition is a recognizable MBR/GPT partition
if ((partInfo.PartitionStyle != PlatformWindowsInterop.PARTITION_STYLE.PARTITION_STYLE_MBR) || partInfo.Mbr.RecognizedPartition)
{
// Add this partition
parts.Add(new()
{
PartitionNumber = (int)partInfo.PartitionNumber,
PartitionSize = partInfo.PartitionLength,
});
}
}
}
else
{
// Increase the buffer size by multiplying it by two.
error = Marshal.GetLastWin32Error();
buffSize *= 2;
}

// Free the drive layout handle
Marshal.FreeHGlobal(driveLayoutPtr);
driveLayoutPtr = IntPtr.Zero;
} while (error == 0x7A);

// Add a hard disk
diskParts.Add(new()
{
HardDiskSize = hardDiskSize,
HardDiskNumber = diskNum,
Partitions = [.. parts],
});
}

// Close the handle
PlatformWindowsInterop.CloseHandle(driveHandle);
}
}
}
catch (Exception ex)
Expand Down
128 changes: 119 additions & 9 deletions SpecProbe/Platform/PlatformWindowsInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,13 @@ public enum EIOControlCode : uint
VideoSetDisplayBrightness = (EFileDevice.Video << 16) | (0x0127 << 2) | EMethod.Buffered | (0 << 14)
}

public enum PARTITION_STYLE : uint
{
PARTITION_STYLE_MBR = 0,
PARTITION_STYLE_GPT = 1,
PARTITION_STYLE_RAW = 2
}

[StructLayout(LayoutKind.Sequential)]
internal struct DISK_GEOMETRY
{
Expand All @@ -431,13 +438,112 @@ internal struct DISK_GEOMETRY
}

[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_DEVICE_NUMBER
internal struct STORAGE_DEVICE_NUMBER
{
public int DeviceType;
public int DeviceNumber;
public int PartitionNumber;
}

[StructLayout(LayoutKind.Explicit)]
internal struct DRIVE_LAYOUT_INFORMATION_MBR
{
[FieldOffset(0)]
public uint Signature;
}

[StructLayout(LayoutKind.Explicit)]
internal struct DRIVE_LAYOUT_INFORMATION_GPT
{
[FieldOffset(0)]
public Guid DiskId;
[FieldOffset(16)]
public long StartingUsableOffset;
[FieldOffset(24)]
public long UsableLength;
[FieldOffset(32)]
public uint MaxPartitionCount;
}

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
internal struct PARTITION_INFORMATION_GPT
{
[FieldOffset(0)]
public Guid PartitionType;
[FieldOffset(16)]
public Guid PartitionId;
[FieldOffset(32)]
public ulong Attributes;
[FieldOffset(40)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)]
public string Name;
}

[StructLayout(LayoutKind.Explicit)]
internal struct PARTITION_INFORMATION_MBR
{
#region Constants
public const byte PARTITION_ENTRY_UNUSED = 0x00;
public const byte PARTITION_FAT_12 = 0x01;
public const byte PARTITION_FAT_16 = 0x04;
public const byte PARTITION_EXTENDED = 0x05;
public const byte PARTITION_IFS = 0x07;
public const byte PARTITION_FAT32 = 0x0B;
public const byte PARTITION_LDM = 0x42;
public const byte PARTITION_NTFT = 0x80;
public const byte PARTITION_VALID_NTFT = 0xC0;
#endregion

[FieldOffset(0)]
[MarshalAs(UnmanagedType.U1)]
public byte PartitionType;
[FieldOffset(1)]
[MarshalAs(UnmanagedType.I1)]
public bool BootIndicator;
[FieldOffset(2)]
[MarshalAs(UnmanagedType.I1)]
public bool RecognizedPartition;
[FieldOffset(4)]
public uint HiddenSectors;
}

[StructLayout(LayoutKind.Explicit)]
internal struct PARTITION_INFORMATION_EX
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(8)]
public long StartingOffset;
[FieldOffset(16)]
public long PartitionLength;
[FieldOffset(24)]
public uint PartitionNumber;
[FieldOffset(28)]
[MarshalAs(UnmanagedType.I1)]
public bool RewritePartition;
[FieldOffset(32)]
public PARTITION_INFORMATION_MBR Mbr;
[FieldOffset(32)]
public PARTITION_INFORMATION_GPT Gpt;
}

/// <summary>
/// Contains extended information about a drive's partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct DRIVE_LAYOUT_INFORMATION_EX
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(4)]
public uint PartitionCount;
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_MBR Mbr;
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
[FieldOffset(48)]
public PARTITION_INFORMATION_EX PartitionEntry;
}

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr CreateFile(
Expand All @@ -450,16 +556,20 @@ internal static extern IntPtr CreateFile(
IntPtr templateFile
);

[DllImport("Kernel32.dll", SetLastError = true)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
uint IoControlCode,
byte[] InBuffer,
int nInBufferSize,
byte[] OutBuffer,
int nOutBufferSize,
out int pBytesReturned,
IntPtr Overlapped
EIOControlCode dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);

[DllImport("Kernel32.dll", SetLastError = true)]
Expand Down

0 comments on commit 877c3fe

Please sign in to comment.