创建虚拟分区

时间:2013-04-02 12:56:07

标签: c++ windows

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

我可以在Windows中使用虚拟磁盘API吗?

1 个答案:

答案 0 :(得分:7)

是的,虚拟磁盘API支持创建和装入卷而无需为其分配驱动器号。这是通过将ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER传递给AttachVirtualDisk

来完成的
// 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. 枚举已装入的卷以查找虚拟磁盘上的卷。
  6. [步骤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;
    }