Determining all bootable partitions






2.81/5 (6 votes)
Determining all bootable partitions using PInvoke
Introduction
In this article, I will show how to determine boot partition of Windows OS with C# by using Win API (pinvoke).
Background
Windows OS has different types of partition on the disk. And there must be a system partition generally (C:\), other than this, there may be user partitions such as D: \, E:\. There may also be another type of partition such as Boot partition which is what constitutes the subject of our writing. Sometimes, developers want to identify boot partition in order not to damage the system because this partition is very important for boot operation of Windows OS.
Thanks to Richard, I realized boot indicator property is not valid on GPT disk. So I updated source code and article as compatible with GPT disk.
Using the Code
I used C# (Pinvoke). I have to define classic MS definitions which are structs, constants, etc. So I won't mention these definitions below.
The Native class contains definitions of structs, constants of Win API.
The NativeApi
the class contains functions of Win API like,CreateFile
DeviceIoControl
etc.
Firstly; We need to obtain handles of all hard drives which are mounted on the system. So we enumerate these drives. And then we can get partition list for all physical drives. Then we can enumerate all partitions.
This is full of body code:
public const uint MAX_NUMBER_OF_DRIVES = 64;
for (uint i = 0; i < MAX_NUMBER_OF_DRIVES; i++)
{
// try to open the current physical drive
string volume = string.Format("\\\\.\\PhysicalDrive{0}", i);
SafeFileHandle hndl = CreateFile(volume, Native.GENERIC_READ | Native.GENERIC_WRITE,
Native.FILE_SHARE_READ | Native.FILE_SHARE_WRITE,
IntPtr.Zero,
Native.OPEN_EXISTING,
Native.FILE_ATTRIBUTE_READONLY,
IntPtr.Zero);
//We have got handle now.
//Some necessary variables definitions
IntPtr driveLayoutPtr = IntPtr.Zero;
int DRIVE_LAYOUT_BUFFER_SIZE = 1024;
int error;
uint dummy = 0;
do
{
error = 0;
driveLayoutPtr = Marshal.AllocHGlobal(DRIVE_LAYOUT_BUFFER_SIZE);
if (DeviceIoControl
(hndl, Native.IOCTL_DISK_GET_DRIVE_LAYOUT_EX, IntPtr.Zero, 0, driveLayoutPtr,
(uint)DRIVE_LAYOUT_BUFFER_SIZE, ref dummy, IntPtr.Zero))
{
// I/O-control has been invoked successfully,
//convert to DRIVE_LAYOUT_INFORMATION_EX
DRIVE_LAYOUT_INFORMATION_EX driveLayout = (DRIVE_LAYOUT_INFORMATION_EX)
Marshal.PtrToStructure(driveLayoutPtr, typeof(DRIVE_LAYOUT_INFORMATION_EX));
//Now i have got partition
for (uint p = 0; p < driveLayout.PartitionCount; p++)
{
//Enumerate partition by using pointer arithmetic
IntPtr ptr = new IntPtr(driveLayoutPtr.ToInt64() +
Marshal.OffsetOf(typeof(DRIVE_LAYOUT_INFORMATION_EX), "PartitionEntry").ToInt64()
+ (p * Marshal.SizeOf(typeof(PARTITION_INFORMATION_EX))));
PARTITION_INFORMATION_EX partInfo = (PARTITION_INFORMATION_EX)
Marshal.PtrToStructure(ptr, typeof(PARTITION_INFORMATION_EX));
//Check partition is recognized or not
if (partInfo.PartitionStyle != PARTITION_STYLE.PARTITION_STYLE_GPT)
{
if ((partInfo.PartitionStyle != PARTITION_STYLE.PARTITION_STYLE_MBR) ||
(partInfo.Mbr.RecognizedPartition))
{
if (partInfo.Mbr.BootIndicator == true)
{
Console.WriteLine("Drive No: " + i + " Partition Number :" +
partInfo.PartitionNumber + " is boot partition");
}
}
}
else if (partInfo.PartitionStyle == PARTITION_STYLE.PARTITION_STYLE_GPT) {
//e3c9e316-0b5c-4db8-817d-f92df00215ae guid is defined from MS here:
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa365449(v=vs.85).aspx
if (partInfo.Gpt.PartitionType== new Guid("e3c9e316-0b5c-4db8-817d-f92df00215ae"))
{
Console.WriteLine("Drive No: " + i + " Partition Number :"
+ partInfo.PartitionNumber + " is boot partition");
}
}
}
}
else
{
error = Marshal.GetLastWin32Error();
DRIVE_LAYOUT_BUFFER_SIZE *= 2;
}
Marshal.FreeHGlobal(driveLayoutPtr);
driveLayoutPtr = IntPtr.Zero;
} while (error == Native.ERROR_INSUFFICIENT_BUFFER);
}
As seen above, we get all partitions and check each one is BootIndicator
or not.
The important thing is at first, filling DRIVE_LAYOUT_INFORMATION_EX structure by using the DeviceIoControl function. And then there is pointer arithmetic to extract buffer. Finally, We need to convert byte buffer to the PARTITION_INFORMATION_EX structure. That's all!
Don't forget that boot partition doesn't have to be separated on all Windows installed machines. Consider that boot OS files may be on OS installed partition which is generally " Local Disk (C:) ".
Finally, if you want to test sample application, the application requires administrator credentials to run correctly.
Points of Interest
I wrote this article for developers which are interested in Win API programming. And I think low-level programming is very hard and interesting. So I like learning new things when I develop a program. I think if you are interested in learning fundamentals of something, then this topic will help you to identify boot partition programmatically. I hope you enjoyed reading this article.