HID开发笔记
+ -

Windows获取HID报告描述符长度比实际长度大64字节

2022-03-14 865 0

HID是USB的一个大类,其中比较核心的概念是报告描述符
在USB设备枚举过程中,不知道大家有没有留意过这样的一个问题,就是自己在配置描述符中指定了报告描述符的长度为某个长度,但在实际的抓包处理时,发现Windows下发的数据长度为自己指定的长度+64字节。

这个问题从XP到win10都有这个问题。

如我们用BUSHOUND抓获取鼠标的HID报告描述符

     CTL    81 06 00 22  00 00 6e 00                            GET DESCRIPTOR     15us
 46  IN     05 01 09 02  a1 01 09 01  a1 00 05 09  19 01 29 03  ..............).  1.1ms
            15 00 25 01  95 08 75 01  81 02 05 01  09 30 09 31  ..%...u......0.1       
            09 38 15 81  25 7f 75 08  95 03 81 06  c0 c0        .8..%.u.......

可以看到,USB标准请求获取的长度为0x006e即110个字节,但实际返回的是46字节,这个长度多了64个字节,而我们在HID的配置描述符中实际指定的长度为46字节,那么为什么这里又是通过0x006e来获取了?

    CTL    80 06 00 02  00 00 22 00                           
34  IN     09 02 22 00  01 01 00 a0  32 09 04 00  00 01 03 01 
           02 00 09 21  11 01 00 01  22 2e 00 07  05 81 03 04 
           00 0a

如上面所述,我们的配置描述符中HID的描述符内容为:

09 21  11 01 00 01  22 2e 00

通过本站的在USB标准请求及描述符线工具分析可知:

0x09,        // bLength
0x21,        // bDescriptorType (HID)
0x11, 0x01,  // bcdHID 1.11
0x00,        // bCountryCode
0x01,        // bNumDescriptors
0x22,        // bDescriptorType[0] (HID)
0x2E, 0x00,  // wDescriptorLength[0] 46

而且我们通过实际分析,这种情况只发生在获取HID报告描述符的条件下,其它的如设备描述符,配置描述符、字符串描述符等其它描述符就不存在这个问题。

本人通过一个虚拟的HID设备,通过分析其获取HID报告描述符的IPR调用栈关系可知:

2: kd> kb
 # RetAddr            : Call Site
00 fffff80c`83424341  : testhid!HidDispatch+0x5f6 [F:\testhid\bulkint.c @ 455] 
01 fffff80c`83371b0f  : testhid!PDO_DispatchIntDevCtl+0x111 [F:\testhid\bulkint.c @ 762] 
02 fffff80c`833717d1  : hidusb!HumCallUSB+0x16f
03 fffff80c`8337b7d7  : hidusb!HumGetDescriptorRequest+0x181
04 fffff80c`83371522  : hidusb!HumGetReportDescriptor+0x77
05 fffff80c`8339422c  : hidusb!HumInternalIoctl+0x522
06 fffff80c`833b2243  : HIDCLASS!HidpCallDriver+0x7c
07 fffff80c`833b0934  : HIDCLASS!HidpCallDriverSynchronous+0x53
08 fffff80c`833b0cba  : HIDCLASS!GetHIDRawReportDescriptor+0x88
09 fffff80c`833b0248  : HIDCLASS!HidpGetDeviceDescriptor+0xba
0a fffff80c`83396207  : HIDCLASS!AllocDeviceResources+0x20
0b fffff80c`833955e5  : HIDCLASS!HidpFdoInitializeDevice+0x17
0c fffff80c`8339557e  : HIDCLASS!HIDSM_ExecuteEntryFunctionsAndPushPopStateMachinesForCurrentState+0x45
0d fffff80c`8339547b  : HIDCLASS!HIDSM_RunStateMachine+0xde
0e fffff80c`833b01a0  : HIDCLASS!HIDSM_AddEvent+0xcb
0f fffff80c`833b2b22  : HIDCLASS!HidpStartDevice+0xc0
10 fffff80c`833b180a  : HIDCLASS!HidpFdoPnp+0xc2
11 fffff80c`83392618  : HIDCLASS!HidpIrpMajorPnp+0x6a
12 fffff80c`7dee27c9  : HIDCLASS!HidpMajorHandler+0x168
13 fffff80c`7dee287e  : bhound7+0x27c9
14 fffff802`e0353191  : bhound7+0x287e
15 fffff802`dff5d486  : nt!PnpAsynchronousCall+0xe5
16 fffff802`dfee3660  : nt!PnpSendIrp+0x92
17 fffff802`e03526ab  : nt!PnpStartDevice+0x88
18 fffff802`e027cca3  : nt!PnpStartDeviceNode+0xdb
19 fffff802`e0355989  : nt!PipProcessStartPhase1+0x53
1a fffff802`e04e1826  : nt!PipProcessDevNodeTree+0x401
1b fffff802`e001062e  : nt!PiRestartDevice+0xba
1c fffff802`dff19039  : nt!PnpDeviceActionWorker+0x15b26e
1d fffff802`dfebd005  : nt!ExpWorkerThread+0xe9
1e fffff802`dfffbe66  : nt!PspSystemThreadStartup+0x41
1f 00000000`00000000  : nt!KiStartSystemThread+0x16

