Windows获取HID报告描述符长度比实际长度大64字节
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
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进行分析该函数:
从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
这个代码要是能看到修改记录就很有意思了
后来也经过某些不知来源的源代码验证:
至此,这个问题也算是有所交待吧。