如何获取Windows盘的序列号?
How to get a serial number of a Windows disk?
我正在尝试获取磁盘的序列号,使用 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
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, '[=11=]', 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()
returns ERROR_INVALID_FUNCTION.
该磁盘确实存在,并且有一个序列号,请参阅此注册表项:
如何在不使用注册表的情况下从 C 代码中检索序列号?
我们可以使用IOCTL_STORAGE_QUERY_PROPERTY
with StorageDeviceProperty
(Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR
)
并使用 STORAGE_DEVICE_DESCRIPTOR
的 SerialNumberOffset
成员
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
代码可以如下所示:
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, OPEN_EXISTING, 0, 0);
- 代替 dwDesiredAccess 我们可以使用 0 因为 IOCTL_STORAGE_QUERY_PROPERTY
定义为
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
- 所以 FILE_ANY_ACCESS
- 接受任何文件访问并且 FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING
只对文件系统设备有意义(更一般的使用缓存) - 对于磁盘设备 - 这是无关紧要的
我尝试了不同的方法,发现发送 IOCTL_STORAGE_QUERY_PROPERTY 对于不同的 USB 设备在用户模式和内核模式代码中都无法正常工作。对于某些 USB 大容量存储器,它没有 return 序列号。我假设有两种正确的方法可以做到这一点:
- IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER(有几个 AV 产品这样做?)
- 创建并发送 URB_CONTROL_DESCRIPTOR_REQUEST
更新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”returns 在我的例子中,USB 大容量存储不正确的序列号 =“E”与我们使用的结果相同 IOCTL_STORAGE_QUERY_PROPERTY。
因此,获取 USB 大容量存储序列号的正确方法是在 KernelMode 代码中创建一个 USB 请求块,并在 UserMode 代码中使用 DeviceIoControl 到集线器驱动程序。
USBVIEW(UserMode代码)通过向HUB驱动发送IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EXioctl获取序列号
returns USB_NODE_CONNECTION_INFORMATION_EX 包含 USB_DEVICE_DESCRIPTOR。 USB_DEVICE_DESCRIPTOR 的 iSerialNumber 成员稍后在
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl 请求最终获得序列号的集线器驱动程序。
我看到的另一种方法可能是使用一些第 3 方库(如 libusb)来简化所有这些操作...
更新二。
我看了一下USBSTOR反汇编代码。 USBSTOR_DeviceControl 例程具有以下用于 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
的代码
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
因此,它按预期将 IRP 向下传递到 usbhub 驱动程序。所以也许这个functionlaty有望在
usbhub 驱动程序有时?这对我来说太棒了...
我正在尝试获取磁盘的序列号,使用 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
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, '[=11=]', 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()
returns ERROR_INVALID_FUNCTION.
该磁盘确实存在,并且有一个序列号,请参阅此注册表项:
如何在不使用注册表的情况下从 C 代码中检索序列号?
我们可以使用IOCTL_STORAGE_QUERY_PROPERTY
with StorageDeviceProperty
(Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR
)
并使用 STORAGE_DEVICE_DESCRIPTOR
SerialNumberOffset
成员
Specifies the byte offset from the beginning of the structure to a NULL-terminated ASCII string that contains the device's serial number. If the device has no serial number, this member is zero.
代码可以如下所示:
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, OPEN_EXISTING, 0, 0);
- 代替 dwDesiredAccess 我们可以使用 0 因为 IOCTL_STORAGE_QUERY_PROPERTY
定义为
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
- 所以 FILE_ANY_ACCESS
- 接受任何文件访问并且 FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING
只对文件系统设备有意义(更一般的使用缓存) - 对于磁盘设备 - 这是无关紧要的
我尝试了不同的方法,发现发送 IOCTL_STORAGE_QUERY_PROPERTY 对于不同的 USB 设备在用户模式和内核模式代码中都无法正常工作。对于某些 USB 大容量存储器,它没有 return 序列号。我假设有两种正确的方法可以做到这一点:
- IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER(有几个 AV 产品这样做?)
- 创建并发送 URB_CONTROL_DESCRIPTOR_REQUEST
更新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”returns 在我的例子中,USB 大容量存储不正确的序列号 =“E”与我们使用的结果相同 IOCTL_STORAGE_QUERY_PROPERTY。 因此,获取 USB 大容量存储序列号的正确方法是在 KernelMode 代码中创建一个 USB 请求块,并在 UserMode 代码中使用 DeviceIoControl 到集线器驱动程序。
USBVIEW(UserMode代码)通过向HUB驱动发送IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EXioctl获取序列号 returns USB_NODE_CONNECTION_INFORMATION_EX 包含 USB_DEVICE_DESCRIPTOR。 USB_DEVICE_DESCRIPTOR 的 iSerialNumber 成员稍后在 IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl 请求最终获得序列号的集线器驱动程序。
我看到的另一种方法可能是使用一些第 3 方库(如 libusb)来简化所有这些操作...
更新二。 我看了一下USBSTOR反汇编代码。 USBSTOR_DeviceControl 例程具有以下用于 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
的代码 ++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
因此,它按预期将 IRP 向下传递到 usbhub 驱动程序。所以也许这个functionlaty有望在 usbhub 驱动程序有时?这对我来说太棒了...