UVC摄像头技术笔记
+ -

Windows驱动USBVideo的USB请求超时默认为5秒

2025-01-21 27 0

今天在调试HOOK一个USB设备驱动,在追踪返回HOOK之后配置描述符之后,上层驱动没有下发SET_CONFIGURATION请求。从设备管理器来看,设备直接返回的是启动失败,请求失败。
故为了分析该问题,需要进行调试追踪,不过意外发现了Windows中UVC驱动的默认超时。

在Windows系统中,兼容UVC驱动使用的驱动usbvideo.sys来驱动,由其配合KS最终实现的是USB相机的功能。故usbvideo.sys是一个承上启下的驱动,其关于USB的相关的操作都是位于该驱动中。

这个UAC驱动usbaudio.sys类似

首先在我们的HOOK驱动的请求设备描述符代码处加上断点,中断后进行栈回溯:

3: kd> k
 # Child-SP          RetAddr               Call Site
00 ffffa580`15e36058 fffff801`c858470a     nt!DbgBreakPointWithStatus
01 ffffa580`15e36060 fffff801`c841ae64     nt!vDbgPrintExWithPrefixInternal+0x169876
02 ffffa580`15e36160 fffff804`ac5d7970     nt!DbgPrint+0x3c
03 ffffa580`15e361a0 fffff804`ac5de48b     MyCamera!HookDirectCamera+0x80 [F:\WorkCamera\MyCamera\camera.c @ 1901] 
04 ffffa580`15e36210 fffff804`ac5de212     MyCamera!PDO_HandleInternalDeviceControl+0x4b [F:\WorkCamera\MyCamera\pdo.c @ 1199] 
05 ffffa580`15e36250 fffff804`ac5db86a     MyCamera!PDO_Dispatch+0xf2 [F:\WorkCamera\MyCamera\pdo.c @ 137] 
06 ffffa580`15e362a0 fffff804`abef456f     MyCamera!DispatchIrp+0x4a [F:\WorkCamera\MyCamera\dten.c @ 117] 
07 ffffa580`15e362e0 fffff804`abef5164     usbvideo!USBVideoCallUSBD+0xd7
08 ffffa580`15e36380 fffff804`abef312b     usbvideo!StartUSBVideoDevice+0x294
09 ffffa580`15e363f0 fffff804`aa77c290     usbvideo!USBVideoPnpStart+0x10b
0a ffffa580`15e36430 fffff804`aa772c94     ks!CKsDevice::PnpStart+0x86ec
0b ffffa580`15e36480 fffff804`acb114b1     ks!CKsDevice::DispatchPnp+0x104
0c ffffa580`15e364f0 fffff801`c88c9191     ksthunk!CKernelFilterDevice::DispatchIrp+0xf9
0d ffffa580`15e36550 fffff801`c84d3486     nt!PnpAsynchronousCall+0xe5
0e ffffa580`15e36590 fffff801`c8459660     nt!PnpSendIrp+0x92
0f ffffa580`15e36600 fffff801`c88c86ab     nt!PnpStartDevice+0x88
10 ffffa580`15e36690 fffff801`c87f2ca3     nt!PnpStartDeviceNode+0xdb
11 ffffa580`15e36720 fffff801`c88cb989     nt!PipProcessStartPhase1+0x53
12 ffffa580`15e36760 fffff801`c8a57826     nt!PipProcessDevNodeTree+0x401
13 ffffa580`15e369e0 fffff801`c858662e     nt!PiRestartDevice+0xba
14 ffffa580`15e36a30 fffff801`c848f039     nt!PnpDeviceActionWorker+0x15b26e
15 ffffa580`15e36b00 fffff801`c8433005     nt!ExpWorkerThread+0xe9
16 ffffa580`15e36b90 fffff801`c8571e66     nt!PspSystemThreadStartup+0x41
17 ffffa580`15e36be0 00000000`00000000     nt!KiStartSystemThread+0x16

可以看到,该请求是由usbaduio.sys在其IRP_MJ_PNP的回调函数usbvideo!USBVideoPnpStart中调用usbvideo!StartUSBVideoDevice实现的。最终创建的URB通过调用usbvideo!USBVideoCallUSBD下发给下层区动,即我们的驱动。

通过IDA打开usbvideo.sys,找到相关的函数。这理进行了简单的整理,代码如下:

