diff --git a/BadBuilder.Formatter/BadBuilder.Formatter.csproj b/BadBuilder.Formatter/BadBuilder.Formatter.csproj index bd44ee5..2d6a66a 100644 --- a/BadBuilder.Formatter/BadBuilder.Formatter.csproj +++ b/BadBuilder.Formatter/BadBuilder.Formatter.csproj @@ -7,4 +7,11 @@ true + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/BadBuilder.Formatter/Constants.cs b/BadBuilder.Formatter/Constants.cs index 99a3530..474a431 100644 --- a/BadBuilder.Formatter/Constants.cs +++ b/BadBuilder.Formatter/Constants.cs @@ -5,5 +5,38 @@ internal const string ORANGE = "\u001b[38;2;255;114;0m"; internal const string ANSI_RESET = "\u001b[0m"; + + + internal const uint GENERIC_READ = 0x80000000; + internal const uint GENERIC_WRITE = 0x40000000; + internal const uint OPEN_EXISTING = 3; + internal const uint FILE_SHARE_READ = 1; + internal const uint FILE_BEGIN = 0; + internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000; + + internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x00070000; + internal const uint IOCTL_DISK_GET_PARTITION_INFO_EX = 0x00070048; + internal const uint IOCTL_DISK_GET_PARTITION_INFO = 0x00074004; + internal const uint IOCTL_DISK_SET_PARTITION_INFO = 0x0007C008; + internal const uint FSCTL_LOCK_VOLUME = 0x00090018; + internal const uint FSCTL_DISMOUNT_VOLUME = 0x00090020; + internal const uint FSCTL_UNLOCK_VOLUME = 0x0009001C; + internal const uint FSCTL_QUERY_RETRIEVAL_POINTERS = 0x0009003B; + internal const uint FSCTL_GET_COMPRESSION = 0x0009003C; + internal const uint FSCTL_SET_COMPRESSION = 0x0009C040; + internal const uint FSCTL_SET_BOOTLOADER_ACCESSED = 0x0009004F; + internal const uint FSCTL_MARK_AS_SYSTEM_HIVE = 0x0009004F; + internal const uint FSCTL_OPLOCK_BREAK_ACK_NO_2 = 0x00090050; + internal const uint FSCTL_INVALIDATE_VOLUMES = 0x00090054; + internal const uint FSCTL_QUERY_FAT_BPB = 0x00090058; + internal const uint FSCTL_REQUEST_FILTER_OPLOCK = 0x0009005C; + internal const uint FSCTL_FILESYSTEM_GET_STATISTICS = 0x00090060; + internal const uint FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064; + internal const uint FSCTL_GET_NTFS_FILE_RECORD = 0x00090068; + internal const uint FSCTL_GET_VOLUME_BITMAP = 0x0009006F; + internal const uint FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073; + internal const uint FSCTL_MOVE_FILE = 0x00090074; + internal const uint FSCTL_IS_VOLUME_DIRTY = 0x00090078; + internal const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x00090083; } } \ No newline at end of file diff --git a/BadBuilder.Formatter/DiskFormatter.cs b/BadBuilder.Formatter/DiskFormatter.cs index 81b7b6a..b6638e8 100644 --- a/BadBuilder.Formatter/DiskFormatter.cs +++ b/BadBuilder.Formatter/DiskFormatter.cs @@ -1,114 +1,220 @@ -using static BadBuilder.Formatter.Win32; -using static BadBuilder.Formatter.Constants; -using static BadBuilder.Formatter.FAT32Utilities; +#pragma warning disable CA1416 +using System.Text; +using Windows.Win32.System.Ioctl; +using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; +using Windows.Win32.Storage.FileSystem; + +using static Windows.Win32.PInvoke; +using static BadBuilder.Formatter.Constants; +using static BadBuilder.Formatter.Utilities; namespace BadBuilder.Formatter { public static class DiskFormatter { - public static unsafe (int, string) FormatVolume(char driveLetter) + public static unsafe string FormatVolume(char driveLetter) { - uint cbRet; - - DISK_GEOMETRY diskGeometry; - PARTITION_INFORMATION diskPartInfo; - PARTITION_INFORMATION_EX exDiskPartInfo; - bool isGPT = false; - uint bytesPerSector = 0; - uint totalSectors; - uint fatSize; - string devicePath = $"\\\\.\\{driveLetter}:"; uint volumeID = GetVolumeID(); + using SafeFileHandle driveHandle = OpenDeviceHandle(devicePath); + if (driveHandle.IsInvalid) return Error("Unable to open device. GetLastError: " + Marshal.GetLastWin32Error()); - IntPtr driveHandle = CreateFileW( - devicePath, - GENERIC_READ | GENERIC_WRITE, - 0, - 0, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING, - 0); + if (!EnableExtendedDASDIO(driveHandle) || !LockDevice(driveHandle)) + return Error("Failed to initialize device access."); - if (driveHandle == -1) return (-1, Error("Unable to open device - close all open programs or windows that may have a handle lock on the drive.")); + DISK_GEOMETRY diskGeometry; + if (!TryGetDiskGeometry(driveHandle, out diskGeometry)) + return Error("Failed to get drive geometry."); - if (!DeviceIoControl( driveHandle, FSCTL_ALLOW_EXTENDED_DASD_IO, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to enable extended DASD IO on the device.")); + PARTITION_INFORMATION partitionInfo = new(); + bool isGPT = false; + if (!TryGetPartitionInfo(driveHandle, ref diskGeometry, out partitionInfo, out isGPT)) + return Error("Failed to get partition information."); - if (!DeviceIoControl(driveHandle, FSCTL_LOCK_VOLUME, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to lock the device.")); + uint totalSectors = (uint)(partitionInfo.PartitionLength / diskGeometry.BytesPerSector); + if (!IsValidFAT32Size(totalSectors)) + return Error("Invalid drive size for FAT32."); - - using (var pDiskGeometry = NativePointer.Allocate()) + FAT32BootSector bootSector = InitializeBootSector(diskGeometry, partitionInfo, totalSectors, volumeID); + FAT32FsInfoSector fsInfo = InitializeFsInfo(); + uint[] firstFATSector = InitializeFirstFATSector(diskGeometry.BytesPerSector); + + string formatOutput = FormatVolumeData(driveHandle, diskGeometry, bootSector, fsInfo, firstFATSector, isGPT, partitionInfo); + if (formatOutput != string.Empty) + return formatOutput; + + if (!UnlockDevice(driveHandle) || !DismountVolume(driveHandle)) + return Error($"Failed to release the device. GetLastError: {Marshal.GetLastWin32Error()}"); + + + return string.Empty; + } + + + private static SafeFileHandle OpenDeviceHandle(string devicePath) => + CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, 0, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_NO_BUFFERING, null); + + private static unsafe bool EnableExtendedDASDIO(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_ALLOW_EXTENDED_DASD_IO, null, 0, null, 0, null, null); + + private static unsafe bool LockDevice(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_LOCK_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool UnlockDevice(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool DismountVolume(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool TryGetDiskGeometry(SafeFileHandle handle, out DISK_GEOMETRY diskGeometry) + { + diskGeometry = new DISK_GEOMETRY(); + fixed (DISK_GEOMETRY* pDiskGeometry = &diskGeometry) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, pDiskGeometry.Pointer, pDiskGeometry.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive geometry.")); - - diskGeometry = Marshal.PtrToStructure(pDiskGeometry.Pointer); + return DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, null, 0, pDiskGeometry, (uint)sizeof(DISK_GEOMETRY), null, null); } - bytesPerSector = diskGeometry.BytesPerSector; + } - using (var pDrivePartInfo = NativePointer.Allocate()) + private static unsafe bool TryGetPartitionInfo(SafeFileHandle handle, ref DISK_GEOMETRY diskGeometry, out PARTITION_INFORMATION partitionInfo, out bool isGPT) + { + partitionInfo = new PARTITION_INFORMATION(); + PARTITION_INFORMATION_EX diskExPartInfo = new(); + isGPT = false; + + fixed (PARTITION_INFORMATION* pPartitionInfo = &partitionInfo) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_PARTITION_INFO, 0, 0, pDrivePartInfo.Pointer, pDrivePartInfo.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive partition information.")); + if (!DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO, null, 0, pPartitionInfo, (uint)sizeof(PARTITION_INFORMATION), null, null)) + { + if (DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO_EX, null, 0, &diskExPartInfo, (uint)sizeof(PARTITION_INFORMATION_EX), null, null)) + { + partitionInfo = new PARTITION_INFORMATION + { + StartingOffset = diskExPartInfo.StartingOffset, + PartitionLength = diskExPartInfo.PartitionLength, + HiddenSectors = (uint)(diskExPartInfo.StartingOffset / diskGeometry.BytesPerSector) + }; + isGPT = (diskExPartInfo.PartitionStyle == PARTITION_STYLE.PARTITION_STYLE_GPT); + return true; + } + return false; + } + return true; + } + } - diskPartInfo = Marshal.PtrToStructure(pDrivePartInfo.Pointer); + private static bool IsValidFAT32Size(uint totalSectors) => + totalSectors >= 65536 && totalSectors < 0xFFFFFFFF; + + private static unsafe FAT32BootSector InitializeBootSector(DISK_GEOMETRY diskGeometry, PARTITION_INFORMATION partitionInfo, uint totalSectors, uint volumeID) + { + byte sectorsPerCluster = CalculateSectorsPerCluster((ulong)partitionInfo.PartitionLength, diskGeometry.BytesPerSector); + uint fatSize = CalculateFATSize(totalSectors, 32, sectorsPerCluster, 2, diskGeometry.BytesPerSector); + + FAT32BootSector bootSector = new FAT32BootSector + { + BytesPerSector = (ushort)diskGeometry.BytesPerSector, + SectorsPerCluster = sectorsPerCluster, + ReservedSectorCount = 32, + NumberOfFATs = 2, + MediaDescriptor = 0xF8, + SectorsPerTrack = (ushort)diskGeometry.SectorsPerTrack, + NumberOfHeads = (ushort)diskGeometry.TracksPerCylinder, + HiddenSectors = partitionInfo.HiddenSectors, + TotalSectors = totalSectors, + SectorsPerFAT = fatSize, + RootCluster = 2, + FSInfoSector = 1, + BackupBootSector = 6, + DriveNumber = 0x80, + BootSignature = 0x29, + VolumeID = volumeID, + Signature = 0x55AA + }; + + Span rawBytes = MemoryMarshal.AsBytes(new Span(ref bootSector)); + + if (bootSector.BytesPerSector != 512) + { + rawBytes[bootSector.BytesPerSector - 2] = 0x55; + rawBytes[bootSector.BytesPerSector - 1] = 0xAA; } - using (var pDriveExPartInfo = NativePointer.Allocate()) + rawBytes[0] = 0xEB; + rawBytes[1] = 0x58; + rawBytes[2] = 0x90; + + string oemName = "MSWIN4.1"; + Encoding.ASCII.GetBytes(oemName).CopyTo(rawBytes.Slice(3, 8)); + + string volumeLabel = "BADUPDATE "; + Encoding.ASCII.GetBytes(volumeLabel).CopyTo(rawBytes.Slice(71, 11)); + + string fileSystemType = "FAT32 "; + Encoding.ASCII.GetBytes(fileSystemType).CopyTo(rawBytes.Slice(82, 8)); + + return bootSector; + } + + private static FAT32FsInfoSector InitializeFsInfo() => new FAT32FsInfoSector + { + LeadSignature = 0x41615252, + StructureSignature = 0x61417272, + TrailSignature = 0xAA550000, + FreeClusterCount = 0, + NextFreeCluster = 3 + }; + + private static uint[] InitializeFirstFATSector(uint bytesPerSector) + { + uint[] sector = new uint[bytesPerSector / 4]; + sector[0] = 0x0FFFFFF8; + sector[1] = 0x0FFFFFFF; + sector[2] = 0x0FFFFFFF; + return sector; + } + + private static unsafe string FormatVolumeData(SafeFileHandle driveHandle, DISK_GEOMETRY geometry, FAT32BootSector bootSector, FAT32FsInfoSector fsInfo, uint[] firstFATSector, bool isGPT, PARTITION_INFORMATION partitionInfo) + { + uint bytesPerSector = geometry.BytesPerSector; + uint totalSectors = (uint)(partitionInfo.PartitionLength / geometry.BytesPerSector); + uint userAreaSize = totalSectors - bootSector.ReservedSectorCount - (bootSector.NumberOfFATs * bootSector.SectorsPerFAT); + uint systemAreaSize = bootSector.ReservedSectorCount + (bootSector.NumberOfFATs * bootSector.SectorsPerFAT) + bootSector.SectorsPerCluster; + uint clusterCount = userAreaSize / bootSector.SectorsPerCluster; + + if (clusterCount < 65536 || clusterCount > 0x0FFFFFFF) + return Error("The drives cluster count is out of range (65536 < clusterCount < 0x0FFFFFFF)"); + + fsInfo.FreeClusterCount = (userAreaSize / bootSector.SectorsPerCluster) - 1; + + ZeroOutSectors(driveHandle, 0, systemAreaSize, bytesPerSector); + + for (int i = 0; i < 2; i++) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_PARTITION_INFO_EX, 0, 0, pDriveExPartInfo.Pointer, pDriveExPartInfo.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive extended partition information.")); - - exDiskPartInfo = Marshal.PtrToStructure(pDriveExPartInfo.Pointer); + uint sectorStart = (i == 0) ? 0 : (uint)bootSector.BackupBootSector; + WriteSector(driveHandle, sectorStart, 1, bytesPerSector, StructToBytes(bootSector)); + WriteSector(driveHandle, sectorStart + 1, 1, bytesPerSector, StructToBytes(fsInfo)); } - isGPT = (exDiskPartInfo.PartitionStyle == PARTITION_STYLE.GPT); - totalSectors = (uint)(diskPartInfo.PartitionLength / diskGeometry.BytesPerSector); - if (totalSectors < 65536 || totalSectors >= 0xffffffff) - return (-1, Error("Invalid drive size for FAT32 - either too small (less than 64K clusters) or too large (greater than 2TB).")); + for (int i = 0; i < bootSector.NumberOfFATs; i++) + { + uint sectorStart = bootSector.ReservedSectorCount + ((uint)i * bootSector.SectorsPerFAT); + WriteSector(driveHandle, sectorStart, 1, bytesPerSector, UintArrayToBytes(firstFATSector)); + } - FAT32BootSector bootSector; - FAT32FsInfoSector fsInfo; + if (!isGPT) + { + SET_PARTITION_INFORMATION setPartInfo = new SET_PARTITION_INFORMATION + { + PartitionType = 0x0C + }; - bootSector.JumpCode = [0xEB, 0x58, 0x90]; - bootSector.OEMName = "MSWIN4.1".ToCharArray(); - bootSector.BytesPerSector = (ushort)bytesPerSector; - bootSector.SectorsPerCluster = CalculateSectorsPerCluster((ulong)diskPartInfo.PartitionLength, bytesPerSector); - bootSector.ReservedSectorCount = 32; - bootSector.NumberOfFATs = 2; - bootSector.MaxRootEntries = 0; - bootSector.TotalSectors16 = 0; - bootSector.MediaDescriptor = 0xF8; - bootSector.SectorsPerFAT16 = 0; - bootSector.SectorsPerTrack = (ushort)diskGeometry.SectorsPerTrack; - bootSector.NumberOfHeads = (ushort)diskGeometry.TracksPerCylinder; - bootSector.HiddenSectors = diskPartInfo.HiddenSectors; - bootSector.TotalSectors = totalSectors; + if (!DeviceIoControl(driveHandle, IOCTL_DISK_SET_PARTITION_INFO, &setPartInfo, (uint)sizeof(SET_PARTITION_INFORMATION), null, 0, null, null)) + return Error($"Failed to set the drive partition information. GetLastError: {Marshal.GetLastWin32Error()}"); + } - fatSize = CalculateFATSize(bootSector.TotalSectors, bootSector.ReservedSectorCount, bootSector.SectorsPerCluster, bootSector.NumberOfFATs, bytesPerSector); - - bootSector.SectorsPerFAT = fatSize; - bootSector.FATFlags = 0; - bootSector.FileSystemVersion = 0; - bootSector.RootCluster = 2; - bootSector.FSInfoSector = 1; - bootSector.BackupBootSector = 6; - bootSector.DriveNumber = 0x80; - bootSector.Reserved1 = 0; - bootSector.BootSignature = 0x29; - bootSector.VolumeID = volumeID; - bootSector.VolumeLabel = "BADUPDATE ".ToCharArray(); - bootSector.FileSystemType = "FAT32 ".ToCharArray(); - bootSector.Signature = 0x55AA; - - if (!DeviceIoControl(driveHandle, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to unlock the device.")); - - return (0, ""); + return ""; } } -} +} \ No newline at end of file diff --git a/BadBuilder.Formatter/FAT32BootSector.cs b/BadBuilder.Formatter/FAT32BootSector.cs index ea5d3c6..5f1351e 100644 --- a/BadBuilder.Formatter/FAT32BootSector.cs +++ b/BadBuilder.Formatter/FAT32BootSector.cs @@ -5,15 +5,10 @@ namespace BadBuilder.Formatter // Reference: https://cscie92.dce.harvard.edu/spring2024/K70F120M/bootSector.h [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 512)] - internal struct FAT32BootSector + internal unsafe struct FAT32BootSector { - [FieldOffset(0)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - internal byte[] JumpCode; - - [FieldOffset(3)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal char[] OEMName; + [FieldOffset(0)] public fixed byte JumpCode[3]; + [FieldOffset(3)] public fixed char OEMName[8]; [FieldOffset(11)] internal ushort BytesPerSector; [FieldOffset(13)] internal byte SectorsPerCluster; @@ -36,26 +31,15 @@ namespace BadBuilder.Formatter [FieldOffset(48)] internal ushort FSInfoSector; [FieldOffset(50)] internal ushort BackupBootSector; - [FieldOffset(52)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - internal byte[] Reserved; + [FieldOffset(52)] public fixed byte Reserved[12]; - [FieldOffset(64)] internal byte DriveNumber; - [FieldOffset(65)] internal byte Reserved1; - [FieldOffset(66)] internal byte BootSignature; - [FieldOffset(67)] internal uint VolumeID; - - [FieldOffset(71)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - internal char[] VolumeLabel; - - [FieldOffset(82)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal char[] FileSystemType; - - [FieldOffset(90)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 420)] - internal byte[] Reserved2; + [FieldOffset(64)] public byte DriveNumber; + [FieldOffset(65)] public byte Reserved1; + [FieldOffset(66)] public byte BootSignature; + [FieldOffset(67)] public uint VolumeID; + [FieldOffset(71)] public fixed char VolumeLabel[11]; + [FieldOffset(82)] public fixed char FileSystemType[8]; + [FieldOffset(90)] public fixed byte Reserved2[420]; [FieldOffset(510)] internal ushort Signature; } diff --git a/BadBuilder.Formatter/FAT32FsInfoSector.cs b/BadBuilder.Formatter/FAT32FsInfoSector.cs index bfdb1ef..2daac5e 100644 --- a/BadBuilder.Formatter/FAT32FsInfoSector.cs +++ b/BadBuilder.Formatter/FAT32FsInfoSector.cs @@ -5,21 +5,17 @@ namespace BadBuilder.Formatter // Reference: https://cscie92.dce.harvard.edu/spring2024/K70F120M/fsInfo.h [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 512)] - internal struct FAT32FsInfoSector + internal unsafe struct FAT32FsInfoSector { [FieldOffset(0)] public uint LeadSignature; - [FieldOffset(4)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] - public byte[] Reserved1; // Zeros + [FieldOffset(4)] public fixed byte Reserved1[480]; // Zeros [FieldOffset(484)] public uint StructureSignature; [FieldOffset(488)] public uint FreeClusterCount; [FieldOffset(492)] public uint NextFreeCluster; - [FieldOffset(496)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public byte[] Reserved2; + [FieldOffset(496)] public fixed byte Reserved2[12]; [FieldOffset(508)] public uint TrailSignature; } diff --git a/BadBuilder.Formatter/NativeMethods.txt b/BadBuilder.Formatter/NativeMethods.txt new file mode 100644 index 0000000..fd019bf --- /dev/null +++ b/BadBuilder.Formatter/NativeMethods.txt @@ -0,0 +1,12 @@ +CreateFileW +WriteFile +SetFilePointer +DeviceIoControl +CloseHandle +VirtualAlloc +VirtualFree +GUID +DISK_GEOMETRY +PARTITION_INFORMATION +PARTITION_INFORMATION_EX +SET_PARTITION_INFORMATION \ No newline at end of file diff --git a/BadBuilder.Formatter/FAT32Utilities.cs b/BadBuilder.Formatter/Utilities.cs similarity index 50% rename from BadBuilder.Formatter/FAT32Utilities.cs rename to BadBuilder.Formatter/Utilities.cs index ab1c011..23fe006 100644 --- a/BadBuilder.Formatter/FAT32Utilities.cs +++ b/BadBuilder.Formatter/Utilities.cs @@ -1,39 +1,24 @@ -using static BadBuilder.Formatter.Win32; -using static BadBuilder.Formatter.Constants; +using Windows.Win32.Foundation; +using Windows.Win32.System.Memory; using System.Runtime.InteropServices; +using static Windows.Win32.PInvoke; +using static BadBuilder.Formatter.Constants; + namespace BadBuilder.Formatter { - static class FAT32Utilities + static class Utilities { - internal struct NativePointer : IDisposable + internal static byte[] StructToBytes(T @struct) where T : struct { - internal IntPtr Pointer; - internal uint Size; - - internal NativePointer(Type type) - { - uint size = (uint)Marshal.SizeOf(type); - Pointer = Marshal.AllocHGlobal((int)size); - Size = size; - } - - public void Dispose() - { - if (Pointer != IntPtr.Zero) - { - Marshal.FreeHGlobal(Pointer); - Pointer = IntPtr.Zero; - Size = 0; - } - } - - internal static NativePointer Allocate() where T : struct - { - return new NativePointer(typeof(T)); - } + Span structSpan = MemoryMarshal.CreateSpan(ref @struct, 1); + Span byteSpan = MemoryMarshal.AsBytes(structSpan); + return byteSpan.ToArray(); } + internal static byte[] UintArrayToBytes(uint[] array) => MemoryMarshal.AsBytes(array.AsSpan()).ToArray(); + + internal static string Error(string error) => $"{ORANGE}[-]{ANSI_RESET} {error}"; internal static void ExitWithError(string error) { @@ -74,41 +59,54 @@ namespace BadBuilder.Formatter }; - internal static void SeekTo(IntPtr hDevice, uint sector, uint bytesPerSector) + internal static unsafe void SeekTo(SafeHandle hDevice, uint sector, uint bytesPerSector) { long offset = sector * bytesPerSector; int lowOffset = (int)(offset & 0xFFFFFFFF); int highOffset = (int)(offset >> 32); - SetFilePointer(hDevice, lowOffset, ref highOffset, FILE_BEGIN); + SetFilePointer(hDevice, lowOffset, &highOffset, Windows.Win32.Storage.FileSystem.SET_FILE_POINTER_MOVE_METHOD.FILE_BEGIN); } - internal static void WriteSector(IntPtr hDevice, uint sector, uint numberOfSectors, uint bytesPerSector, byte[] data) + internal static unsafe void WriteSector(SafeHandle hDevice, uint sector, uint numberOfSectors, uint bytesPerSector, byte[] data) { uint bytesWritten; SeekTo(hDevice, sector, bytesPerSector); - if (!WriteFile(hDevice, data, numberOfSectors * bytesPerSector, out bytesWritten, 0)) - ExitWithError("Unable to write to write sectors to FAT32 device, exiting."); + fixed (byte* pData = &data[0]) + { + if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pData, numberOfSectors * bytesPerSector, &bytesWritten, null)) + ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + } } - internal static void ZeroOutSectors(IntPtr hDevice, uint sector, uint numberOfSectors, uint bytesPerSector) + internal static unsafe void ZeroOutSectors(SafeHandle hDevice, uint sector, uint numberOfSectors, uint bytesPerSector) { const uint burstSize = 128; uint writeSize; + uint bytesWritten; - byte[] zeroBuffer = new byte[bytesPerSector * burstSize]; - Array.Clear(zeroBuffer); + byte* pZeroSector = (byte*)VirtualAlloc(null, bytesPerSector * burstSize, VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT | VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE, PAGE_PROTECTION_FLAGS.PAGE_READWRITE); - SeekTo(hDevice, sector, bytesPerSector); - - while (numberOfSectors > 0) + try { - writeSize = (numberOfSectors > burstSize) ? burstSize : numberOfSectors; - WriteSector(hDevice, sector, numberOfSectors, bytesPerSector, zeroBuffer); - numberOfSectors -= writeSize; + SeekTo(hDevice, sector, bytesPerSector); + + while (numberOfSectors > 0) + { + writeSize = (numberOfSectors > burstSize) ? burstSize : numberOfSectors; + + if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pZeroSector, writeSize * bytesPerSector, &bytesWritten, null)) + ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + + numberOfSectors -= writeSize; + } + } + finally + { + VirtualFree(pZeroSector, bytesPerSector * burstSize, VIRTUAL_FREE_TYPE.MEM_RELEASE); } } } diff --git a/BadBuilder.Formatter/Win32.cs b/BadBuilder.Formatter/Win32.cs deleted file mode 100644 index 91154d3..0000000 --- a/BadBuilder.Formatter/Win32.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System.Runtime.InteropServices; - -namespace BadBuilder.Formatter -{ - static partial class Win32 - { - internal const uint GENERIC_READ = 0x80000000; - internal const uint GENERIC_WRITE = 0x40000000; - internal const uint OPEN_EXISTING = 3; - internal const uint FILE_SHARE_READ = 1; - internal const uint FILE_BEGIN = 0; - internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000; - - internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x00070000; - internal const uint IOCTL_DISK_GET_PARTITION_INFO_EX = 0x00070048; - internal const uint IOCTL_DISK_GET_PARTITION_INFO = 0x00074004; - internal const uint FSCTL_LOCK_VOLUME = 0x00090018; - internal const uint FSCTL_UNLOCK_VOLUME = 0x0009001C; - internal const uint FSCTL_QUERY_RETRIEVAL_POINTERS = 0x0009003B; - internal const uint FSCTL_GET_COMPRESSION = 0x0009003C; - internal const uint FSCTL_SET_COMPRESSION = 0x0009C040; - internal const uint FSCTL_SET_BOOTLOADER_ACCESSED = 0x0009004F; - internal const uint FSCTL_MARK_AS_SYSTEM_HIVE = 0x0009004F; - internal const uint FSCTL_OPLOCK_BREAK_ACK_NO_2 = 0x00090050; - internal const uint FSCTL_INVALIDATE_VOLUMES = 0x00090054; - internal const uint FSCTL_QUERY_FAT_BPB = 0x00090058; - internal const uint FSCTL_REQUEST_FILTER_OPLOCK = 0x0009005C; - internal const uint FSCTL_FILESYSTEM_GET_STATISTICS = 0x00090060; - internal const uint FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064; - internal const uint FSCTL_GET_NTFS_FILE_RECORD = 0x00090068; - internal const uint FSCTL_GET_VOLUME_BITMAP = 0x0009006F; - internal const uint FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073; - internal const uint FSCTL_MOVE_FILE = 0x00090074; - internal const uint FSCTL_IS_VOLUME_DIRTY = 0x00090078; - internal const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x00090083; - - - private const string Kernel32 = "kernel32.dll"; - - [LibraryImport(Kernel32, SetLastError = true)] - internal static partial IntPtr CreateFileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile); - - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool WriteFile( - IntPtr hFile, - byte[] lpBuffer, - uint nNumberOfBytesToWrite, - out uint lpNumberOfBytesWritten, - IntPtr lpOverlapped); - - [LibraryImport(Kernel32, SetLastError = true)] - internal static partial uint SetFilePointer( - IntPtr hFile, - int lDistanceToMove, - ref int lpDistanceToMoveHigh, - uint dwMoveMethod); - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - IntPtr lpInBuffer, - uint nInBufferSize, - IntPtr lpOutBuffer, - uint nOutBufferSize, - out uint lpBytesReturned, - IntPtr lpOverlapped); - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr hObject); - - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct DISK_GEOMETRY - { - internal long Cylinders; - internal MediaType MediaType; - internal uint TracksPerCylinder; - internal uint SectorsPerTrack; - internal uint BytesPerSector; - } - - internal enum MediaType // from winioctl.h - { - Unknown, // Format is unknown - F5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector - F3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector - F3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector - F3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector - F3_720_512, // 3.5", 720KB, 512 bytes/sector - F5_360_512, // 5.25", 360KB, 512 bytes/sector - F5_320_512, // 5.25", 320KB, 512 bytes/sector - F5_320_1024, // 5.25", 320KB, 1024 bytes/sector - F5_180_512, // 5.25", 180KB, 512 bytes/sector - F5_160_512, // 5.25", 160KB, 512 bytes/sector - RemovableMedia, // Removable media other than floppy - FixedMedia, // Fixed hard disk media - F3_120M_512, // 3.5", 120M Floppy - F3_640_512, // 3.5" , 640KB, 512 bytes/sector - F5_640_512, // 5.25", 640KB, 512 bytes/sector - F5_720_512, // 5.25", 720KB, 512 bytes/sector - F3_1Pt2_512, // 3.5" , 1.2Mb, 512 bytes/sector - F3_1Pt23_1024, // 3.5" , 1.23Mb, 1024 bytes/sector - F5_1Pt23_1024, // 5.25", 1.23MB, 1024 bytes/sector - F3_128Mb_512, // 3.5" MO 128Mb 512 bytes/sector - F3_230Mb_512, // 3.5" MO 230Mb 512 bytes/sector - F8_256_128, // 8", 256KB, 128 bytes/sector - F3_200Mb_512, // 3.5", 200M Floppy (HiFD) - F3_240M_512, // 3.5", 240Mb Floppy (HiFD) - F3_32M_512 // 3.5", 32Mb Floppy - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)] - internal struct GUID - { - internal uint Data1; - internal ushort Data2; - internal ushort Data3; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal byte[] Data4; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 32)] - internal struct PARTITION_INFORMATION - { - internal long StartingOffset; - internal long PartitionLength; - internal uint HiddenSectors; - internal uint PartitionNumber; - internal byte PartitionType; - internal byte BootIndicator; - internal byte RecognizedPattern; - internal byte RewritePartition; - } - - [StructLayout(LayoutKind.Explicit, Size = 144)] - internal struct PARTITION_INFORMATION_EX - { - [FieldOffset(0)] - internal PARTITION_STYLE PartitionStyle; - - [FieldOffset(8)] - internal long StartingOffset; - - [FieldOffset(16)] - internal long PartitionLength; - - [FieldOffset(24)] - internal uint PartitionNumber; - - [FieldOffset(28)] - internal byte RewritePartition; - - [FieldOffset(29)] - internal byte IsServicePartition; - - [FieldOffset(32)] - internal unsafe fixed byte Union[112]; - - public unsafe PARTITION_INFORMATION_MBR Mbr - { - get - { - fixed (byte* p = Union) - { - return *(PARTITION_INFORMATION_MBR*)p; - } - } - set - { - fixed (byte* p = Union) - { - *(PARTITION_INFORMATION_MBR*)p = value; - } - } - } - - public unsafe PARTITION_INFORMATION_GPT Gpt - { - get - { - fixed (byte* p = Union) - { - return *(PARTITION_INFORMATION_GPT*)p; - } - } - set - { - fixed (byte* p = Union) - { - *(PARTITION_INFORMATION_GPT*)p = value; - } - } - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 24)] - internal struct PARTITION_INFORMATION_MBR - { - internal byte PartitionType; - internal byte BootIndicator; - internal byte RecognizedPartition; - private byte _padding1; - internal uint HiddenSectors; - internal GUID PartitionId; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 112)] - internal struct PARTITION_INFORMATION_GPT - { - internal GUID PartitionType; - internal GUID PartitionId; - internal ulong Attributes; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] - internal ushort[] Name; - } - - internal enum PARTITION_STYLE : uint - { - MBR = 0, - GPT = 1, - RAW = 2 - } - } -} \ No newline at end of file diff --git a/BadBuilder/BadBuilder.csproj b/BadBuilder/BadBuilder.csproj index 9032e08..558c64d 100644 --- a/BadBuilder/BadBuilder.csproj +++ b/BadBuilder/BadBuilder.csproj @@ -13,10 +13,6 @@ - - - - diff --git a/BadBuilder/ConsoleExperiences/DiskExperience.cs b/BadBuilder/ConsoleExperiences/DiskExperience.cs index bebf627..66b0ee4 100644 --- a/BadBuilder/ConsoleExperiences/DiskExperience.cs +++ b/BadBuilder/ConsoleExperiences/DiskExperience.cs @@ -1,11 +1,6 @@ using Spectre.Console; using BadBuilder.Models; using BadBuilder.Helpers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BadBuilder { @@ -38,15 +33,28 @@ namespace BadBuilder ); } - static void FormatDisk(List disks, string selectedDisk) + static bool FormatDisk(List disks, string selectedDisk) { int diskIndex = disks.FindIndex(disk => $"{disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}" == selectedDisk); + bool ret = true; + string output = string.Empty; AnsiConsole.Status().SpinnerStyle(LightOrangeStyle).Start($"[#76B900]Formatting disk[/] {selectedDisk}", ctx => { if (diskIndex == -1) return; - DiskHelper.FormatDisk(disks[diskIndex]).Wait(); + + output = DiskHelper.FormatDisk(disks[diskIndex]); + if (output != string.Empty) ret = false; }); + + if (!ret) + { + AnsiConsole.Clear(); + ShowWelcomeMessage(); + Console.Write("\n" + output + "\n"); + } + + return ret; } } -} +} \ No newline at end of file diff --git a/BadBuilder/ConsoleExperiences/DownloadExperience.cs b/BadBuilder/ConsoleExperiences/DownloadExperience.cs index c216de6..aad3fe0 100644 --- a/BadBuilder/ConsoleExperiences/DownloadExperience.cs +++ b/BadBuilder/ConsoleExperiences/DownloadExperience.cs @@ -1,7 +1,7 @@ using Spectre.Console; using BadBuilder.Helpers; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; namespace BadBuilder { diff --git a/BadBuilder/ConsoleExperiences/ExtractExperience.cs b/BadBuilder/ConsoleExperiences/ExtractExperience.cs index 7a469b3..2671e7e 100644 --- a/BadBuilder/ConsoleExperiences/ExtractExperience.cs +++ b/BadBuilder/ConsoleExperiences/ExtractExperience.cs @@ -30,4 +30,4 @@ namespace BadBuilder AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{filesToExtract.Count()}[/] files extracted."); } } -} +} \ No newline at end of file diff --git a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs index b098e81..df9904a 100644 --- a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs +++ b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs @@ -1,9 +1,4 @@ using Spectre.Console; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BadBuilder { @@ -167,4 +162,4 @@ namespace BadBuilder AnsiConsole.MarkupLine($"[#ffac4d]Removed:[/] {selectedApp.folder.Split('\\').Last()}\n"); } } -} +} \ No newline at end of file diff --git a/BadBuilder/Helpers/ArchiveHelper.cs b/BadBuilder/Helpers/ArchiveHelper.cs index ef731a0..0021406 100644 --- a/BadBuilder/Helpers/ArchiveHelper.cs +++ b/BadBuilder/Helpers/ArchiveHelper.cs @@ -1,8 +1,9 @@ -using SharpCompress.Archives; -using SharpCompress.Common; -using Spectre.Console; +using Spectre.Console; using System.Diagnostics; -using static BadBuilder.Constants; +using SharpCompress.Common; +using SharpCompress.Archives; + +using static BadBuilder.Utilities.Constants; namespace BadBuilder.Helpers { @@ -27,7 +28,7 @@ namespace BadBuilder.Helpers } catch (Exception ex) { - // Sorry, but these exceptions are invalid. SharpCompress is mad because there's nested ZIPs in xexmenu. + // Sorry, but these exceptions are invalid. The files extract just fine. Debug.WriteLine(ex); } } diff --git a/BadBuilder/Helpers/DiskHelper.cs b/BadBuilder/Helpers/DiskHelper.cs index 784c299..9403dfa 100644 --- a/BadBuilder/Helpers/DiskHelper.cs +++ b/BadBuilder/Helpers/DiskHelper.cs @@ -1,6 +1,5 @@ using BadBuilder.Models; -using System.Diagnostics; -using System.Management; +using BadBuilder.Formatter; namespace BadBuilder.Helpers { @@ -28,25 +27,6 @@ namespace BadBuilder.Helpers return disks; } - internal static async Task FormatDisk(DiskInfo disk) - { - ResourceHelper.ExtractEmbeddedBinary("fat32format.exe"); - - Process process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = @"fat32format.exe", - Arguments = $"-c64 {disk.DriveLetter}", - RedirectStandardOutput = false, - RedirectStandardError = false, - UseShellExecute = false, - CreateNoWindow = true - } - }; - - process.Start(); - await process.WaitForExitAsync(); - } + internal static string FormatDisk(DiskInfo disk) => DiskFormatter.FormatVolume(disk.DriveLetter.ToCharArray()[0]); } } \ No newline at end of file diff --git a/BadBuilder/Helpers/DownloadHelper.cs b/BadBuilder/Helpers/DownloadHelper.cs index f19ded5..3265531 100644 --- a/BadBuilder/Helpers/DownloadHelper.cs +++ b/BadBuilder/Helpers/DownloadHelper.cs @@ -1,7 +1,7 @@ using Octokit; using Spectre.Console; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; namespace BadBuilder.Helpers { diff --git a/BadBuilder/Helpers/FileSystemHelper.cs b/BadBuilder/Helpers/FileSystemHelper.cs index 38b90a7..e5c0630 100644 --- a/BadBuilder/Helpers/FileSystemHelper.cs +++ b/BadBuilder/Helpers/FileSystemHelper.cs @@ -1,6 +1,4 @@ -using BadBuilder.Models; - -namespace BadBuilder.Helpers +namespace BadBuilder.Helpers { internal static class FileSystemHelper { diff --git a/BadBuilder/Helpers/PatchHelper.cs b/BadBuilder/Helpers/PatchHelper.cs index 2aff559..16c6858 100644 --- a/BadBuilder/Helpers/PatchHelper.cs +++ b/BadBuilder/Helpers/PatchHelper.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; namespace BadBuilder.Helpers { diff --git a/BadBuilder/Helpers/ResourceHelper.cs b/BadBuilder/Helpers/ResourceHelper.cs deleted file mode 100644 index 92bc13e..0000000 --- a/BadBuilder/Helpers/ResourceHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; - -namespace BadBuilder.Helpers -{ - internal static class ResourceHelper - { - internal static void ExtractEmbeddedBinary(string resourceName) - { - var assembly = Assembly.GetExecutingAssembly(); - string fullResourceName = $"BadBuilder.Resources.{resourceName}"; - - using (Stream resourceStream = assembly.GetManifestResourceStream(fullResourceName)) - { - if (resourceStream == null) return; - - using (FileStream fileStream = new FileStream($@"{resourceName}", FileMode.Create, FileAccess.Write)) - resourceStream.CopyTo(fileStream); - } - } - } -} \ No newline at end of file diff --git a/BadBuilder/Models/DiskInfo.cs b/BadBuilder/Models/DiskInfo.cs index f385250..e9bbb49 100644 --- a/BadBuilder/Models/DiskInfo.cs +++ b/BadBuilder/Models/DiskInfo.cs @@ -37,4 +37,4 @@ return $"{bytes} bytes"; } } -} +} \ No newline at end of file diff --git a/BadBuilder/Program.cs b/BadBuilder/Program.cs index d0967cc..6c2cd7c 100644 --- a/BadBuilder/Program.cs +++ b/BadBuilder/Program.cs @@ -6,7 +6,8 @@ using Spectre.Console; using BadBuilder.Models; using BadBuilder.Helpers; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; +using BadBuilder.Utilities; namespace BadBuilder { @@ -26,7 +27,6 @@ namespace BadBuilder static void Main(string[] args) { - BadBuilder.Formatter.DiskFormatter.FormatVolume('E'); ShowWelcomeMessage(); while (true) @@ -43,7 +43,7 @@ namespace BadBuilder bool confirmation = PromptFormatConfirmation(selectedDisk); if (confirmation) { - FormatDisk(disks, selectedDisk); + if (!FormatDisk(disks, selectedDisk)) continue; break; } } diff --git a/BadBuilder/Resources/fat32format.exe b/BadBuilder/Resources/fat32format.exe deleted file mode 100644 index 40baa54..0000000 Binary files a/BadBuilder/Resources/fat32format.exe and /dev/null differ diff --git a/BadBuilder/Models/ActionQueue.cs b/BadBuilder/Utilities/ActionQueue.cs similarity index 96% rename from BadBuilder/Models/ActionQueue.cs rename to BadBuilder/Utilities/ActionQueue.cs index 7bb263c..7146615 100644 --- a/BadBuilder/Models/ActionQueue.cs +++ b/BadBuilder/Utilities/ActionQueue.cs @@ -1,4 +1,4 @@ -namespace BadBuilder.Models +namespace BadBuilder.Utilities { internal class ActionQueue { diff --git a/BadBuilder/Constants.cs b/BadBuilder/Utilities/Constants.cs similarity index 90% rename from BadBuilder/Constants.cs rename to BadBuilder/Utilities/Constants.cs index 1d0aa23..4ddd874 100644 --- a/BadBuilder/Constants.cs +++ b/BadBuilder/Utilities/Constants.cs @@ -1,4 +1,4 @@ -namespace BadBuilder +namespace BadBuilder.Utilities { internal static class Constants {