HID.DLL导出函数HidD_GetAttributes探究
HidD_GetAttributes函数用于获取HID设备的基本信息,如设备的VendorID,ProductID和VersionNumber,对应于USB HID设备描述符的如下字段:
WORD idVendor; //厂商编号
WORD idProduct; //产品编号
WORD bcdDevice;
HidD_GetAttributes函数原型
BOOLEAN HidD_GetAttributes(
[in] HANDLE HidDeviceObject,
[out] PHIDD_ATTRIBUTES Attributes
);
HidD_GetAttributes函数使用
HIDD_ATTRIBUTES Attributes;
ZeroMemory(&Attributes, sizeof(Attributes));
Attributes.Size = sizeof(HIDD_ATTRIBUTES);
if (!HidD_GetAttributes(tmp_DeviceHandle, &Attributes))
{
CloseHandle(tmp_DeviceHandle);
continue;
}
HidD_GetAttributes应用层实现
在Hid.dll导出函数与IOCTL对应大全http://www.usbzh.com/article/detail-929.html 一节,可知HidD_GetAttributes是通过IOCTL_HID_GET_COLLECTION_INFORMATION来实现的。
我们查看REACTOS的源代码hid.c中查看的HidD_GetAttributes源代码,路径位于:
E:\reactos\ReactOS-0.4.13-src-2020-0731\ReactOS-0.4.13\dll\win32\hid\hid.c
其具体的代码:
/*
* HidD_GetAttributes EXPORTED
*
* @implemented
*/
HIDAPI
BOOLEAN WINAPI
HidD_GetAttributes(IN HANDLE HidDeviceObject,
OUT PHIDD_ATTRIBUTES Attributes)
{
HID_COLLECTION_INFORMATION hci;
DWORD RetLen;
if(!DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_COLLECTION_INFORMATION,
NULL, 0,
&hci, sizeof(HID_COLLECTION_INFORMATION),
&RetLen, NULL))
{
return FALSE;
}
/* copy the fields */
Attributes->Size = sizeof(HIDD_ATTRIBUTES);
Attributes->VendorID = hci.VendorID;
Attributes->ProductID = hci.ProductID;
Attributes->VersionNumber = hci.VersionNumber;
return TRUE;
}
可以看到,实际上是通过HID_COLLECTION_INFORMATION结构体来获取数据,最后再复制给HIDD_ATTRIBUTES Attributes结构体。
HID_COLLECTION_INFORMATION结构体的定义如下:
typedef struct _HID_COLLECTION_INFORMATION {
//
// DescriptorSize is the size of the input buffer required to accept
// the collection descriptor returned by
// IOCTL_HID_GET_COLLECTION_DESCRIPTOR.
//
ULONG DescriptorSize;
//
// Polled is TRUE if this collection is a polled collection.
//
BOOLEAN Polled;
//
// Reserved1 must be set to zero.
//
UCHAR Reserved1[ 1 ];
//
// Vendor ids of this hid device
//
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
//
// Additional fields, if any, will be added at the end of this structure.
//
} HID_COLLECTION_INFORMATION, *PHID_COLLECTION_INFORMATION;
HidD_GetAttributes内核层实现
通过HID.dll与整个HID驱动程序架构的关系揭密http://www.usbzh.com/article/detail-931.html 一节,可知,这个IOCTL请求是发给HIDCLASS.SYS的PDO的,由于HIDCLASS的PDO和FDO共用一套回调函数,故我们直接转到其IRP_MJ_DEVICE_CONTROL的回调函数HidClassDispatch中。
DriverExtension->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HidClassDispatch;
实际的函数为HidClass_DeviceControl来处理的。
在ReactOS hidclass.c源代码中,对应IOCTL_HID_GET_COLLECTION_INFORMATION的处理如下:
case IOCTL_HID_GET_COLLECTION_INFORMATION:
{
//
// check if output buffer is big enough
//
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
{
//
// invalid buffer size
//
Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_BUFFER_SIZE;
}
//
// get output buffer
//
CollectionInformation = Irp->AssociatedIrp.SystemBuffer;
ASSERT(CollectionInformation);
//
// get collection description
//
CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
PDODeviceExtension->CollectionNumber);
ASSERT(CollectionDescription);
//
// init result buffer
//
CollectionInformation->DescriptorSize = CollectionDescription->PreparsedDataLength;
CollectionInformation->Polled = CommonDeviceExtension->DriverExtension->DevicesArePolled;
CollectionInformation->VendorID = CommonDeviceExtension->Attributes.VendorID;
CollectionInformation->ProductID = CommonDeviceExtension->Attributes.ProductID;
CollectionInformation->VersionNumber = CommonDeviceExtension->Attributes.VersionNumber;
//
// complete request
//
Irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
可以看到,这里有一个重要的数据PreparsedDataLength,不过在HidD_GetAttributes中是不需要的,另外关于VendorID,ProductID和VersionNumber的获取,是直接通过Attributes来获取的。
而Attributes的内容来源于hidclass的fdo在IRP_MN_START_DEVICE时获取的描述符时获取的。
具体的调用关系为:
IRP_MJ_PNP.IRP_MN_START_DEVICE -> HidClassFDO_StartDevice ->HidClassFDO_GetDescriptors
而在HidClassFDO_GetDescriptors中是通过发送一个IOCTL_HID_GET_DEVICE_ATTRIBUTES到HIDUSB驱动中获取的。
NTSTATUS
HidClassFDO_GetDescriptors(
IN PDEVICE_OBJECT DeviceObject)
{
...
IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_ATTRIBUTES;
IoStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DEVICE_ATTRIBUTES);
Irp->UserBuffer = &FDODeviceExtension->Common.Attributes;
Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
...
}
最终hidusb的代码如下:
case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
{
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_DEVICE_ATTRIBUTES))
{
//
// invalid request
//
Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES invalid buffer\n");
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_BUFFER_SIZE;
}
//
// store result
//
DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
ASSERT(HidDeviceExtension->DeviceDescriptor);
Irp->IoStatus.Information = sizeof(HID_DESCRIPTOR);
Attributes = Irp->UserBuffer;
Attributes->Size = sizeof(HID_DEVICE_ATTRIBUTES);
Attributes->VendorID = HidDeviceExtension->DeviceDescriptor->idVendor;
Attributes->ProductID = HidDeviceExtension->DeviceDescriptor->idProduct;
Attributes->VersionNumber = HidDeviceExtension->DeviceDescriptor->bcdDevice;
可以看到,这就获取的是设备描述符中相应的字段。
注:以上的源代码来自于REACTOS的源代码实现,而并非真实的windows源代码。不过实际的过程一致,只是代码细节上有所差异。