10

我正在编写一个库来提取有关 Windows 系统(XP 或更高版本)上的物理磁盘、分区和卷的信息。

我正在尝试获取卷的容量。以下是我所知道的方法以及每种方法失败的原因:

奇怪的是,簇的数量 fromFSCTL_GET_VOLUME_BITMAP和 WMI 的CIM_LogicalDisk.Size属性一致,并且都比 from 的值小 4096 字节IOCTL_DISK_GET_LENGTH_INFO

获得卷容量的正确方法是什么?由于所有其他查询都无需管理员访问权限即可工作,因此我也在为此寻找最低权限的解决方案。

4

1 回答 1

3

你到底想得到什么?

  • 1) 物理磁盘容量

    或者

  • 2) 磁盘分区的容量

    或者

  • 3) 分区上文件系统的容量

物理磁盘有 PDO,因为它 disk.sys 创建并附加 FDO(\Device\Harddisk<I>\DR0-名称或\Device\Harddisk<I>\Partition0-符号链接,其中我的磁盘编号在 0、1、2 ..)

为物理磁盘上的每个分区 disk.sys 创建 PDO ( \Device\Harddisk<I>\Partition<J>- (J in {1,2,3..}) - 符号链接到 some \Device\HarddiskVolume<X>)

1)有几种方法可以获得物理磁盘容量:

  • 一个)

打开任何\Device\Harddisk<I>\Partition<J>设备({0,1,..} 中的 J - 所以磁盘 FDO 或任何分区 PDO)并使用和/或(FILE_READ_ACCESS | FILE_WRITE_ACCESS)发送IOCTL_SCSI_PASS_THROUGH_DIRECT - 我们得到或构造。SCSIOP_READ_CAPACITYSCSIOP_READ_CAPACITY16SCSIOP_READ_CAPACITYSCSIOP_READ_CAPACITY16

READ_CAPACITY_DATA_EX rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
    sizeof(sptd), 0, 0, 0, 0, CDB12GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
    sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY16}
};

if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
    &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
    DbgPrint("---- SCSIOP_READ_CAPACITY16 ----\n");
    rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart = _byteswap_uint64(rcd.LogicalBlockAddress.QuadPart) + 1;
    DbgPrint("%I64x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart *= rcd.BytesPerBlock;
    DbgPrint("%I64x %I64u\n", rcd.LogicalBlockAddress.QuadPart, rcd.LogicalBlockAddress.QuadPart);
}

或者

    READ_CAPACITY_DATA rcd;
    SCSI_PASS_THROUGH_DIRECT sptd = {
        sizeof(sptd), 0, 0, 0, 0, CDB10GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
        sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY}
    };

    if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
        &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
    {
        DbgPrint("---- SCSIOP_READ_CAPACITY ----\n");
        rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
        rcd.LogicalBlockAddress = _byteswap_ulong(rcd.LogicalBlockAddress) + 1;
        DbgPrint("%x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
        ULARGE_INTEGER u = {rcd.LogicalBlockAddress};
        u.QuadPart *= rcd.BytesPerBlock;
        DbgPrint("%I64x %I64u\n", u.QuadPart, u.QuadPart);
    }
  • b)

打开任何\Device\Harddisk<I>\Partition<J>设备FILE_READ_ACCESS并发送IOCTL_STORAGE_READ_CAPACITY - 必须与 a) 结果相同 - 此请求句柄ClassReadDriveCapacityclasspnp.sys中,内部将 SCSI 请求 ( SCSIOP_READ_CAPACITY) 发送到磁盘 PDO。这种方式不适用于XP。

STORAGE_READ_CAPACITY sc;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_STORAGE_READ_CAPACITY, 0, 0, &sc, sizeof(sc)))
{
    DbgPrint("---- IOCTL_STORAGE_READ_CAPACITY ----\n");
    DbgPrint("%I64x %I64x %x \n", sc.DiskLength.QuadPart, sc.NumberOfBlocks.QuadPart, sc.BlockLength);
    sc.NumberOfBlocks.QuadPart *= sc.BlockLength;
    DbgPrint("%I64x %I64u\n", sc.NumberOfBlocks.QuadPart, sc.NumberOfBlocks.QuadPart);
}
  • C)

打开任何\Device\Harddisk<I>\Partition<J>访问权限并发送IOCTL_DISK_GET_DRIVE_GEOMETRY_EX并使用DISK_GEOMETRY_EX.DiskSize. 这是认为最好的方法。不需要任何权利和在 XP 上工作

DISK_GEOMETRY_EX GeometryEx;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &GeometryEx, sizeof(GeometryEx)))
{
    DbgPrint("---- IOCTL_DISK_GET_DRIVE_GEOMETRY ----\n");

    ULONG BytesPerCylinder = GeometryEx.Geometry.TracksPerCylinder * GeometryEx.Geometry.SectorsPerTrack * GeometryEx.Geometry.BytesPerSector;

    DbgPrint("%I64x == %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart, GeometryEx.DiskSize.QuadPart / BytesPerCylinder);
    DbgPrint("%I64x <= %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart * BytesPerCylinder, GeometryEx.DiskSize.QuadPart);
}
  • d)

打开\Device\Harddisk<I>\Partition0\Device\Harddisk<I>\Dr0使用FILE_READ_ACCESS并使用IOCTL_DISK_GET_LENGTH_INFO

  • 2)

获取磁盘上分区的容量 - 打开\Device\Harddisk<I>\Partition<J>(其中 J 在 {1,2..} 中)或者如果 X 字母分配给分区 -\GLOBAL??\X:并使用IOCTL_DISK_GET_LENGTH_INFO。再次需要FILE_READ_ACCESS

GET_LENGTH_INFORMATION gli;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &gli, sizeof(gli)))
{
    DbgPrint("---- IOCTL_DISK_GET_LENGTH_INFO ----\n");
    DbgPrint("%I64x %I64u\n", gli.Length.QuadPart, gli.Length.QuadPart);
}
  • 3)

获取分区上文件系统的容量 - 打开任何文件(\GLOBAL??\X:\例如)并使用NtQueryVolumeInformationFile ( FileFsSizeInformation )

FILE_FS_SIZE_INFORMATION fsi;
if (0 <= NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT))
{
    if (0 <= NtQueryVolumeInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileFsSizeInformation))
    {
        DbgPrint("%I64x %x %x\n", fsi.TotalAllocationUnits.QuadPart, fsi.SectorsPerAllocationUnit, fsi.BytesPerSector);
        fsi.TotalAllocationUnits.QuadPart *= fsi.SectorsPerAllocationUnit * fsi.BytesPerSector;
        DbgPrint("%I64x %I64u\n", fsi.TotalAllocationUnits.QuadPart, fsi.TotalAllocationUnits.QuadPart);
    }
    NtClose(hFile);
}

或使用GetDiskFreeSpaceEx - 在内部它也调用NtQueryVolumeInformationFile( FileFsSizeInformation)但使用标志FILE_DIRECTORY_FILE,因此作为输入参数,您只能使用目录

于 2016-08-09T16:23:27.007 回答