可以看到,其获取HID报告描述符是通过 hidusb!HumGetReportDescriptor函数获取的。
我们使用IDA进行分析该函数:
HumGetReportDescriptor

从HID结构体可以看到:

typedef struct _USB_HID_DESCRIPTOR
{
    UCHAR   bLength;
    UCHAR   bDescriptorType;
    USHORT  bcdHID;
    UCHAR   bCountry;
    UCHAR   bNumDescriptors;
    UCHAR   bReportType;
    USHORT  wReportLength;
} USB_HID_DESCRIPTOR, * PUSB_HID_DESCRIPTOR;

其长度为位于偏移为7的地址,所以

(unsigned __int16 *)(v9 + 87)

可能为扩展单元中USB_HID_DESCRIPTOR偏移地址。
我们通过windbg加载调试显示:

fffff80c`8337b770 488b4140        mov     rax,qword ptr [rcx+40h]
fffff80c`8337b774 4533d2          xor     r10d,r10d
fffff80c`8337b777 4c8bb2b8000000  mov     r14,qword ptr [rdx+0B8h]
fffff80c`8337b77e 4d8bf8          mov     r15,r8
fffff80c`8337b781 488bf2          mov     rsi,rdx
fffff80c`8337b784 4c89542478      mov     qword ptr [rsp+78h],r10
fffff80c`8337b789 488bf9          mov     rdi,rcx
fffff80c`8337b78c 488b5810        mov     rbx,qword ptr [rax+10h]
fffff80c`8337b790 0fb74357        movzx   eax,word ptr [rbx+57h]
fffff80c`8337b794 83c040          add     eax,40h                    //+64字节
fffff80c`8337b797 89442470        mov     dword ptr [rsp+70h],eax
fffff80c`8337b79b 8b4340          mov     eax,dword ptr [rbx+40h]
fffff80c`8337b79e a801            test    al,1
fffff80c`8337b7a0 0f85a2030000    jne     hidusb!HumGetReportDescriptor+0x3e8 (fffff80c`8337bb48)
fffff80c`8337b7a6 488b4310        mov     rax,qword ptr [rbx+10h]
fffff80c`8337b7aa 418d5228        lea     edx,[r10+28h]
fffff80c`8337b7ae 0fb64802        movzx   ecx,byte ptr [rax+2]
fffff80c`8337b7b2 440fb64356      movzx   r8d,byte ptr [rbx+56h]

本人反汇编和调试的机器都是win10,不过系统版本不一致,故汇编可能有出入。

断到fffff80c 8337b794 处 查看rbx的值 :

1: kd> r rbx
rbx=ffffa88878d49f10

查看其内容:

1: kd> r rbx
rbx=ffffa88878d49f10
1: kd> db rbx+50
ffffa888`78d49f60  09 21 11 01 00 01 22 2e-00 00 00 00 00 00 00 00  .!....".........

可以看到,这里加了64字节,就是对HID描述符的长度加了64来获取HID报告描述符的。

另外我们可以分析IRP完成的是URB,是是一个控制传输的URB.

    CTL    81 06 00 22  00 00 6e 00                            GET DESCRIPTOR     15us 
46  IN     05 01 09 02  a1 01 09 01  a1 00 05 09  19 01 29 03  ..............).  1.1ms 
           15 00 25 01  95 08 75 01  81 02 05 01  09 30 09 31  ..%...u......0.1        
           09 38 15 81  25 7f 75 08  95 03 81 06  c0 c0        .8..%.u.......          
    URB    88 00 08 00  00 00 00 00  98 43 8b eb  fe 59 00 00  CONTROL TRANSFER    1us 
           00 00 00 00  00 00 00 00  b0 7c 2a 11  01 a6 ff ff                          
           0b 00 00 00  2e 00 00 00  90 35 51 1e  01 a6 ff ff                          
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00                          
           02 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00                          
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00                          
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00                          
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00                          
           81 06 00 22  00 00 6e 00

