2013-04-02 88 views
4

我想在使用C++的USB驱动器上创建一个隐藏的虚拟分区。创建一个虚拟分区

我可以在Windows中使用虚拟磁盘API来做到这一点吗?

+1

@HarryJohnston其实Windows支持他们。 –

+0

@CaptainObvlious:你的答案是关于虚拟磁盘上的分区,这似乎是对OP的实际含义的合理猜测,但AFAIK通常不称为“虚拟分区”。 –

回答

7

是的,虚拟磁盘API支持创建和装入卷而不分配驱动器号的能力。这是通过ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTERAttachVirtualDisk

// This call requires elevated privileges to succeed. 
::AttachVirtualDisk(
    handle, // Handle returned by CreateVirtualDisk or OpenVirtualDisk 
    NULL, 
    ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 
    0, // no provider-specific flags 
    0, // no parameters 
    NULL); 

困难的部分是访问卷没有驱动器号。虚拟磁盘API不提供直接获取虚拟驱动器上卷的列表的方法。这很重要,因为您必须通过UNC路径访问卷,因为驱动器号不可用。要获取UNC路径,您需要枚举所有已安装的卷以查找位于虚拟硬盘驱动器上的卷。这是通过打开每个安装的卷并使用DeviceIoControl来检索与文件句柄关联的设备信息来完成的。

包含的示例执行以下步骤在我们的虚拟驱动器上创建,装入和访问隐藏的卷。

  1. 分别创建或打开虚拟磁盘文件CreateVirtualDiskOpenVirtalDisk
  2. 将虚拟磁盘连接到AttachVirtualDisk您必须对此步骤拥有较高的权限才能成功
  3. 初始化设备。
  4. 在设备上创建卷。
  5. 枚举安装的卷以找到虚拟磁盘上的卷。

[步骤3和4需要从磁盘管理控制面板手动完成。这当然可以通过编程完成,但会为示例添加更多代码。]

#include <iostream> 
#include <fstream> 
#include <string> 
#include <virtdisk.h> 

#pragma comment(lib, "virtdisk.lib") 

// Fix unresolved link error 
static const GUID VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT = { 0xEC984AEC, 0xA0F9, 0x47e9, 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B }; 

#define ARRAY_SIZE(a)        \ 
    ((sizeof(a)/sizeof(*(a)))/     \ 
    static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) 


DWORD CreateDisk(PCWSTR virtualDiskFilePath, HANDLE *handle) 
{ 
    VIRTUAL_STORAGE_TYPE storageType = 
    { 
     VIRTUAL_STORAGE_TYPE_DEVICE_VHD, 
     VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT 
    }; 

    CREATE_VIRTUAL_DISK_PARAMETERS parameters = {}; 
    parameters.Version = CREATE_VIRTUAL_DISK_VERSION_1; 
    parameters.Version1.MaximumSize = 1024 * 1024 * 1024; 
    parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE; 
    parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE; 
    parameters.Version1.SourcePath = NULL; 

    int result = ::CreateVirtualDisk(
     &storageType, 
     virtualDiskFilePath, 
     VIRTUAL_DISK_ACCESS_ALL, 
     NULL, 
     CREATE_VIRTUAL_DISK_FLAG_NONE, 
     0, 
     &parameters, 
     NULL, 
     handle); 

    return result; 
} 


DWORD OpenDisk(PCWSTR virtualDiskFilePath, HANDLE *handle) 
{ 
    VIRTUAL_STORAGE_TYPE storageType = 
    { 
     VIRTUAL_STORAGE_TYPE_DEVICE_VHD, 
     VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT 
    }; 

    OPEN_VIRTUAL_DISK_PARAMETERS parameters = 
    { 
     OPEN_VIRTUAL_DISK_VERSION_1 
    }; 

    parameters.Version1.RWDepth = 1024; 

    return ::OpenVirtualDisk(
     &storageType, 
     virtualDiskFilePath, 
     VIRTUAL_DISK_ACCESS_ALL, 
     OPEN_VIRTUAL_DISK_FLAG_NONE, 
     &parameters, 
     handle); 
} 



