我正在尝试使用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, '\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()
返回ERROR_INVALID_FUNCTION。
磁盘确实存在,并且它有序列号,请参阅此注册表项:
如何在不使用注册表的情况下从C代码中检索序列号?
答案 0 :(得分:4)
我们可以IOCTL_STORAGE_QUERY_PROPERTY
使用StorageDeviceProperty
(表示调用者正在查询设备描述符,STORAGE_DEVICE_DESCRIPTOR
)
并使用SerialNumberOffset
STORAGE_DEVICE_DESCRIPTOR
成员
指定从结构的开头到a的字节偏移量 以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, 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
只对文件系统设备有意义(更常见的是使用缓存) - 对于磁盘设备 - 这是无关紧要的
答案 1 :(得分:1)
我尝试了不同的方法,发现在用户模式和内核模式代码中,对于不同的USB设备,发送IOCTL_STORAGE_QUERY_PROPERTY均无法按预期方式工作。对于某些USB大容量存储,它不返回序列号。我认为有2种正确的方法可以做到这一点:
更新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标准吗?
在我的情况下,对于USB Mass Storage,“ wmic diskdrive get name,serialnumber”返回错误的序列号=“ E”,与使用IOCTL_STORAGE_QUERY_PROPERTY的结果相同。 因此,获取USB大容量存储设备序列号的正确方法是在KernelMode代码中创建USB请求块,并在UserMode代码中将DeviceIoControl用于集线器驱动程序。
USBVIEW(用户模式代码)通过将IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl发送给HUB驱动程序来获取序列号 返回包含USB_DEVICE_DESCRIPTOR的USB_NODE_CONNECTION_INFORMATION_EX。 USB_DEVICE_DESCRIPTOR的iSerialNumber成员稍后将在 向集线器驱动程序发出IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl请求,该请求最终获得序列号。
我看到的另一种方法可能是使用libusb之类的第三方库来简化所有这些操作...
更新2。 我看了一下USBSTOR的反汇编代码。 USBSTOR_DeviceControl例程具有以下代码,用于IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
因此,它按预期将IRP沿堆栈向下传递到usbhub驱动程序。因此,也许可以在 usbhub驱动程序何时出现?对我来说那太好了...