可以看到,其请求时使用了长度为0x06e的长度,

81 06 00 22  00 00 6e 00

而IPR完成后,主机更新了实际返回的长度

2e 00 00 00

在于控制传输的URB的分析可见:http://www.usbzh.com/article/detail-648.html

在USB中文网的技术交流群中@xtoolbox也说过:

xtoolbox 18:12:07
按USB规范这样操作是允许的

xtoolbox 18:13:08
从机回复的内容不能大于主机指定的长度,可以小于或等于

清忱↙ 18:13:23
是的

清忱↙ 18:13:43
微软的源代码我也找到了

xtoolbox 18:13:45
有个特例,当数据长度是mps整倍数时,控制传输不需要0包结束

xtoolbox 18:16:01
猜测微软是在测试某些设备的时候,发现设备返回的内容总是圆整到mps,然后干脆加个最大mps来适配这些设备。

清忱↙ 18:16:16
是的

清忱↙ 18:16:48
估计也随心所欲的发挥了一下,反正也符合规范

xtoolbox 18:17:48
这个代码要是能看到修改记录就很有意思了

后来也经过某些不知来源的源代码验证:
HumGetReportDescriptor

至此,这个问题也算是有所交待吧。

HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936   

0 篇笔记 写笔记

USB复合HID设备报告描述符的区分
在USB规范中,设备的功能是通过接口来承载的,在USB规格书中就是接口描述符。对于一般的设备,一般一个接口就是一个功能,这个功能可以是鼠标,键盘或其它设备类型。当然这个只是对USB简易设备而言的,对于一些USB复杂设备如UVC摄像头,UAC音频等是多接口相互配合来实现其功能的,为了整合这么多接口使......
HID报告描述符详解
HID的报告描述符和其它描述符不一样,不是具有固定的数据结构,而是由固件开发商根据上报的数据自行组装的。这个组装的原材料是HID规范定义的ITEM。所以说HID报告描述符就像搭积木一样,其最终的数据结构和设计图纸是什么,都是由固件开发商决定的,但是其最终的产品又是由HID规范提供的各个标准积木模块搭......
USB键盘报告描述符数据格式分析
USB键盘的HID报表描述符的内容参见下表。数据的输入端点为中断方式,当有键盘敲击事件时,会上报数据长度为8字节的数据报告。0x05,0x01,// Global Generic Desktop0x09,0x06,// Local KeyBoard 0xA1,0x01,// Main app col......
USB鼠标HID报告描述符数据格式分析
注意:这里的鼠标为普通鼠标报告描述符的信息解释,其并不适用于全鼠标报表描述符。一个示例如下:0x05,0x01, // Global Generic Desktop0x09,0x02, // Local Mouse0xa1,0x01, // Main app collection0x09,......
USB中文网出品-HID报告描述符分析工具
HidReportParser.exe工具已经网页化,可访问http://www.usbzh.com/tool/usb.html 在线分析USB-HID报告描述符USB HID规范中有一个很重要的概念,就是HID的报告描述符。本人搞了这么久的HID设备开发,其中一项最头疼的是HID报告描述符的......
HID 报表描述符/报告描述符
报表描述符用于描述USB HID设备与主机之间数据交互的格式。根据数据的传输方向,分为输入报告和输出报告。输入报告是USB设备发送数据给主机的。大部分的USB HID设备主要实现的是输入报告。如我们平常使用的USB鼠标,就是通过我们点击鼠标按键或滑动鼠标流轮实现给主机上报鼠标的报告信息。输出报告......
HID报告描述符协议
HID报告描述符通过报告描述符的条目定义了HID报告数据的格式。对于大部分的HID设备来说,数据的传输方向都是输入的,即使用输入端点数据从设备端到主机端,并且使用的是中断的方式。当然HID设备不仅仅为数据的输入,有时也需要数据的输出。这时就需要一个输出中断端点来实现。报告描述符包含了属于特定Rep......
USB官网HID报告描述符生成工具
USB规范官网提供了一个HID报告描述符生成工具,https://www.usb.org/document-library/hid-descriptor-tool 使用我们此工具,我们可以:创建、编辑和验证HID报告描述符。输出多种输出(.txt、.inc、.h等)查看一些标准的设备报告描述符......
VS2019分析HID报告描述符解析源代码
常有人问,有没有HID解析的工具,好像除了usb官网提供的dt.exe,没有其它的好的东东了。Linux下有相关的工具,在本站的社区里:http://www.usbzh.com/zone/detail-12.html 可以查看做HID开发,其实最麻烦的就是HID报表描述符了。这里本人移植了Reac......
HID报告描述符Report Descriptor解析分析
HID独有的报告描述符也叫报表描述符对于初学者来说比较复杂,但是如果真正理解了它,我们又不得不惊吧于设计者的巧妙设计。HID报告描述符是由一系列的条目(item)组成的,一个item一般是又包括头部信息(bTag,bType,bSize)和数据内容。所以说item在HID的报告描述符中是最小的信息单......
HID报告描述符Usage
Usage是HID报告描述符的一部分,用于应用开发人员提供有关控制测量内容的信息。Usage标签指示了设备供应商对其设备的控制或者设备控制组的特定用途建议。例如虽然报告描述符中定义了数据格式(例如,三个 8 位字段),但使用Usages定义了应对数据执行的操作含义,例如 x、y 和 z 输入。 此功......
HID报告描述符分类大全
下表列出的是全部的项目的前缀字和简要功能说明。项目类型项目标志(Tag)项目前缀,nn为数据长度功能说明MainInput8x1000 00 nn定义输入报表,主机利用该信息解析设备提供的数据。主机向控制端口发送Get_Report实现输入MainOutpu......
HID音量控制报告描述符
音量控制HID规范提供了两种报告描述符,分别对应按钮式和旋钮式。Up/Down 按钮音量控制下面的示例定义了一对按钮,用于渐变变量,例如音量增大和音量向下按钮。输入设备必须定义为相对设备。–1将减小音量+1将增大音量。0对音量没有影响。UsagePage(Consumer)Usage(Vol......
HID磁带滚轮按钮报告描述符
磁带滚轮按钮是一个弹簧式旋钮,可旋转 ±90°,用户食指有一个小凹痕。 当用户向右或向左扭转旋钮时,磁带与从弹簧加载的中心位置的旋转成正比的速率前进或后退。UsagePage(Consumer)Usage(Tape Jog)Logical Minimum(-127),Logical Maxi......
HID 单元按钮报告描述符
单选按钮是一组相互排斥的按钮。在下面的示例中,音频接收器使用三个单选按钮在计算机、DVD 设备或万维网之间进行选择作为显示源。机械连接式单选接钮 Mechanically Linked Radio Buttons一般的,单选按钮实现有一个机械系统,它释放任何未按下的按钮,并将最后按下的按钮保持在活......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!