当我尝试通过DeviceIoControl获取物理扇区大小时,我收到Access被拒绝

lok*_*oki 0 windows delphi iis winapi deviceiocontrol

从我的网络服务器应用程序,我需要检查应用程序所在的硬盘的物理扇区大小.为此,我使用DeviceIoControlIOCTL_STORAGE_QUERY_PROPERTY查询StorageAccessAlignmentProperty.问题是,当我尝试从网络服务器运行这些命令时,我得到" 访问被拒绝 "错误.

如何从webserver应用程序中检索inetpub所在的硬盘的物理扇区大小?

我从https://msdn.microsoft.com/windows/compatibility/advanced-format-disk-compatibility-update了解到,在Windows 8中,Microsoft引入了一个新的API,可以从一个非特权的应用程序启用Calling.API是一个新的信息类的形式,FileFsSectorSizeInformation具有相关的结构FILE_FS_SECTOR_SIZE_INFORMATION,但我不知道如何使它与Delphi一起工作

这是我的实际代码不起作用(用Delphi编写):

{~~~~~~~~~~~~~~~~~~~~~~~~~}
procedure _CheckSectorSize;

type
  STORAGE_PROPERTY_ID  = (StorageDeviceProperty = 0,
                          StorageAdapterProperty,
                          StorageDeviceIdProperty,
                          StorageDeviceUniqueIdProperty,
                          StorageDeviceWriteCacheProperty,
                          StorageMiniportProperty,
                          StorageAccessAlignmentProperty,
                          StorageDeviceSeekPenaltyProperty,
                          StorageDeviceTrimProperty,
                          StorageDeviceWriteAggregationProperty,
                          StorageDeviceDeviceTelemetryProperty,
                          StorageDeviceLBProvisioningProperty,
                          StorageDevicePowerProperty,
                          StorageDeviceCopyOffloadProperty,
                          StorageDeviceResiliencyProperty,
                          StorageDeviceMediumProductType,
                          StorageAdapterCryptoProperty,
                          StorageDeviceIoCapabilityProperty = 48,
                          StorageAdapterProtocolSpecificProperty,
                          StorageDeviceProtocolSpecificProperty,
                          StorageAdapterTemperatureProperty,
                          StorageDeviceTemperatureProperty,
                          StorageAdapterPhysicalTopologyProperty,
                          StorageDevicePhysicalTopologyProperty,
                          StorageDeviceAttributesProperty);
  STORAGE_QUERY_TYPE  = (PropertyStandardQuery = 0,
                         PropertyExistsQuery = 1,
                         PropertyMaskQuery = 2,
                         PropertyQueryMaxDefined = 3);
  _STORAGE_PROPERTY_QUERY = packed record
    PropertyId: STORAGE_PROPERTY_ID;
    QueryType: STORAGE_QUERY_TYPE;
    AdditionalParameters: array[0..9] of Byte;
 end;
  _STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR = packed record
    Version: DWORD; // Contains the size of this structure, in bytes. The value of this member will change as members are added to the structure.
    Size: DWORD; // Specifies the total size of the data returned, in bytes. This may include data that follows this structure.
    BytesPerCacheLine: DWORD; // The number of bytes in a cache line of the device.
    BytesOffsetForCacheAlignment: DWORD; // The address offset necessary for proper cache access alignment, in bytes.
    BytesPerLogicalSector: DWORD; // The number of bytes in a logical sector of the device.
    BytesPerPhysicalSector: DWORD; // The number of bytes in a physical sector of the device.
    BytesOffsetForSectorAlignment: DWORD; // The logical sector offset within the first physical sector where the first logical sector is placed, in bytes.
 end;

var
  aVolumePath: array[0..MAX_PATH] of AnsiChar;
  aVolumeName: array[0..MAX_PATH] of AnsiChar;
  hFile: THANDLE;
  inbuf: _STORAGE_PROPERTY_QUERY;
  outbuf: _STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
  dwLen: DWORD;
  i: integer;

