Bra*_*out 8 c# file-io deviceiocontrol
我编写了下面的代码,将0xFF写入USB存储设备上的所有字节.由于某种原因,WriteFile()调用在扇区242开始出错.我在两个独立的USB存储设备上完成了这个操作,然后在十六进制编辑器中检查设备.扇区242似乎是FAT16格式化设备上文件分配表的开始,以及NTFS设备上引导区域的开始.我确信它在这些确切的位置错误并不是巧合,但是我不知道如何改变这种行为.我在WriteFile失败时收到的HRESULT是-2147024891,即E_ACCESSDENIED.有谁知道可能导致问题的原因是什么?
注意:如果您要在本地系统上运行此代码,请非常小心,因为我已经硬编码了USB设备的物理设备ID.请务必使用您尝试写入的设备更新deviceId变量.你不想破坏你的硬盘.
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());
}
}
Run Code Online (Sandbox Code Playgroud)
编辑/ UPDATE
在使用第三方工具对USB设备进行低级擦除后,我能够成功地使用它.驱动器完全归零后,我能够成功写入设备.一旦它识别出有效的fat或ntfs文件系统及其使用情况,就好像windows正在锁定设备一样
const uint FSCTL_LOCK_VOLUME = 0x00090018;
const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
Run Code Online (Sandbox Code Playgroud)
与DeviceIoControl配对似乎没有覆盖设备上的锁定窗口.
有没有人知道如何在具有有效文件系统的驱动器上使用DeviceIoControl成功锁定Windows中的可移动USB设备?
我已经使用了几个第三方工具来完成我正在尝试的工作并且它们成功运行.我知道这是可能的,但我读过的所有MSDN文档都没有帮助解决问题.
编辑/更新2
这取自http://msdn.microsoft.com/en-us/library/ff551353.aspx
"应用程序需要锁定卷,卸载卷或两者,然后才能发出DASD I/O.这是Windows Vista的新功能,用于解决潜在的恶意技术.
文件系统将阻止对磁盘保留部分的所有写操作.在这种情况下,那些保留部分包括MBR和两个FAT区域.要阻止这些区域,您需要通过发送FSCTL_LOCK_VOLUME来锁定卷.您必须在执行实际写入操作的同一卷句柄上发出此结构.如果存在打开的文件句柄,则此请求可能会失败.在这种情况下,应用程序可以通过发出FSCTL_DISMOUNT_VOLUME强制卸载文件系统.但是,在文件句柄关闭之前,实际上不会卸载卷.在此之前,应用程序可以使用当前打开的相同文件句柄继续发出DASD I/O.
在卷文件系统已知的卷空间之外有一个扩展区域,其中将阻止写入操作.要允许对此区域执行写入操作,必须在卷句柄上发出FSCTL_ALLOW_EXTENDED_DASD_IO.
您可以使用Win32 API例程DeviceIoControl来发出所有以前的FSCTS."
我相信这正是我们在上面的代码中实现的,但它似乎没有正常工作.我们正在处理并锁定和卸载设备,因此我们应该能够正确地写入受保护区域吗?
编辑/更新3
好的,这是打开磁盘和列的当前顺序.锁定,卸载等方法只是我们认为错误的顺序.
SafeFileHandle volumeHandle = CreateFile("\\.\E:",...);
LockVolume(volumeHandle);
DismountVolume(volumeHandle);
SafeFileHandle diskHandle = CreateFile("\\.\PHYSICALDRIVE1"...);
WriteStuff(diskHandle);
//Fails...
UnlockVolume(volumeHandle);
CloseVolume(volumeHandle);
CloseDisk(diskHandle);
Run Code Online (Sandbox Code Playgroud)
我仍然得到相同的结果,它只在磁盘被删除时才有效.
磁盘和驱动器之间存在混淆.
如果要完全访问磁盘(在您使用时就是这种情况\\.\PHYSICALDRIVE),则必须锁定所有已装入的卷,这些卷基本上都是物理磁盘的所有分区(即驱动器).
而不是使用FSCTL_LOCK_VOLUME返回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就足够了还要确保您使用管理员权限(提升过程)运行您的应用程序.