__int64 __fastcall USBVideoCallUSBD(__int64 a1, ULONG_PTR Urb, ULONG code, ULONG_PTR RequestUrb)
{
  _DEVICE_OBJECT *DeviceObject; // r15
  ULONG IoControlCode; // ecx
  IRP *bIrp; // rax
  IRP *Irp; // rbx
  NTSTATUS status; // eax
  struct _IO_STATUS_BLOCK IoStatusBlock; // [rsp+50h] [rbp-30h] BYREF
  struct _KEVENT Event; // [rsp+60h] [rbp-20h] BYREF
  union _LARGE_INTEGER Timeout; // [rsp+A0h] [rbp+20h] BYREF

  DeviceObject = *(_DEVICE_OBJECT **)(a1 + 40);
  *(_QWORD *)&Event.Header.Lock = 0i64;
  Event.Header.WaitListHead.Flink = 0i64;
  Event.Header.WaitListHead.Blink = 0i64;
  IoStatusBlock.Pointer = 0i64;
  IoStatusBlock.Information = 0i64;
  KeInitializeEvent(&Event, NotificationEvent, 0);
  IoControlCode = 0x220003;                     // IOCTL_INTERNAL_USB_SUBMIT_URB
  if ( code )
    IoControlCode = code;
  bIrp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceObject, 0i64, 0, 0i64, 0, 1u, &Event, &IoStatusBlock);
  Irp = bIrp;
  if ( !bIrp )
    return 0xC000009Ai64;                       // STATUS_INSUFFICIENT_RESOURCES
  status = IoSetCompletionRoutineEx(*(PDEVICE_OBJECT *)(a1 + 24), bIrp, USBVideoCompleteSynch, &Event, 1u, 1u, 1u);
  if ( status < 0 )
    goto LABEL_14;
  if ( RequestUrb )
    Urb = RequestUrb;
  Irp->Tail.Overlay.CurrentStackLocation[-1].Parameters.Others.Argument1 = (PVOID)Urb;
  status = IofCallDriver(DeviceObject, Irp);
  if ( status == 0x103 )                        // STATUS_PENDING
  {
    Timeout.QuadPart = -50000000i64;
    if ( KeWaitForSingleObject(&Event, Executive, 0, 0, &Timeout) == 0x102 )
    {
      if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control && BYTE1(WPP_GLOBAL_Control->Timer) >= 5u )
        WPP_SF_(WPP_GLOBAL_Control->AttachedDevice, 18i64, &WPP_b7038f1b2f3431bf9a8df6f91b35ef63_Traceguids);
      IoCancelIrp(Irp);
      KeWaitForSingleObject(&Event, Executive, 0, 0, 0i64);
      IoStatusBlock.Status = 0x102;             // STATUS_TIMEOUT
    }
  }
  else
  {
LABEL_14:
    IoStatusBlock.Status = status;
  }
  IofCompleteRequest(Irp, 0);
  return (unsigned int)IoStatusBlock.Status;
}

可见,下发IRP的超时是间为 -50000000百纳秒,即5秒。如果5秒内该请求超时无法完成,则取消该IRP,等待其完成例程调用和,即可获取到event事件。

__int64 __fastcall USBVideoCompleteSynch(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
  if ( Irp->PendingReturned )
    KeSetEvent((PRKEVENT)Context, 0, 0);
  return 0xC00000
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数据包的最大响应时间及超时指标
每个 USB 设备都必须响应默认管道上的 Setup 包。Setup包用于设备的检测和配置,并执行一些常用功能,例如设置 USB 设备的地址,请求设备的描述符或检查端点的状态。符合 USB 规范的主机希望所有请求最多在5秒内得到处理。它还为特定请求指定了更严格的时间规定:不包含数据阶段(dat......
USB2.0 设备配置SET_CONFIGURATION传输及事务分析
USB2.0 设备配置SET_CONFIGURATION传输包含两个事务:事务38由SETUP令牌,DATA0数据和ACK事务组成。可以看到该SETUP事务由主机发送给设备,设备返回ACK表示接收成功。对于事务39,有一个IN事务,分别为IN令牌,DATA1和ACK,用于对上一个事务的数据确认。......
Window系统USB驱动提交URB并超时示例代码
Windows系统自带的USB驱动URB会设置一个超时,这个时间大概是5秒钟。源代码可以参考如下实现:NTSTATUSUSBAudioCancelCompleteSynch( IN PDEVICE_OBJECT DeviceObject, IN PIRP ......
Windows驱动USBVideo的USB请求超时默认为5秒
今天在调试HOOK一个USB设备驱动,在追踪返回HOOK之后配置描述符之后,上层驱动没有下发SET_CONFIGURATION请求。从设备管理器来看,设备直接返回的是启动失败,请求失败。故为了分析该问题,需要进行调试追踪,不过意外发现了Windows中UVC驱动的默认超时。在Windows系统中,......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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