创建虚拟分区

4
我正在尝试使用C++在USB驱动器上创建一个隐藏的虚拟分区。
我能否使用Windows中的虚拟磁盘API来实现这一点?

1
@HarryJohnston 实际上Windows支持它们。 - Captain Obvlious
@CaptainObvlious:你的回答涉及到虚拟磁盘上的分区,这似乎是对原帖意思的合理猜测,但据我所知,它通常不被称为“虚拟分区”。 - Harry Johnston
1个回答

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. 使用CreateVirtualDiskOpenVirtualDisk创建或打开虚拟磁盘文件。
  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;
}

2
步骤3和4需要手动从磁盘管理控制面板完成。当然,这也可以通过编程完成,但会给示例添加更多的代码。请问您需要更多关于如何通过编程完成这些步骤的细节吗? - Arthur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接