begin

  // Convert the directory to a Volume Name
  aVolumePath[0] := #$00;
  if not GetVolumePathNameA(pAnsiChar(DFRooter_HayStackDirectory),  // _In_  LPCTSTR lpszFileName,
                            aVolumePath,  // _Out_ LPTSTR  lpszVolumePathName,
                            length(aVolumePath)) then raiseLastOsError; // _In_ DWORD cchBufferLength
  aVolumeName[0] := #$00;
  if not GetVolumeNameForVolumeMountPointA(aVolumePath, // _In_  LPCTSTR lpszVolumeMountPoint,
                                           aVolumeName,  // _Out_ LPTSTR lpszVolumeName,
                                           length(aVolumeName)) then raiseLastOsError; // _In_  DWORD   cchBufferLength

  // Opening a physical device so no trailing '\'. Trailing '\' would open the ROOT DIR instead of the volume
  for i := 1 to High(aVolumeName) do
    if aVolumeName[i] = #0 then begin
      if aVolumeName[i-1] = '\' then aVolumeName[i-1] := #0;
      break;
    end;

  //create the file
  hFile := CreateFileA(PAnsiChar(@aVolumeName[0]), // _In_ LPCTSTR lpFileName,
                       GENERIC_READ, // _In_ DWORD dwDesiredAccess,
                       FILE_SHARE_READ or FILE_SHARE_WRITE, //_In_ DWORD dwShareMode,
                       0, // _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
                       OPEN_EXISTING, // _In_ DWORD dwCreationDisposition,
                       FILE_ATTRIBUTE_NORMAL, // _In_ DWORD dwFlagsAndAttributes,
                       0); // _In_opt_ HANDLE hTemplateFile
  if (hFile = INVALID_HANDLE_VALUE) then raiseLastOsError;
  try

    ZeroMemory(@inbuf, SizeOf(inbuf));
    ZeroMemory(@outbuf, SizeOf(outbuf));
    inbuf.QueryType := PropertyStandardQuery;
    inbuf.PropertyId := StorageAccessAlignmentProperty;
    outbuf.Size := sizeOf(outbuf);
    if not DeviceIoControl(hFile, //  _In_ HANDLE hDevice,
                           IOCTL_STORAGE_QUERY_PROPERTY, // _In_ DWORD dwIoControlCode,
                           @inbuf, // _In_opt_ LPVOID lpInBuffer,
                           sizeof(inbuf), // _In_ DWORD nInBufferSize,
                           @outbuf, // _Out_opt_ LPVOID lpOutBuffer,
                           sizeof(outbuf), // _In_ DWORD nOutBufferSize,
                           dwLen, // _Out_opt_ LPDWORD lpBytesReturned,
                           nil) then raiseLastOsError; // _Inout_opt_ LPOVERLAPPED lpOverlapped

  finally
    CloseHandle(hFile);
  end;

end;
Run Code Online (Sandbox Code Playgroud)

RbM*_*bMm 5

让我们来寻找IOCTL_STORAGE_QUERY_PROPERTY定义:

CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)- FILE_ANY_ACCESS在这里使用 这意味着任何具有任何访问权限的文件句柄可以用于此IOCTL.但是如何打开设备发送此ioctl?你GENERIC_READ在电话中使用CreateFileA(为什么不CreateFileW呢?!).正是在这一点上我猜你有访问被拒绝的错误.也可以使用说IOCTL_DISK_GET_DRIVE_GEOMETRY- 它也可以使用FILE_ANY_ACCESS.所以,如果你有确切的设备名称,你可以使用下一个代码(c/c ++):

HANDLE hFile = CreateFileW(DeviceName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

if (hFile != INVALID_HANDLE_VALUE)
{
    DISK_GEOMETRY dg;
    STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR sad;

    static STORAGE_PROPERTY_QUERY spq = { StorageAccessAlignmentProperty, PropertyStandardQuery }; 
    ULONG BytesReturned;

    if (!DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sad, sizeof(sad), &BytesReturned, 0))
    {
        GetLastError();
    }

    if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &dg, sizeof(dg), &BytesReturned, 0))
    {
        GetLastError();
    }

    CloseHandle(hFile);
}
else
{
    GetLastError();
}
Run Code Online (Sandbox Code Playgroud)

