如何显式锁定已挂载的文件系统?

时间:2015-03-20 15:47:09

标签: windows winapi windows-7 disk

如何在Windows 7中写入物理驱动器?

我正在尝试写入Windows 7中的物理磁盘(例如\\.\PhysicalDrive0)。

此问题已被问及 to death ,但从未得到过回答。它曾经在Windows XP中运行,但微软故意在Windows Vista中破解。微软提供了关于如何做到这一点的提示,但是没有人能够解决这个问题。

它曾经工作

在过去,允许写入物理磁盘(只要您是管理员)。执行此操作的方法甚至记录在知识库文章中:

  

INFO: Direct Drive Access Under Win32

     

要在基于Win32的应用程序中打开直接磁盘访问(原始I / O)的物理硬盘驱动器,请使用表单的设备名称

\\.\PhysicalDriveN
     

其中N为0,1,2等,表示系统中的每个物理驱动器。

     

您可以使用具有这些设备名称的CreateFile()应用程序编程接口(API)打开物理或逻辑驱动器,前提是您具有对驱动器的适当访问权限(即,您必须是管理员)。必须同时使用CreateFile()FILE_SHARE_READ和FILE_SHARE_WRITE标志才能访问驱动器。

在增加安全限制的情况下,Windows Vista中的所有内容都发生了变化。

您如何写入物理磁盘?

很多人和许多答案,在许多stackoverflow问题上都很混乱:

  • 写入物理磁盘(例如\\.\PhysicalDrive0)和
  • 写入逻辑卷(例如\\.\C$

Microsoft注意到restrictions placed on both kinds of operations

  

阻止对卷和磁盘的直接写入操作

     

如果符合以下条件,DASD(直接访问存储设备)卷句柄上的写操作将成功

     
      
  • 未安装文件系统,或
  •   
  • 正在写入的扇区是引导扇区。
  •   
  • 写入的扇区驻留在文件系统空间之外。
  •   
  • 通过请求独占写访问权来隐式锁定文件系统。
  •   
  • 通过发送锁定/卸载请求明确锁定了文件系统。
  •   
  • 写入请求已由内核模式驱动程序标记,指示应绕过此检查。该标志称为SL_FORCE_DIRECT_WRITE,它位于IrpSp-> flags字段中。文件系统和存储驱动程序都会检查此标志。
  •   

就我而言,我要求写一个 Physical ,而不是一个 Logical 。 Microsoft注意到写入物理磁盘句柄的新限制:

  

如果出现以下情况,磁盘句柄上的写操作将成功:

     
      
  • 写入的扇区不属于文件系统。
  •   
  • 写入的扇区属于明确锁定的已装入文件系统。
  •   
  • 写入的扇区属于未装入的文件系统或卷没有文件系统。
  •   
  • 我写的部门属于文件系统 - >失败
  • 我写的部门属于已安装,未锁定的文件系统 - >失败
  • 我写的部门属于已安装的文件系统,并且位于具有文件系统的逻辑卷内。

关于如何使其发挥作用的提示围绕着:

  • 卸载文件系统
  • 锁定文件系统

但问题是如何卸载文件系统?你如何锁定文件系统?

你现在在做什么?

我能够读取磁盘的所有物理扇区;那没问题。问题是当我想到磁盘的物理扇区时。

我当前的代码是伪代码:

void ZeroSector(Int64 PhysicalSectorNumber)
{
    String diskName := '\\.\PhysicalDrive0'; 

    DWORD desiredAccess := GENERIC_READ or GENERIC_WRITE;

    //INFO: Direct Drive Access Under Win32
    //https://support.microsoft.com/en-us/kb/100027
    //says you nedd both
    DWORD shareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;

    //Open the physical disk
    hDisk := CreateFile(diskName, desiredAccess, shareMode,
            nil, OPEN_EXISTING, 0, 0);
    if hDisk = INVALID_HANDLE_VALUE
        RaiseLastWin32Error();
    try
    {
        Int32 bytesPerPhysicalSector := 4096; //Determined elsewhere using IOCTL_STORAGE_QUERY_PROPERTY+STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR

        //Setup buffer for what we will be writing
        Byte[] buffer = new Byte[bytesPerPhysicalSector];

        //Calculate the byte offset of where the sector is
        Int64 byteOffset = PhysicalSectorNumber * bytesPerPhysicalSector;

        //Seek to that byte offset
        SetFilePointer(hDisk, byteOffset.Lo, byteOffset.Hi, FILE_BEGIN); 

        //Write the buffer
        DWORD numberOfBytesWritten;
        if (!WriteFile(hDisk, buffer, bytesPerPhysicalSector, out numberOfBytesWritten, nil))
            RaiseLastWin32Error();
    }
    finally
    {
        CloseHandle(hDisk);
    }
}

令人惊讶的:

  • 可以打开GENERIC_READ + GENERIC_WRITE访问
  • 的物理磁盘
  • 直到实际的WriteFile失败后才会失败:

    ERROR_ACCESS_DENIED

如何做微软所说的

微软说我的写作会失败,他们是对的。他们说我需要明确锁定文件系统:

  

如果出现以下情况,磁盘句柄上的写操作将成功:

     
      
  • 写入的扇区属于明确锁定的已装入文件系统。
  •   

除非我不知道如何做到这一点。

我知道我可能必须使用DeviceIoControl和其中一个IOCTLS"锁定" 一个卷。但这带来了三个挑战:

  • 确定所选物理磁盘上的哪些卷
  • 确定使用哪个IOCTL
  • 弄清楚如何解锁锁定的卷

忽略这些问题,我盲目地尝试了LockFile API。在致电WriteFile之前:

//Try to lock the physical sector we will be writing
if (!LockFile(DiskHandle, byteOffset.Lo, byteOffset.Hi, bytesPerPhysicalSector, 0)
   RaiseLastWin32Error();

失败了:

ERROR_INVALID_FUNCTION (1)

1 个答案:

答案 0 :(得分:5)

检查FSCTL_LOCK_VOLUME,FSCTL_DISMOUNT_VOLUME控制代码。我相信您必须枚举磁盘上的所有卷,然后卸载并锁定它们。锁定成功后,磁盘就是您的全部。

但您可能无法在系统驱动器上执行此操作。我还猜测会有关于包含页面文件的卷的警告。

完成此操作后,我可以写入相应的\\。\ PhysicalDrive3。我以前不能:

HANDLE hVol = CreateFileA(
    "\\\\.\\X:", 
    FILE_READ_DATA | FILE_WRITE_DATA,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD unused;
BOOL b = DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
if (!b) {
    printf("%u", GetLastError());
    abort();
}

...
HANDLE h = CreateFileA(
    argv[1], // that's my \\physicaldrive3
    FILE_READ_DATA | FILE_WRITE_DATA, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL,
    OPEN_EXISTING,
    0,
    NULL);
...
    bResult = WriteFile(h, buf, cb, &dwWritten, &foo);
    if (!bResult) {
        // used to fail without messing with vol handle
        printf("Failed writing data. Error = %d.\n", GetLastError()); 
        return 0;
    }