首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C# WriteFile()停止在USB驱动器上的242扇区写入

C# WriteFile()停止在USB驱动器上的242扇区写入
EN

Stack Overflow用户
提问于 2012-08-22 21:00:21
回答 5查看 6.1K关注 0票数 9

我编写了下面的代码来编写0xFF到我的USB存储设备上的所有字节。由于某些原因,WriteFile()调用在扇区242开始出错。我在两个单独的USB存储设备上完成了这个操作,然后在一个十六进制编辑器中检查了这些设备。扇区242似乎是FAT16格式化设备上文件分配表的开始,以及NTFS设备上启动区域的开始。我确信,它在这些确切的位置上出错是没有关联的,但是我不知道如何改变这种行为。当HRESULT失败时,我正在接收的WriteFile是-2147024891,即E_ACCESSDENIED。有人知道是什么导致了这个问题吗?

注意:如果要在本地系统上运行此代码,请非常小心,因为我已经为我的USB设备硬编码了物理设备ID。请确保使用要写入的设备更新deviceId变量。你不想毁了你的硬盘。

代码语言:javascript
运行
复制
    public enum EMoveMethod : uint
    {
        Begin = 0,
        Current = 1,
        End = 2
    }

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] long lDistanceToMove, [Out] out int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    [DllImport("kernel32", SetLastError = true)]
    internal extern static int ReadFile(SafeFileHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32.dll", SetLastError = true)]
    internal extern static int WriteFile(SafeFileHandle handle, byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool CloseHandle(SafeFileHandle handle);

public void wipeDisk()
{
        const uint OPEN_EXISTING = 3;
        const uint GENERIC_WRITE = (0x40000000);
        const uint FSCTL_LOCK_VOLUME = 0x00090018;
        const uint FSCTL_UNLOCK_VOLUME = 0x0009001c;
        const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;

        bool success = false;
        int intOut;
        string deviceId = @"\\.\PHYSICALDRIVE2";
        long DiskSize = 2056320000;

        SafeFileHandle diskHandle = CreateFile(deviceId, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
        if (diskHandle.IsInvalid)
        {
            Console.WriteLine(deviceId + " open error.");
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": opened.");

        success = DeviceIoControl(diskHandle, FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (!success)
        {
            Console.WriteLine(deviceId + " lock error.");
            CloseHandle(diskHandle);
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": locked.");

        success = DeviceIoControl(diskHandle, FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (!success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": dismount error.");
            DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
            CloseHandle(diskHandle);
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unmounted.");

        int numBytesPerSector = 512;
        long numTotalSectors = DiskSize / 512;

        byte[] junkBytes = new byte[512];
        for (int x = 0; x < 512; x++)
        {
            junkBytes[x] = 0xFF;
        }

        for (long sectorNum = 0; sectorNum < numTotalSectors; sectorNum++)
        {
            int numBytesWritten = 0;
            int moveToHigh;

            uint rvalsfp = SetFilePointer(diskHandle, sectorNum * numBytesPerSector, out moveToHigh, EMoveMethod.Begin);

            Console.WriteLine("File pointer set " + Marshal.GetHRForLastWin32Error().ToString() + ": " + (sectorNum * numBytesPerSector).ToString());

            int rval = WriteFile(diskHandle, junkBytes, junkBytes.Length, out numBytesWritten, IntPtr.Zero);

            if (numBytesWritten != junkBytes.Length)
            {
                Console.WriteLine("Write error on track " + sectorNum.ToString() + " from " + (sectorNum * numBytesPerSector).ToString() + "-" + moveToHigh.ToString() + " " + Marshal.GetHRForLastWin32Error().ToString() + ": Only " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
                break;
            }
            else
            {
                Console.WriteLine("Write success " + Marshal.GetHRForLastWin32Error().ToString() + ": " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
            }
        }

        success = DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlocked.");
        }
        else
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlock error: " + Marshal.GetHRForLastWin32Error().ToString());
        }

        success = CloseHandle(diskHandle);
        if (success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": handle closed.");
        }
        else
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": close handle error: " + Marshal.GetHRForLastWin32Error().ToString());
        }
}

编辑/更新

在使用第三方工具对USB设备进行低级别擦除之后,我能够成功地完成这个任务。在驱动器完全归零后,我能够成功地写到设备上。当windows识别有效的fat或ntfs文件系统和使用

代码语言:javascript
运行
复制
    const uint FSCTL_LOCK_VOLUME = 0x00090018;
    const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;

DeviceIoControl配对似乎不覆盖设备上的锁窗口。

是否有人知道如何在具有有效文件系统的驱动器上使用DeviceIoControl成功地锁定可移动的USB设备?

我已经使用了一些第三方工具来完成我想要做的事情,它们都很成功。我知道这是可能的,但我所读过的所有MSDN文档都无助于解决这个问题。

编辑/更新2

这是取自https://web.archive.org/web/20130507212546/http://msdn.microsoft.com/en-us/library/ff551353.aspx

应用程序在发布DASD /O之前需要锁定卷、卸载卷或两者兼而有之,这对于Windows来说是新的,是为了解决潜在的恶意技术。

  1. 文件系统将阻止对磁盘保留部分的所有写操作。在这种情况下,保留的部分包括MBR和两个胖区域。要阻止这些区域,您需要发送FSCTL_LOCK_VOLUME来锁定卷。必须在执行实际写操作的同一卷句柄上发出此结构。如果有打开的文件句柄,此请求可能失败。在这种情况下,应用程序可以通过发出FSCTL_DISMOUNT_VOLUME强制文件系统的卸载。但是,在关闭文件句柄之前,卷实际上不会被卸载。在此之前,应用程序可以使用当前打开的文件句柄继续发出DASD I/O。
  2. 在文件系统已知的卷空间之外有一个扩展区域,其中写入操作将被阻止。若要允许对此区域执行写操作,必须在卷句柄上发出FSCTL_ALLOW_EXTENDED_DASD_IO。

您可以使用Win32 API例程DeviceIoControl发出所有以前的FSCTS。

我相信这正是我们在上述代码中所实现的,但它似乎没有正常工作。我们得到了一个手柄,并且正在锁定和卸载设备,所以我们应该能够写到保护区域,对吗?

编辑/更新3

好的,这是当前打开磁盘和容量的顺序。锁定、拆卸等的方法只按我们认为错误的顺序工作。

代码语言:javascript
运行
复制
SafeFileHandle volumeHandle = CreateFile("\\.\E:",...);
LockVolume(volumeHandle);
DismountVolume(volumeHandle);
SafeFileHandle diskHandle = CreateFile("\\.\PHYSICALDRIVE1"...);
WriteStuff(diskHandle);
//Fails...
UnlockVolume(volumeHandle);
CloseVolume(volumeHandle);
CloseDisk(diskHandle);

我仍然得到同样的结果,只有当磁盘被破坏时,它才能工作。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-08-24 17:14:38

在这里,磁盘和驱动器之间有一种混淆。

如果希望完全访问磁盘(这是您使用\\.\PHYSICALDRIVE时的情况),则必须锁定所有挂载的卷,这些卷基本上都是分区(即物理磁盘的)。

不要在CreateFile("\\.\PHYSICALDRIVE"...)返回的句柄上使用CreateFile("\\.\PHYSICALDRIVE"...),而是使用string.Replace("\\\\.\\{0}:", DriveLetter)模式为每个安装的卷(它是驱动器,而不是物理磁盘)提供一个句柄。

您可以使用IOCTL_DISK_GET_DRIVE_LAYOUT获取给定物理磁盘的挂载卷列表(最终需要一个字母列表)。

编辑:

来自MSDN

如果下列条件之一为真,磁盘句柄上的写入将成功: 要写入的扇区不属于卷的范围。 要写入的扇区属于已装入的卷,但您已经使用FSCTL_LOCK_VOLUME或FSCTL_DISMOUNT_VOLUME显式锁定或卸载了卷。 要写入的扇区属于除了RAW之外没有挂载文件系统的卷内。

基本上,你应该做的是:

  • 获取每个卷的句柄
  • 在每个卷上使用FSCTL_LOCK_VOLUME FSCTL_DISMOUNT_VOLUME。如果卷中没有使用任何文件(即没有任何进程打开对任何文件的句柄),FSCTL_LOCK_VOLUME就足够了。
  • 获取物理磁盘的句柄
  • 写入物理磁盘
  • 关闭两个手柄。关闭卷手柄将释放锁。

还要确保您运行的应用程序具有管理权限(提升进程)。

票数 5
EN

Stack Overflow用户

发布于 2012-08-24 15:21:28

我猜您正在使用Windows Vista或更高版本。操作系统将阻止任何直接写入这些扇区的尝试,因此您需要先执行锁定操作。关于这一点的更多信息如下:

http://msdn.microsoft.com/en-us/library/ff551353.aspx

另外,我也是这么提这个帖子的:

CreateFile:对原始磁盘的直接写入操作“拒绝访问”- Vista,Win7

那里的调查信息可能会有帮助.

票数 1
EN

Stack Overflow用户

发布于 2012-08-27 16:27:29

编辑

我编辑了这个答案,以反映Ken2k的建议。

实际上,Ken2k的建议确实解决了我所面临的问题。我不知道为什么我以前使用该方法的尝试失败了,但是我刚刚重新访问/调整了我的代码,并且该方法看起来确实正确工作。

下面是我为解决这个问题而使用的步骤:

  • 获取物理磁盘的句柄
  • 获取物理磁盘上每个逻辑驱动器的句柄
  • 锁定物理磁盘上的每个驱动器
  • 卸下物理磁盘上的每个驱动器。
  • 锁定物理磁盘(可选)
  • 卸下物理磁盘(可选)
  • 使用物理磁盘句柄将整个物理磁盘清零。
  • 解锁每个逻辑驱动器
  • 解锁物理磁盘(仅当您选择锁定磁盘时)
  • 关闭逻辑驱动器句柄
  • 关闭物理磁盘句柄

注意:如果您希望在不终止程序的情况下执行背对背磁盘操作,并且已经使用了FSCTL_DISMOUNT_VOLUME功能,则需要使用类似于以下内容的方法“重新装入”磁盘:

代码语言:javascript
运行
复制
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskDrive");

代码语言:javascript
运行
复制
System.IO.DriveInfo.GetDrives();

要将逻辑驱动器ID映射到物理磁盘ID,当您试图锁定每个单独的逻辑驱动器时,请使用以下代码将逻辑驱动器标签链接到物理磁盘标签:

代码语言:javascript
运行
复制
    List<string> driveLetters = new List<string>();
    string deviceId = @"\\.\PHYSICALDRIVE1";
    string queryString = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceId + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
    ManagementObjectSearcher diskSearcher = new ManagementObjectSearcher("root\\CIMV2", queryString);
    ManagementObjectCollection diskMoc = diskSearcher.Get();
    foreach (ManagementObject diskMo in diskMoc)
    {
        queryString = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + diskMo["DeviceID"] + "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
        ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher("root\\CIMV2", queryString);

        ManagementObjectCollection driveMoc = driveSearcher.Get();
        foreach (ManagementObject driveMo in driveMoc)
        {
            driveLetters.Add("\\\\.\\" + driveMo["DeviceID"].ToString());
        }
    }

例如,如果物理磁盘标签是\\.\PHYSICALDRIVE1,并且它包含一个带有驱动器字母"E“的逻辑驱动器,那么上面的代码将把\\.\E:映射到\\.\PHYSICALDRIVE1

根据ken2k的建议,也可以使用IOCTL_DISK_GET_DRIVE_LAYOUT功能完成此映射。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12081343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档