Winapi 如何获取Windows磁盘的序列号?

Winapi 如何获取Windows磁盘的序列号?,winapi,windows-7,ioctl,Winapi,Windows 7,Ioctl,我正在尝试使用IOCTL\u存储\u获取\u媒体\u序列号获取磁盘的序列号: HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THRO

我正在尝试使用IOCTL\u存储\u获取\u媒体\u序列号获取磁盘的序列号:

HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
  struct {
    USHORT Reserved;
    USHORT SerialNumberLength;
    UCHAR SerialNumber[252];
  } dsn;
  DWORD nr;   
  memset(&dsn, '\0', sizeof dsn);
  if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
                       NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
    printf("Serial number: %s\n", dsn.SerialNumber);
  } else {
    printf("No serial number, error %d.\n", (int)GetLastError());
  }
}
但是,GetLastError返回错误\无效\函数

磁盘确实存在,并且具有序列号,请参阅此注册表项:

如何在不使用注册表的情况下从C代码中检索序列号?

我们可以使用with表示调用方正在查询设备描述符

并使用存储设备描述符的SerialNumberOffset成员

指定从结构开头到指定位置的字节偏移量 包含设备序列号的以NULL结尾的ASCII字符串。 如果设备没有序列号,则此成员为零

代码可以如下所示:

ULONG GetSerial(HANDLE hFile)
{
    static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery }; 

    union {
        PVOID buf;
        PSTR psz;
        PSTORAGE_DEVICE_DESCRIPTOR psdd;
    };

    ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;

    ULONG dwError;

    do 
    {
        dwError = ERROR_NO_SYSTEM_RESOURCES;

        if (buf = LocalAlloc(0, size))
        {
            ULONG BytesReturned;

            if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
            {
                if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
                {
                    if (psdd->Size > size)
                    {
                        size = psdd->Size;
                        dwError = ERROR_MORE_DATA;
                    }
                    else
                    {
                        if (psdd->SerialNumberOffset)
                        {
                            DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                            dwError = NOERROR;
                        }
                        else
                        {
                            dwError = ERROR_NO_DATA;
                        }
                    }
                }
                else
                {
                    dwError = ERROR_GEN_FAILURE;
                }
            }
            else
            {
                dwError = GetLastError();
            }

            LocalFree(buf);
        }
    } while (dwError == ERROR_MORE_DATA);

    return dwError;
}
此外,对于开放式设备,我们可以使用CreateFileW L\\\.\\PhysicalDrive0,0,0,0,打开现有的0,0;-就地dwDesiredAccess可以使用0,因为IOCTL\u存储\u查询\u属性定义为 CTL_代码IOCTL_存储_基,0x0500,方法_缓冲,FILE_ANY_ACCESS-因此FILE_ANY_ACCESS-接受任何文件访问和FILE_FLAG_WRITE_to | FILE_FLAG_NO_缓冲仅对使用缓存的文件系统设备有意义-对于磁盘设备-这与我们可以使用的无关

表示调用者正在查询设备描述符

并使用存储设备描述符的SerialNumberOffset成员

指定从结构开头到指定位置的字节偏移量 包含设备序列号的以NULL结尾的ASCII字符串。 如果设备没有序列号,则此成员为零

代码可以如下所示:

ULONG GetSerial(HANDLE hFile)
{
    static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery }; 

    union {
        PVOID buf;
        PSTR psz;
        PSTORAGE_DEVICE_DESCRIPTOR psdd;
    };

    ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;

    ULONG dwError;

    do 
    {
        dwError = ERROR_NO_SYSTEM_RESOURCES;

        if (buf = LocalAlloc(0, size))
        {
            ULONG BytesReturned;

            if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
            {
                if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
                {
                    if (psdd->Size > size)
                    {
                        size = psdd->Size;
                        dwError = ERROR_MORE_DATA;
                    }
                    else
                    {
                        if (psdd->SerialNumberOffset)
                        {
                            DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                            dwError = NOERROR;
                        }
                        else
                        {
                            dwError = ERROR_NO_DATA;
                        }
                    }
                }
                else
                {
                    dwError = ERROR_GEN_FAILURE;
                }
            }
            else
            {
                dwError = GetLastError();
            }

            LocalFree(buf);
        }
    } while (dwError == ERROR_MORE_DATA);

    return dwError;
}
此外,对于开放式设备,我们可以使用CreateFileW L\\\.\\PhysicalDrive0,0,0,0,打开现有的0,0;-就地dwDesiredAccess可以使用0,因为IOCTL\u存储\u查询\u属性定义为
CTL_代码IOCTL_存储_基,0x0500,方法_缓冲,文件任意访问-所以文件任意访问-接受任意文件访问和文件标志写入-通过文件标志无缓冲仅对使用缓存的文件系统设备有意义-对于磁盘设备-这是不相关的

我尝试了不同的方法,并发现发送IOCTL存储查询属性无法按预期工作用于用户和内核模式代码中的不同USB设备。对于某些USB大容量存储器,它不返回序列号。我假设有两种正确的方法:

IOCTL\u存储\u获取\u媒体\u序列号\u一些AV产品会这样做吗? 创建并发送城市控制描述符请求 更新1。 我看到在一个文件系统的迷你过滤器驱动程序中使用IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,如下所示:

FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
        Irp = IoBuildDeviceIoControlRequest(
            IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
            Device,
...
        Irp = IoBuildDeviceIoControlRequest(
            IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
            Device,....
不幸的是,我还没有在KernelMode代码中对此进行测试,但试图让它在用户模式代码中工作,结果表明这个IOCTL主要是 不同的设备不支持,可能此IOCTL是为将来保留的,作为获取序列号的标准方式,并将 以后的USB标准要求什么

此外,wmic diskdrive get name,serialnumber在我的USB大容量存储案例中返回的序列号不正确=E与使用IOCTL\u Storage\u QUERY\u属性的结果相同。 因此,获取USB大容量存储器序列号的正确方法是在KernelMode代码中创建一个USB请求块,并在UserMode代码中对集线器驱动程序使用DeviceIoControl

USBVIEW UserMode代码通过将IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX IOCTL发送到集线器驱动程序来获取序列号,该集线器驱动程序 返回包含USB设备描述符的USB节点连接信息。USB_设备_描述符的iSerialNumber成员稍后将在中使用 IOCTL_USB_从节点获取描述符_连接IOCTL请求到集线器驱动程序,集线器驱动程序最终获取序列号

我看到的另一种方法可能是使用一些第三方库(如libusb)来简化所有这些操作

更新2。 我看了一下USBSTOR反汇编代码。USBSTOR\U设备控制例程具有以下IOCTL\U存储\U GET\U媒体\U序列号代码

    ++v3->CurrentLocation;
    ++v3->Tail.Overlay.CurrentStackLocation;
    v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
因此,它按照预期将IRP沿着堆栈传递给usbhub驱动程序。因此,这一功能有望在未来实现
什么时候见usbhub的司机?这对我来说太好了…

我尝试了不同的方法,发现在用户和内核模式代码中,发送IOCTL\u STORAGE\u QUERY\u属性对于不同的USB设备都无法正常工作。对于某些USB大容量存储器,它不返回序列号。我假设有两种正确的方法:

IOCTL\u存储\u获取\u媒体\u序列号\u一些AV产品会这样做吗? 创建并发送城市控制描述符请求 更新1。 我看到在一个文件系统的迷你过滤器驱动程序中使用IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,如下所示:

FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
        Irp = IoBuildDeviceIoControlRequest(
            IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
            Device,
...
        Irp = IoBuildDeviceIoControlRequest(
            IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
            Device,....
不幸的是,我还没有在KernelMode代码中对此进行测试,但试图让它在用户模式代码中工作,结果表明这个IOCTL主要是 不受不同设备的支持,可能此IOCTL是为将来保留的标准 获取序列号的方法将是 以后的USB标准要求什么

此外,wmic diskdrive get name,serialnumber在我的USB大容量存储案例中返回的序列号不正确=E与使用IOCTL\u Storage\u QUERY\u属性的结果相同。 因此,获取USB大容量存储器序列号的正确方法是在KernelMode代码中创建一个USB请求块,并在UserMode代码中对集线器驱动程序使用DeviceIoControl

USBVIEW UserMode代码通过将IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX IOCTL发送到集线器驱动程序来获取序列号,该集线器驱动程序 返回包含USB设备描述符的USB节点连接信息。USB_设备_描述符的iSerialNumber成员稍后将在中使用 IOCTL_USB_从节点获取描述符_连接IOCTL请求到集线器驱动程序,集线器驱动程序最终获取序列号

我看到的另一种方法可能是使用一些第三方库(如libusb)来简化所有这些操作

更新2。 我看了一下USBSTOR反汇编代码。USBSTOR\U设备控制例程具有以下IOCTL\U存储\U GET\U媒体\U序列号代码

    ++v3->CurrentLocation;
    ++v3->Tail.Overlay.CurrentStackLocation;
    v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
因此,它按照预期将IRP沿着堆栈传递给usbhub驱动程序。因此,这一功能有望在未来实现
什么时候见usbhub的司机?这对我来说太好了…

如果USB设备具有CSM-1内容安全接口,USB客户端驱动程序可以使用此请求查询其序列号。ERROR\u INVALID\u函数是从STATUS\u INVALID\u DEVICE\u REQUEST转换而来的-表示设备根本不支持此功能ioctl@RbMm:谢谢你的洞察力。没有USB设备连接到这台计算机,所以我仍然不明白为什么会出现此错误。但是,您向其发送IOCTL\u STORAGE\u GET\u MEDIA\u SERIAL\u编号的设备无法处理此请求。它返回状态\u无效\u设备\u请求错误。win32层将其转换为错误\无效\函数,并从DeviceIoControl返回false。如果设备上存在序列号,但并非所有磁盘设备都有序列号-可以通过IOCTL_STORAGE_QUERY_Property获取。如果USB设备具有CSM-1内容安全接口,USB客户端驱动程序可以使用此请求查询其序列号。ERROR\u INVALID\u函数是从STATUS\u INVALID\u DEVICE\u REQUEST转换而来的-表示设备根本不支持此功能ioctl@RbMm:谢谢你的洞察力。没有USB设备连接到这台计算机,所以我仍然不明白为什么会出现此错误。但是,您向其发送IOCTL\u STORAGE\u GET\u MEDIA\u SERIAL\u编号的设备无法处理此请求。它返回状态\u无效\u设备\u请求错误。win32层将其转换为错误\无效\函数,并从DeviceIoControl返回false。如果设备上存在串行数据,但并非所有磁盘设备都有串行数据,则可以通过IOCTL_STORAGE_QUERY_PROPERTYYes获取串行数据,此代码带有IOCTL_STORAGE_QUERY_属性和SerialNumberOffset,只需稍作修改即可工作。是的,此代码带有IOCTL_STORAGE_QUERY_属性和SerialNumberOffset,只需稍作修改即可工作。如问题所示,IOCTL\u存储\u获取\u媒体\u序列号对我不起作用。@谢谢,你说得对。我重新检查并更新了答案。如问题所示,IOCTL\u存储\u获取\u媒体\u序列号对我不起作用。@pts谢谢,你说得对。我重新检查并更新了我的答案。