int main(int argc, char *argv[]) 
{ 
    LPTSTR virtualDiskFilePath = _T("c:\\source\\drive.vhd"); 
    HANDLE handle; 
    DWORD result; 
    ULONG bytesUsed; 
    bool vhdCreated = false; 

    // Create or open a virtual disk file 
    result = CreateDisk(virtualDiskFilePath, &handle); 
    if(result == ERROR_FILE_EXISTS) 
    { 
     result = OpenDisk(virtualDiskFilePath, &handle); 
     if(result != ERROR_SUCCESS) 
     { 
      std::wcout << "Unable to open virtual disk" << std::endl; 
      return 1; 
     } 
    } 
    else if(result != ERROR_SUCCESS) 
    { 
     std::wcout << "Unable to create virtual disk" << std::endl; 
     return 1; 
    } 
    else 
    { 
     vhdCreated = true; 
    } 


    // Now that the virtual disk is open we need to mount it. 
    // 
    // FROM MSDN: 
    // To attach and detach a virtual disk, you must also have the 
    // SE_MANAGE_VOLUME_NAME privilege present in your token. This privilege 
    // is stripped from an administrator's token when User Account Control is 
    // in use, so you may need to elevate your application to gain access to 
    // the unrestricted token that includes this privilege. 
    result = ::AttachVirtualDisk(
     handle, 
     NULL, 
     ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 
     0, // no provider-specific flags 
     0, // no parameters 
     NULL); 
    if(result != ERROR_SUCCESS) 
    { 
     std::wcout << "Unable to attach virtual disk" << std::endl; 
     return 1; 
    } 

    if(result == ERROR_SUCCESS && vhdCreated == true) 
    { 
     std::wcout 
      << "Virtual disk image created. Go into the Computer Management admin panel" << std::endl 
      << "and add a volume and format it.\n" << std::endl; 
     system("pause"); 
    } 

    // Now we need to grab the device name \\.\PhysicalDrive# 
    TCHAR physicalDriveName[MAX_PATH]; 
    DWORD physicalDriveNameSize = ARRAY_SIZE(physicalDriveName); 

    result = ::GetVirtualDiskPhysicalPath(handle, &physicalDriveNameSize, physicalDriveName); 
    if(result != ERROR_SUCCESS) 
    { 
     std::wcout << "Unable to retrieve virtual disk path" << std::endl; 
     return 1; 
    } 
    const std::wstring deviceName = physicalDriveName; 


    // HACK!!! Wait for windows to complete the mount. 
    Sleep(2500); 


    // In order to get the UNC path of the volumes located on the virtual disk we 
    // need to enumerate all mounted volumes and check which device they are located 
    // on. 
    std::wstring volumeName; 

    TCHAR volumeNameBuffer[MAX_PATH]; 
    HANDLE hVol = ::FindFirstVolume(volumeNameBuffer, ARRAY_SIZE(volumeNameBuffer)); 
    if(hVol == INVALID_HANDLE_VALUE) 
    { 
     std::wcout << "Unable to find first volume" << std::endl; 
     return 1; 
    } 
    do 
    { 
     // Get rid of trailing backslash so we can open the volume 
     size_t len = wcslen(volumeNameBuffer); 
     if (volumeNameBuffer[len-1] == '\\') 
     { 
      volumeNameBuffer[len-1] = 0; 
     } 

     HANDLE volumeHandle = ::CreateFile( 
      volumeNameBuffer, 
      GENERIC_READ, 
      FILE_SHARE_READ | FILE_SHARE_WRITE, 
      NULL, 
      OPEN_EXISTING, 
      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 
      NULL); 
     if(volumeHandle == INVALID_HANDLE_VALUE) 
     { 
      std::wcout << "Unable to open volume " << volumeNameBuffer << std::endl; 
     } 
     else 
     { 
      // We can grab the id of the device and use it to create a 
      // proper device name. 
      STORAGE_DEVICE_NUMBER deviceInfo = {0}; 
      if (::DeviceIoControl(
        volumeHandle, 
        IOCTL_STORAGE_GET_DEVICE_NUMBER, 
        NULL, 
        0, 
        &deviceInfo, 
        sizeof(deviceInfo), 
        &bytesUsed, 
        NULL)) 
      { 
       std::wstring tmpName(
        std::wstring(L"\\\\.\\PhysicalDrive") 
        + std::to_wstring((long long)deviceInfo.DeviceNumber)); 
       if(_wcsicmp(tmpName.c_str(), deviceName.c_str()) == 0) 
       { 
        volumeName = std::wstring(volumeNameBuffer) + L"\\\\"; 
        CloseHandle(volumeHandle); 
        break; 
       } 
      } 

      CloseHandle(volumeHandle); 
     } 
    } 
    while(::FindNextVolume(hVol, volumeNameBuffer, ARRAY_SIZE(volumeNameBuffer)) != FALSE); 

    ::FindVolumeClose(hVol); 

    if(volumeName.size() == 0) 
    { 
     std::wcout << "Unable to locate a volume on this device" << std::endl; 
     return 1; 
    } 

    std::wcout << "Device: " << physicalDriveName << std::endl; 
    std::wcout << "Volume: " << volumeName << std::endl; 


    std::wcout << "\n\nSuccess! Now create the file!" << std::endl; 

    // Now let's create a file for fits and giggles 
    std::ofstream output; 

    output.open(volumeName + L"hello.txt"); 
    if(output.fail()) 
    { 
     std::wcout << "Unable to open output file." << std::endl; 
     return 1; 
    } 
    output.close(); 

    // Volume will be unmounted when the application closes 
    system("pause"); 

    return 0; 
} 
+1

“步骤3和步骤4需要通过磁盘管理控制面板手动完成,当然这可以通过编程方式完成,但会为示例添加更多代码”。 ----请提供更多详细信息,说明如何以编程方式完成这些步骤。 – Arthur