即使从低完整性过程中,此代码也能完美运行 没有任何特权或管理员sid要求.

注意,DeviceName必须是准确的设备名,而不是文件/文件夹名称.

这个卑鄙的名字就像"\\\\?\\c:"是好的,但对于名称,"\\\\?\\c:\\"或者"\\\\?\\c:\\anypath"你已经得到ERROR_INVALID_PARAMETER(或STATUS_INVALID_PARAMETER),如果磁盘是由文件系统挂载的.这是因为IOCTL_STORAGE_QUERY_PROPERTYIOCTL_DISK_GET_DRIVE_GEOMETRY仅由磁盘设备对象处理.但是当磁盘通过文件系统挂载时 - io子系统将请求重定向到文件系统设备对象,而不是通过VPB(除非您通过确切的设备名称和非常低的访问权限打开文件).文件系统设备只返回 STATUS_INVALID_PARAMETER any IOCTL(IRP_MJ_DEVICE_CONTROL),如果这不是卷打开,而是文件或目录.否则它将它传递给磁盘设备对象(不要将此与FSCTL(IRP_MJ_FILE_SYSTEM_CONTROL)混淆- DeviceIoControl内部调用或ZwDeviceIoControlFile(发送ioctl)或ZwFsControlFile(发送fsctl))

另一种选择,获取磁盘扇区信息 - 查询文件系统,当然万一磁盘是由某些文件系统挂载的.我们可以用它(从win8开始)或.再次 - 对于此请求,我们可以打开任何访问权限的文件句柄.我们不需要NtQueryVolumeInformationFile FileFsSectorSizeInformationFileFsSizeInformationGENERIC_READ

HANDLE hFile = CreateFileW(FileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);

if (hFile != INVALID_HANDLE_VALUE)
{
    FILE_FS_SECTOR_SIZE_INFORMATION ffssi;
    FILE_FS_SIZE_INFORMATION ffsi;

    IO_STATUS_BLOCK iosb;

    NtQueryVolumeInformationFile(hFile, &iosb, &ffsi, sizeof(ffsi), FileFsSizeInformation);
    NtQueryVolumeInformationFile(hFile, &iosb, &ffssi, sizeof(ffssi), FileFsSectorSizeInformation);
    CloseHandle(hFile);

}
Run Code Online (Sandbox Code Playgroud)

注意-在这里我们可以使用任何文件路径,准确设备路径太(有一个重要的注意) -所以"\\\\?\\c:""\\\\?\\c:\\""\\\\?\\c:\\windows\\notepad.exe"-都将是确定这里.但是如果确切的设备名称("\\\\?\\c:")你需要使用说FILE_EXECUTE访问设备的电话CreateFileW,否则文件系统设备将打开磁盘设备,FO_DIRECT_DEVICE_OPEN并将设置在文件对象中.结果请求将被发送到磁盘设备对象,它没有处理它,你得到了STATUS_INVALID_DEVICE_REQUEST


芬妮说MSDN

使用此(IOCTL_STORAGE_QUERY_PROPERTY)IOCTL获取物理扇区大小确实有一些限制.它:

  • 需要提升权限; 如果您的应用程序没有运行特权,您可能需要编写
    如上所述的Windows服务应用程序

这是错误或有意识的谎言 - 再次不需要任何提升的特权.此代码甚至可以从来宾帐户工作,完整性水平较低.我们当然可以在调用中使用和STANDARD_RIGHTS_READ(注意 - 这不是GENERIC_READ- GENERIC_READ在这里使用是严重错误)CreateFileW,但可以使用和0(在这种情况下CreateFile实际使用FILE_READ_ATTRIBUTES | SYNCHRONIZE访问请求).所以文件是坏的和错误的

  • @RbMm,对于0期望访问`CreateFileW`实际上使用`FILE_READ_ATTRIBUTES | SYNCHRONIZE`,即使使用`FILE_FLAG_OVERLAPPED`.但这不是问题,因为每个人都被授予对磁盘设备的基本访问权限,包括同步,执行和读取元数据(属性,安全性)的权限. (2认同)