HID.dll报告描述符解析数据PreparsedData
前面我们提到了Hid.dll使用HidD_GetPreparsedData来获取一个不透明的报告描述符数据,使用该数据可以解析出很多关于HID设备的有用信息。如我们可以通过HidP_GetCaps来解析出HIDP_CAPS。
hid.dll是HidD_GetPreparsedData函数通过IOCTL_HID_GET_COLLECTION_DESCRIPTOR来获取PreparsedData的,这个IOCTL是发送给hidclass.sys的。而hidclass.sys是将系统从固件中获取的报告描述符使用HidP_GetCollectionDescription解析并导出一个结构体HIDP_DEVICE_DESC来表示HID设备的报告描述符。
当然HIDP_DEVICE_DESC中CollectionDescLength的数量表示了HID报告描述符复合的HID逻辑设备数量,所以说每个单独的HIDP_COLLECTION_DESC表示单独的HID逻辑设备的报告描述符信息,而HIDP_REPORT_IDS描述了所有描述符报告Report的信息。
HIDP_COLLECTION_DESC结构体中描述符关于HID逻辑设备的报告描述符数据信息。
typedef struct _HIDP_COLLECTION_DESC
{
USAGE UsagePage;
USAGE Usage;
UCHAR CollectionNumber;
UCHAR Reserved[15];
USHORT InputLength;
USHORT OutputLength;
USHORT FeatureLength;
USHORT PreparsedDataLength;
PHIDP_PREPARSED_DATA PreparsedData;
}HIDP_COLLECTION_DESC, * PHIDP_COLLECTION_DESC;
其中就有我们关注的PREPARSED_DATA信息,其中PreparsedData是数据的指针,PreparsedDataLength是数据缓冲区的长度,而IOCTL_HID_GET_COLLECTION_DESCRIPTOR的处量就是复制这些数据给应用层。
case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
{
//
// get collection description
//
CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
PDODeviceExtension->CollectionNumber);
ASSERT(CollectionDescription);
//
// check if output buffer is big enough
//
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < CollectionDescription->PreparsedDataLength)
{
//
// invalid buffer size
//
Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_BUFFER_SIZE;
}
//
// copy result
//
ASSERT(Irp->UserBuffer);
RtlCopyMemory(Irp->UserBuffer, CollectionDescription->PreparsedData, CollectionDescription->PreparsedDataLength);
//
// complete request
//
Irp->IoStatus.Information = CollectionDescription->PreparsedDataLength;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
PreparsedData数据我们知道是通地hidparser.sys解析hid报告描述符实现,其函数HidP_GetCollectionDescription,实现函数为HidParser_GetCollectionDescription。我们在HIDP_DEVICE_DESC和HIDP_COLLECTION_DESC、HIDP_REPORT_IDS关系注释一文http://www.usbzh.com/article/detail-956.html 看到过HidParser_GetCollectionDescription的实现原理。
在HidParser_GetCollectionDescription中首先通过HidParser_ParseReportDescriptor将HID报告描述符解析成一个ParserContext临时变量,再由ParserContext初始化HIDP_DEVICE_DESC。
ParserContext的介绍可见:HID_PARSER_CONTEXT解析报告描述符生成的COLLECTION树形结构http://www.usbzh.com/article/detail-958.html
我们回过头再看 USHORT PreparsedDataLength和 PHIDP_PREPARSED_DATA PreparsedData,其PreparsedData的数据由HidParser_BuildContext函数构建,其完整的数据结构如下图:
以上的数据结构定义来自REACTOS源代码的解析,实际与WINDOWS的不一致。