VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp@ 85334

Last change on this file since 85334 was 83803, checked in by vboxsync, 5 years ago

VBoxUSB: VC++ 14.1 adjustments and warnings (and some cleanups). bugref:8489

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.6 KB
Line 
1/* $Id: VBoxUsbMon.cpp 83803 2020-04-18 18:20:34Z vboxsync $ */
2/** @file
3 * VBox USB Monitor
4 */
5
6/*
7 * Copyright (C) 2011-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*
29 *
30 * Theory of Operation
31 * - or -
32 * The Document I Wish The Original Author Had Written
33 *
34 *
35 * The USB Monitor (VBoxUSBMon.sys) serves to capture and uncapture USB
36 * devices. Its job is to ensure that the USB proxy (VBoxUSB.sys) gets installed
37 * for captured devices and removed again when not needed, restoring the regular
38 * driver (if any).
39 *
40 * The USB Monitor does not handle any actual USB traffic; that is the role of
41 * VBoxUSB.sys, the USB proxy. A typical solution for installing such USB proxy
42 * is using a filter driver, but that approach was rejected because filter drivers
43 * cannot be dynamically added and removed. What VBoxUSBMon does instead is hook
44 * into the dispatch routine of the bus driver, i.e. USB hub driver, and alter
45 * the PnP information returned by the bus driver.
46 *
47 * The key functionality for capturing is cycling a USB port (which causes a USB
48 * device reset and triggers re-enumeration in the Windows USB driver stack), and
49 * then modifying IRP_MN_QUERY_ID / BusQueryHardwareIDs and related requests so
50 * that they return the synthetic USB VID/PID that VBoxUSB.sys handles rather than
51 * the true hardware VID/PID. That causes Windows to install VBoxUSB.sys for the
52 * device.
53 *
54 * Uncapturing again cycles the USB port but returns unmodified hardware IDs,
55 * causing Windows to load the normal driver for the device.
56 *
57 * Identifying devices to capture or release (uncapture) is done through USB filters,
58 * a cross-platform concept which matches USB device based on their VID/PID, class,
59 * and other criteria.
60 *
61 * There is an IOCTL interface for adding/removing USB filters and applying them.
62 * The IOCTLs are normally issued by VBoxSVC.
63 *
64 * USB devices are enumerated by finding all USB hubs (GUID_DEVINTERFACE_USB_HUB)
65 * and querying their child devices (i.e. USB devices or other hubs) by sending
66 * IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations. This is done when
67 * applying existing filters.
68 *
69 * Newly arrived USB devices are intercepted early in their PnP enumeration
70 * through the hooked bus driver dispatch routine. Devices which satisty the
71 * filter matching criteria are morphed (see above) such that VBoxUSB.sys loads
72 * for them before any default driver does.
73 *
74 * There is an IDC interface to VBoxUSB.sys which allows the USB proxy to report
75 * that it's installed for a given USB device, and also report when the USB proxy
76 * is unloaded (typically caused by either unplugging the device or uncapturing
77 * and cycling the port). VBoxUSBMon.sys relies on these IDC calls to track
78 * captured devices and be informed when VBoxUSB.sys unloads.
79 *
80 * Windows 8+ complicates the USB Monitor's life by automatically putting some
81 * USB devices to a low-power state where they are unable to respond to any USB
82 * requests and VBoxUSBMon can't read any of their descriptors (note that in
83 * userland, the device descriptor can always be read, but string descriptors
84 * can't). Such devices' USB VID/PID/revision is recovered using the Windows
85 * PnP Manager from their DevicePropertyHardwareID, but their USB class/subclass
86 * and protocol unfortunately cannot be unambiguously recovered from their
87 * DevicePropertyCompatibleIDs.
88 *
89 * Filter drivers add another complication. With filter drivers in place, the
90 * device objects returned by the BusRelations query (or passing through the PnP
91 * hooks) may not be PDOs but rather filter DOs higher in the stack. To avoid
92 * confusion, we flatten the references to their base, i.e. the real PDO, which
93 * should remain the same for the lifetime of a device. Note that VBoxUSB.sys
94 * always passes its own PDO in the proxy startup IOCTL.
95 */
96
97
98/*********************************************************************************************************************************
99* Header Files *
100*********************************************************************************************************************************/
101#include "VBoxUsbMon.h"
102#include "../cmn/VBoxUsbIdc.h"
103#include <iprt/errcore.h>
104#include <VBox/usblib.h>
105#include <excpt.h>
106
107
108/*********************************************************************************************************************************
109* Defined Constants And Macros *
110*********************************************************************************************************************************/
111#define VBOXUSBMON_MEMTAG 'MUBV'
112
113
114/*********************************************************************************************************************************
115* Structures and Typedefs *
116*********************************************************************************************************************************/
117typedef struct VBOXUSBMONINS
118{
119 void * pvDummy;
120} VBOXUSBMONINS, *PVBOXUSBMONINS;
121
122typedef struct VBOXUSBMONCTX
123{
124 VBOXUSBFLTCTX FltCtx;
125} VBOXUSBMONCTX, *PVBOXUSBMONCTX;
126
127typedef struct VBOXUSBHUB_PNPHOOK
128{
129 VBOXUSBHOOK_ENTRY Hook;
130 bool fUninitFailed;
131} VBOXUSBHUB_PNPHOOK, *PVBOXUSBHUB_PNPHOOK;
132
133typedef struct VBOXUSBHUB_PNPHOOK_COMPLETION
134{
135 VBOXUSBHOOK_REQUEST Rq;
136} VBOXUSBHUB_PNPHOOK_COMPLETION, *PVBOXUSBHUB_PNPHOOK_COMPLETION;
137
138#define VBOXUSBMON_MAXDRIVERS 5
139typedef struct VBOXUSB_PNPDRIVER
140{
141 PDRIVER_OBJECT DriverObject;
142 VBOXUSBHUB_PNPHOOK UsbHubPnPHook;
143 PDRIVER_DISPATCH pfnHookStub;
144} VBOXUSB_PNPDRIVER, *PVBOXUSB_PNPDRIVER;
145
146typedef struct VBOXUSBMONGLOBALS
147{
148 PDEVICE_OBJECT pDevObj;
149 VBOXUSB_PNPDRIVER pDrivers[VBOXUSBMON_MAXDRIVERS];
150 KEVENT OpenSynchEvent;
151 IO_REMOVE_LOCK RmLock;
152 uint32_t cOpens;
153 volatile LONG ulPreventUnloadOn;
154 PFILE_OBJECT pPreventUnloadFileObj;
155} VBOXUSBMONGLOBALS, *PVBOXUSBMONGLOBALS;
156
157
158/*********************************************************************************************************************************
159* Global Variables *
160*********************************************************************************************************************************/
161static VBOXUSBMONGLOBALS g_VBoxUsbMonGlobals;
162
163/*
164 * Note: Must match the VID & PID in the USB driver .inf file!!
165 */
166/*
167 BusQueryDeviceID USB\Vid_80EE&Pid_CAFE
168 BusQueryInstanceID 2
169 BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE&Rev_0100
170 BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE
171 BusQueryCompatibleIDs USB\Class_ff&SubClass_00&Prot_00
172 BusQueryCompatibleIDs USB\Class_ff&SubClass_00
173 BusQueryCompatibleIDs USB\Class_ff
174*/
175
176static WCHAR const g_szBusQueryDeviceId[] = L"USB\\Vid_80EE&Pid_CAFE";
177static WCHAR const g_szBusQueryHardwareIDs[] = L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0";
178static WCHAR const g_szBusQueryCompatibleIDs[] = L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0";
179static WCHAR const g_szDeviceTextDescription[] = L"VirtualBox USB";
180
181
182
183PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes)
184{
185 PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBMON_MEMTAG);
186 Assert(pvMem);
187 return pvMem;
188}
189
190PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes)
191{
192 PVOID pvMem = VBoxUsbMonMemAlloc(cbBytes);
193 if (pvMem)
194 {
195 RtlZeroMemory(pvMem, cbBytes);
196 }
197 return pvMem;
198}
199
200VOID VBoxUsbMonMemFree(PVOID pvMem)
201{
202 ExFreePoolWithTag(pvMem, VBOXUSBMON_MEMTAG);
203}
204
205#define VBOXUSBDBG_STRCASE(_t) \
206 case _t: return #_t
207#define VBOXUSBDBG_STRCASE_UNKNOWN(_v) \
208 default: LOG((__FUNCTION__": Unknown Value (0n%d), (0x%x)", _v, _v)); return "Unknown"
209
210/* These minor code are semi-undocumented. */
211#ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION
212#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
213#endif
214#ifndef IRP_MN_DEVICE_ENUMERATED
215#define IRP_MN_DEVICE_ENUMERATED 0x19
216#endif
217
218static const char* vboxUsbDbgStrPnPMn(UCHAR uMn)
219{
220 switch (uMn)
221 {
222 VBOXUSBDBG_STRCASE(IRP_MN_START_DEVICE);
223 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_REMOVE_DEVICE);
224 VBOXUSBDBG_STRCASE(IRP_MN_REMOVE_DEVICE);
225 VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_REMOVE_DEVICE);
226 VBOXUSBDBG_STRCASE(IRP_MN_STOP_DEVICE);
227 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_STOP_DEVICE);
228 VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_STOP_DEVICE);
229 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_RELATIONS);
230 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_INTERFACE);
231 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_CAPABILITIES);
232 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCES);
233 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
234 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_TEXT);
235 VBOXUSBDBG_STRCASE(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
236 VBOXUSBDBG_STRCASE(IRP_MN_READ_CONFIG);
237 VBOXUSBDBG_STRCASE(IRP_MN_WRITE_CONFIG);
238 VBOXUSBDBG_STRCASE(IRP_MN_EJECT);
239 VBOXUSBDBG_STRCASE(IRP_MN_SET_LOCK);
240 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_ID);
241 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_PNP_DEVICE_STATE);
242 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_BUS_INFORMATION);
243 VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_USAGE_NOTIFICATION);
244 VBOXUSBDBG_STRCASE(IRP_MN_SURPRISE_REMOVAL);
245 VBOXUSBDBG_STRCASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
246 VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_ENUMERATED);
247 VBOXUSBDBG_STRCASE_UNKNOWN(uMn);
248 }
249}
250
251/**
252 * Send IRP_MN_QUERY_DEVICE_RELATIONS
253 *
254 * @returns NT Status
255 * @param pDevObj USB device pointer
256 * @param pFileObj Valid file object pointer
257 * @param pDevRelations Pointer to DEVICE_RELATIONS pointer (out)
258 */
259NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations)
260{
261 IO_STATUS_BLOCK IoStatus;
262 KEVENT Event;
263 NTSTATUS Status;
264 PIRP pIrp;
265 PIO_STACK_LOCATION pSl;
266
267 KeInitializeEvent(&Event, NotificationEvent, FALSE);
268
269 Assert(pDevRelations);
270 *pDevRelations = NULL;
271
272 pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pDevObj, NULL, 0, NULL, &Event, &IoStatus);
273 if (!pIrp)
274 {
275 WARN(("IoBuildDeviceIoControlRequest failed!!"));
276 return STATUS_INSUFFICIENT_RESOURCES;
277 }
278 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
279
280 pSl = IoGetNextIrpStackLocation(pIrp);
281 pSl->MajorFunction = IRP_MJ_PNP;
282 pSl->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
283 pSl->Parameters.QueryDeviceRelations.Type = BusRelations;
284 pSl->FileObject = pFileObj;
285
286 Status = IoCallDriver(pDevObj, pIrp);
287 if (Status == STATUS_PENDING)
288 {
289 LOG(("IoCallDriver returned STATUS_PENDING!!"));
290 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
291 Status = IoStatus.Status;
292 }
293
294 if (Status == STATUS_SUCCESS)
295 {
296 PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)IoStatus.Information;
297 LOG(("pRel = %p", pRel));
298 if (VALID_PTR(pRel))
299 {
300 *pDevRelations = pRel;
301 }
302 else
303 {
304 WARN(("Invalid pointer %p", pRel));
305 }
306 }
307 else
308 {
309 WARN(("IRP_MN_QUERY_DEVICE_RELATIONS failed Status(0x%x)", Status));
310 }
311
312 LOG(("IoCallDriver returned %x", Status));
313 return Status;
314}
315
316VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker)
317{
318 NTSTATUS Status = STATUS_UNSUCCESSFUL;
319 PWSTR szwHubList;
320 Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_USB_HUB, NULL, 0, &szwHubList);
321 if (Status != STATUS_SUCCESS)
322 {
323 LOG(("IoGetDeviceInterfaces failed with %d\n", Status));
324 return;
325 }
326 if (szwHubList)
327 {
328 UNICODE_STRING UnicodeName;
329 PDEVICE_OBJECT pHubDevObj;
330 PFILE_OBJECT pHubFileObj;
331 PWSTR szwHubName = szwHubList;
332 while (*szwHubName != UNICODE_NULL)
333 {
334 RtlInitUnicodeString(&UnicodeName, szwHubName);
335 Status = IoGetDeviceObjectPointer(&UnicodeName, FILE_READ_DATA, &pHubFileObj, &pHubDevObj);
336 if (Status == STATUS_SUCCESS)
337 {
338 /* We could not log hub name here.
339 * It is the paged memory and we cannot use it in logger cause it increases the IRQL
340 */
341 LOG(("IoGetDeviceObjectPointer returned %p %p", pHubDevObj, pHubFileObj));
342 if (!pfnWalker(pHubFileObj, pHubDevObj, pvWalker))
343 {
344 LOG(("the walker said to stop"));
345 ObDereferenceObject(pHubFileObj);
346 break;
347 }
348
349 LOG(("going forward.."));
350 ObDereferenceObject(pHubFileObj);
351 }
352 szwHubName += wcslen(szwHubName) + 1;
353 }
354 ExFreePool(szwHubList);
355 }
356}
357
358/* NOTE: the stack location data is not the "actual" IRP stack location,
359 * but a copy being preserved on the IRP way down.
360 * See the note in VBoxUsbPnPCompletion for detail */
361static NTSTATUS vboxUsbMonHandlePnPIoctl(PDEVICE_OBJECT pDevObj, PIO_STACK_LOCATION pSl, PIO_STATUS_BLOCK pIoStatus)
362{
363 LOG(("IRQL = %d", KeGetCurrentIrql()));
364 switch(pSl->MinorFunction)
365 {
366 case IRP_MN_QUERY_DEVICE_TEXT:
367 {
368 LOG(("IRP_MN_QUERY_DEVICE_TEXT: pIoStatus->Status = %x", pIoStatus->Status));
369 if (pIoStatus->Status == STATUS_SUCCESS)
370 {
371 WCHAR *pId = (WCHAR *)pIoStatus->Information;
372 if (VALID_PTR(pId))
373 {
374 KIRQL Iqrl = KeGetCurrentIrql();
375 /* IRQL should be always passive here */
376 ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
377 switch (pSl->Parameters.QueryDeviceText.DeviceTextType)
378 {
379 case DeviceTextLocationInformation:
380 LOG(("DeviceTextLocationInformation"));
381 LOG_STRW(pId);
382 break;
383
384 case DeviceTextDescription:
385 LOG(("DeviceTextDescription"));
386 LOG_STRW(pId);
387 if (VBoxUsbFltPdoIsFiltered(pDevObj))
388 {
389 LOG(("PDO (0x%p) is filtered", pDevObj));
390 WCHAR *pId2 = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szDeviceTextDescription));
391 AssertBreak(pId2);
392 memcpy(pId2, g_szDeviceTextDescription, sizeof(g_szDeviceTextDescription));
393 LOG(("NEW szDeviceTextDescription"));
394 LOG_STRW(pId2);
395 ExFreePool((PVOID)pIoStatus->Information);
396 pIoStatus->Information = (ULONG_PTR)pId2;
397 }
398 else
399 {
400 LOG(("PDO (0x%p) is NOT filtered", pDevObj));
401 }
402 break;
403 default:
404 LOG(("DeviceText %d", pSl->Parameters.QueryDeviceText.DeviceTextType));
405 break;
406 }
407 }
408 else
409 LOG(("Invalid pointer %p", pId));
410 }
411 break;
412 }
413
414 case IRP_MN_QUERY_ID:
415 {
416 LOG(("IRP_MN_QUERY_ID: Irp->pIoStatus->Status = %x", pIoStatus->Status));
417 if (pIoStatus->Status == STATUS_SUCCESS && pDevObj)
418 {
419 WCHAR *pId = (WCHAR *)pIoStatus->Information;
420#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
421 WCHAR *pTmp;
422#endif
423 if (VALID_PTR(pId))
424 {
425 KIRQL Iqrl = KeGetCurrentIrql();
426 /* IRQL should be always passive here */
427 ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
428
429 switch (pSl->Parameters.QueryId.IdType)
430 {
431 case BusQueryInstanceID:
432 LOG(("BusQueryInstanceID"));
433 LOG_STRW(pId);
434 break;
435
436 case BusQueryDeviceID:
437 {
438 LOG(("BusQueryDeviceID"));
439 pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryDeviceId));
440 if (!pId)
441 {
442 WARN(("ExAllocatePool failed"));
443 break;
444 }
445
446 BOOLEAN bFiltered = FALSE;
447 NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
448 if (Status != STATUS_SUCCESS || !bFiltered)
449 {
450 if (Status == STATUS_SUCCESS)
451 {
452 LOG(("PDO (0x%p) is NOT filtered", pDevObj));
453 }
454 else
455 {
456 WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
457 }
458 ExFreePool(pId);
459 break;
460 }
461
462 LOG(("PDO (0x%p) is filtered", pDevObj));
463 ExFreePool((PVOID)pIoStatus->Information);
464 memcpy(pId, g_szBusQueryDeviceId, sizeof(g_szBusQueryDeviceId));
465 pIoStatus->Information = (ULONG_PTR)pId;
466 break;
467 }
468 case BusQueryHardwareIDs:
469 {
470 LOG(("BusQueryHardwareIDs"));
471#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
472 while (*pId) //MULTI_SZ
473 {
474 LOG_STRW(pId);
475 while (*pId) pId++;
476 pId++;
477 }
478#endif
479 pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryHardwareIDs));
480 if (!pId)
481 {
482 WARN(("ExAllocatePool failed"));
483 break;
484 }
485
486 BOOLEAN bFiltered = FALSE;
487 NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
488 if (Status != STATUS_SUCCESS || !bFiltered)
489 {
490 if (Status == STATUS_SUCCESS)
491 {
492 LOG(("PDO (0x%p) is NOT filtered", pDevObj));
493 }
494 else
495 {
496 WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
497 }
498 ExFreePool(pId);
499 break;
500 }
501
502 LOG(("PDO (0x%p) is filtered", pDevObj));
503
504 memcpy(pId, g_szBusQueryHardwareIDs, sizeof(g_szBusQueryHardwareIDs));
505#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
506 LOG(("NEW BusQueryHardwareIDs"));
507 pTmp = pId;
508 while (*pTmp) //MULTI_SZ
509 {
510
511 LOG_STRW(pTmp);
512 while (*pTmp) pTmp++;
513 pTmp++;
514 }
515#endif
516 ExFreePool((PVOID)pIoStatus->Information);
517 pIoStatus->Information = (ULONG_PTR)pId;
518 break;
519 }
520 case BusQueryCompatibleIDs:
521 LOG(("BusQueryCompatibleIDs"));
522#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
523 while (*pId) //MULTI_SZ
524 {
525 LOG_STRW(pId);
526 while (*pId) pId++;
527 pId++;
528 }
529#endif
530 if (VBoxUsbFltPdoIsFiltered(pDevObj))
531 {
532 LOG(("PDO (0x%p) is filtered", pDevObj));
533 pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryCompatibleIDs));
534 if (!pId)
535 {
536 WARN(("ExAllocatePool failed"));
537 break;
538 }
539 memcpy(pId, g_szBusQueryCompatibleIDs, sizeof(g_szBusQueryCompatibleIDs));
540#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
541 LOG(("NEW BusQueryCompatibleIDs"));
542 pTmp = pId;
543 while (*pTmp) //MULTI_SZ
544 {
545 LOG_STRW(pTmp);
546 while (*pTmp) pTmp++;
547 pTmp++;
548 }
549#endif
550 ExFreePool((PVOID)pIoStatus->Information);
551 pIoStatus->Information = (ULONG_PTR)pId;
552 }
553 else
554 {
555 LOG(("PDO (0x%p) is NOT filtered", pDevObj));
556 }
557 break;
558
559 default:
560 /** @todo r=bird: handle BusQueryContainerID and whatever else we might see */
561 break;
562 }
563 }
564 else
565 {
566 LOG(("Invalid pointer %p", pId));
567 }
568 }
569 break;
570 }
571
572#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
573 case IRP_MN_QUERY_DEVICE_RELATIONS:
574 {
575 switch(pSl->Parameters.QueryDeviceRelations.Type)
576 {
577 case BusRelations:
578 LOG(("BusRelations"));
579
580 if (pIoStatus->Status == STATUS_SUCCESS)
581 {
582 PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)pIoStatus->Information;
583 LOG(("pRel = %p", pRel));
584 if (VALID_PTR(pRel))
585 {
586 for (unsigned i=0;i<pRel->Count;i++)
587 {
588 if (VBoxUsbFltPdoIsFiltered(pDevObj))
589 LOG(("New PDO %p", pRel->Objects[i]));
590 }
591 }
592 else
593 LOG(("Invalid pointer %p", pRel));
594 }
595 break;
596 case TargetDeviceRelation:
597 LOG(("TargetDeviceRelation"));
598 break;
599 case RemovalRelations:
600 LOG(("RemovalRelations"));
601 break;
602 case EjectionRelations:
603 LOG(("EjectionRelations"));
604 break;
605 default:
606 LOG(("QueryDeviceRelations.Type=%d", pSl->Parameters.QueryDeviceRelations.Type));
607 }
608 break;
609 }
610
611 case IRP_MN_QUERY_CAPABILITIES:
612 {
613 LOG(("IRP_MN_QUERY_CAPABILITIES: pIoStatus->Status = %x", pIoStatus->Status));
614 if (pIoStatus->Status == STATUS_SUCCESS)
615 {
616 PDEVICE_CAPABILITIES pCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
617 if (VALID_PTR(pCaps))
618 {
619 LOG(("Caps.SilentInstall = %d", pCaps->SilentInstall));
620 LOG(("Caps.UniqueID = %d", pCaps->UniqueID ));
621 LOG(("Caps.Address = %d", pCaps->Address ));
622 LOG(("Caps.UINumber = %d", pCaps->UINumber ));
623 }
624 else
625 LOG(("Invalid pointer %p", pCaps));
626 }
627 break;
628 }
629
630 default:
631 break;
632#endif
633 } /*switch */
634
635 LOG(("Done returns %x (IRQL = %d)", pIoStatus->Status, KeGetCurrentIrql()));
636 return pIoStatus->Status;
637}
638
639NTSTATUS _stdcall VBoxUsbPnPCompletion(DEVICE_OBJECT *pDevObj, IRP *pIrp, void *pvContext)
640{
641 LOG(("Completion PDO(0x%p), IRP(0x%p), Status(0x%x)", pDevObj, pIrp, pIrp->IoStatus.Status));
642 ASSERT_WARN(pvContext, ("zero context"));
643
644 PVBOXUSBHOOK_REQUEST pRequest = (PVBOXUSBHOOK_REQUEST)pvContext;
645 /* NOTE: despite a regular IRP processing the stack location in our completion
646 * differs from those of the PnP hook since the hook is invoked in the "context" of the calle,
647 * while the completion is in the "coller" context in terms of IRP,
648 * so the completion stack location is one level "up" here.
649 *
650 * Moreover we CAN NOT access irp stack location in the completion because we might not have one at all
651 * in case the hooked driver is at the top of the irp call stack
652 *
653 * This is why we use the stack location we saved on IRP way down.
654 * */
655 PIO_STACK_LOCATION pSl = &pRequest->OldLocation;
656 ASSERT_WARN(pIrp == pRequest->pIrp, ("completed IRP(0x%x) not match request IRP(0x%x)", pIrp, pRequest->pIrp));
657 /* NOTE: we can not rely on pDevObj passed in IoCompletion since it may be zero
658 * in case IRP was created with extra stack locations and the caller did not initialize
659 * the IO_STACK_LOCATION::DeviceObject */
660 DEVICE_OBJECT *pRealDevObj = pRequest->pDevObj;
661// Assert(!pDevObj || pDevObj == pRealDevObj);
662// Assert(pSl->DeviceObject == pDevObj);
663
664 switch(pSl->MinorFunction)
665 {
666 case IRP_MN_QUERY_DEVICE_TEXT:
667 case IRP_MN_QUERY_ID:
668#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
669 case IRP_MN_QUERY_DEVICE_RELATIONS:
670 case IRP_MN_QUERY_CAPABILITIES:
671#endif
672 if (NT_SUCCESS(pIrp->IoStatus.Status))
673 {
674 vboxUsbMonHandlePnPIoctl(pRealDevObj, pSl, &pIrp->IoStatus);
675 }
676 else
677 {
678 ASSERT_WARN(pIrp->IoStatus.Status == STATUS_NOT_SUPPORTED, ("Irp failed with status(0x%x)", pIrp->IoStatus.Status));
679 }
680 break;
681
682 case IRP_MN_SURPRISE_REMOVAL:
683 case IRP_MN_REMOVE_DEVICE:
684 if (NT_SUCCESS(pIrp->IoStatus.Status))
685 {
686 VBoxUsbFltPdoRemove(pRealDevObj);
687 }
688 else
689 {
690 AssertFailed();
691 }
692 break;
693
694 /* These two IRPs are received when the PnP subsystem has determined the id of the newly arrived device */
695 /* IRP_MN_START_DEVICE only arrives if it's a USB device of a known class or with a present host driver */
696 case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
697 case IRP_MN_QUERY_RESOURCES:
698 /* There used to be code to support SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT but it was not reliable. */
699
700 default:
701 break;
702 }
703
704 LOG(("<==PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x), Sl PDO(0x%p), Compl PDO(0x%p)",
705 vboxUsbDbgStrPnPMn(pSl->MinorFunction),
706 pRealDevObj, pIrp, pIrp->IoStatus.Status,
707 pSl->DeviceObject, pDevObj));
708#ifdef DEBUG_misha
709 NTSTATUS tmpStatus = pIrp->IoStatus.Status;
710#endif
711 PVBOXUSBHOOK_ENTRY pHook = pRequest->pHook;
712 NTSTATUS Status = VBoxUsbHookRequestComplete(pHook, pDevObj, pIrp, pRequest);
713 VBoxUsbMonMemFree(pRequest);
714#ifdef DEBUG_misha
715 if (Status != STATUS_MORE_PROCESSING_REQUIRED)
716 {
717 Assert(pIrp->IoStatus.Status == tmpStatus);
718 }
719#endif
720 VBoxUsbHookRelease(pHook);
721 return Status;
722}
723
724/**
725 * Device PnP hook
726 *
727 * @param pDevObj Device object.
728 * @param pIrp Request packet.
729 */
730static NTSTATUS vboxUsbMonPnPHook(IN PVBOXUSBHOOK_ENTRY pHook, IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
731{
732 LOG(("==>PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x)", vboxUsbDbgStrPnPMn(IoGetCurrentIrpStackLocation(pIrp)->MinorFunction), pDevObj, pIrp, pIrp->IoStatus.Status));
733
734 if (!VBoxUsbHookRetain(pHook))
735 {
736 WARN(("VBoxUsbHookRetain failed"));
737 return VBoxUsbHookRequestPassDownHookSkip(pHook, pDevObj, pIrp);
738 }
739
740 PVBOXUSBHUB_PNPHOOK_COMPLETION pCompletion = (PVBOXUSBHUB_PNPHOOK_COMPLETION)VBoxUsbMonMemAlloc(sizeof (*pCompletion));
741 if (!pCompletion)
742 {
743 WARN(("VBoxUsbMonMemAlloc failed"));
744 VBoxUsbHookRelease(pHook);
745 pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
746 pIrp->IoStatus.Information = 0;
747 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
748 return STATUS_INSUFFICIENT_RESOURCES;
749 }
750
751 NTSTATUS Status = VBoxUsbHookRequestPassDownHookCompletion(pHook, pDevObj, pIrp, VBoxUsbPnPCompletion, &pCompletion->Rq);
752#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
753 if (Status != STATUS_PENDING)
754 {
755 LOG(("Request completed, Status(0x%x)", Status));
756 VBoxUsbHookVerifyCompletion(pHook, &pCompletion->Rq, pIrp);
757 }
758 else
759 {
760 LOG(("Request pending"));
761 }
762#endif
763 return Status;
764}
765
766/**
767 * Device PnP hook stubs.
768 *
769 * @param pDevObj Device object.
770 * @param pIrp Request packet.
771 */
772#define VBOX_PNPHOOKSTUB(n) NTSTATUS _stdcall VBoxUsbMonPnPHook##n(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) \
773{ \
774 return vboxUsbMonPnPHook(&g_VBoxUsbMonGlobals.pDrivers[n].UsbHubPnPHook.Hook, pDevObj, pIrp); \
775}
776
777#define VBOX_PNPHOOKSTUB_INIT(n) g_VBoxUsbMonGlobals.pDrivers[n].pfnHookStub = VBoxUsbMonPnPHook##n
778
779VBOX_PNPHOOKSTUB(0)
780VBOX_PNPHOOKSTUB(1)
781VBOX_PNPHOOKSTUB(2)
782VBOX_PNPHOOKSTUB(3)
783VBOX_PNPHOOKSTUB(4)
784AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
785
786typedef struct VBOXUSBMONHOOKDRIVERWALKER
787{
788 PDRIVER_OBJECT pDrvObj;
789} VBOXUSBMONHOOKDRIVERWALKER, *PVBOXUSBMONHOOKDRIVERWALKER;
790
791/**
792 * Logs an error to the system event log.
793 *
794 * @param ErrCode Error to report to event log.
795 * @param ReturnedStatus Error that was reported by the driver to the caller.
796 * @param uErrId Unique error id representing the location in the driver.
797 * @param cbDumpData Number of bytes at pDumpData.
798 * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
799 *
800 * NB: We only use IoLogMsg.dll as the message file, limiting
801 * ErrCode to status codes and messages defined in ntiologc.h
802 */
803static void vboxUsbMonLogError(NTSTATUS ErrCode, NTSTATUS ReturnedStatus, ULONG uErrId, USHORT cbDumpData, PVOID pDumpData)
804{
805 PIO_ERROR_LOG_PACKET pErrEntry;
806
807
808 /* Truncate dumps that do not fit into IO_ERROR_LOG_PACKET. */
809 if (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData > ERROR_LOG_MAXIMUM_SIZE)
810 cbDumpData = ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
811
812 pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxUsbMonGlobals.pDevObj,
813 FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData);
814 if (pErrEntry)
815 {
816 uint8_t *pDump = (uint8_t *)pErrEntry->DumpData;
817 if (cbDumpData)
818 memcpy(pDump, pDumpData, cbDumpData);
819 pErrEntry->MajorFunctionCode = 0;
820 pErrEntry->RetryCount = 0;
821 pErrEntry->DumpDataSize = cbDumpData;
822 pErrEntry->NumberOfStrings = 0;
823 pErrEntry->StringOffset = 0;
824 pErrEntry->ErrorCode = ErrCode;
825 pErrEntry->UniqueErrorValue = uErrId;
826 pErrEntry->FinalStatus = ReturnedStatus;
827 pErrEntry->IoControlCode = 0;
828 IoWriteErrorLogEntry(pErrEntry);
829 }
830 else
831 {
832 LOG(("Failed to allocate error log entry (cb=%d)\n", FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData));
833 }
834}
835
836static DECLCALLBACK(BOOLEAN) vboxUsbMonHookDrvObjWalker(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext)
837{
838 RT_NOREF2(pHubFile, pvContext);
839 PDRIVER_OBJECT pDrvObj = pHubDo->DriverObject;
840
841 /* First we try to figure out if we are already hooked to this driver. */
842 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
843 if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
844 {
845 LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
846 /* We've already hooked to this one -- nothing to do. */
847 return TRUE;
848 }
849 /* We are not hooked yet, find an empty slot. */
850 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
851 {
852 if (!g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
853 {
854 /* Found an emtpy slot, use it. */
855 g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = pDrvObj;
856 ObReferenceObject(pDrvObj);
857 LOG(("pDrivers[%d] = %p, installing the hook...\n", i, pDrvObj));
858 VBoxUsbHookInit(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook,
859 pDrvObj,
860 IRP_MJ_PNP,
861 g_VBoxUsbMonGlobals.pDrivers[i].pfnHookStub);
862 VBoxUsbHookInstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
863 return TRUE; /* Must continue to find all drivers. */
864 }
865 if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
866 {
867 LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
868 /* We've already hooked to this one -- nothing to do. */
869 return TRUE;
870 }
871 }
872 /* No empty slots! No reason to continue. */
873 LOG(("No empty slots!\n"));
874 ANSI_STRING ansiDrvName;
875 NTSTATUS Status = RtlUnicodeStringToAnsiString(&ansiDrvName, &pDrvObj->DriverName, true);
876 if (Status != STATUS_SUCCESS)
877 {
878 ansiDrvName.Length = 0;
879 LOG(("RtlUnicodeStringToAnsiString failed with 0x%x", Status));
880 }
881 vboxUsbMonLogError(IO_ERR_INSUFFICIENT_RESOURCES, STATUS_SUCCESS, 1, ansiDrvName.Length, ansiDrvName.Buffer);
882 if (Status == STATUS_SUCCESS)
883 RtlFreeAnsiString(&ansiDrvName);
884 return FALSE;
885}
886
887/**
888 * Finds all USB drivers in the system and installs hooks if haven't done already.
889 */
890static NTSTATUS vboxUsbMonInstallAllHooks()
891{
892 vboxUsbMonHubDevWalk(vboxUsbMonHookDrvObjWalker, NULL);
893 return STATUS_SUCCESS;
894}
895
896static NTSTATUS vboxUsbMonHookCheckInit()
897{
898 static bool fIsHookInited = false;
899 if (fIsHookInited)
900 {
901 LOG(("hook inited already, success"));
902 return STATUS_SUCCESS;
903 }
904 return vboxUsbMonInstallAllHooks();
905}
906
907static NTSTATUS vboxUsbMonHookInstall()
908{
909 /* Nothing to do here as we have already installed all hooks in vboxUsbMonHookCheckInit(). */
910 return STATUS_SUCCESS;
911}
912
913static NTSTATUS vboxUsbMonHookUninstall()
914{
915#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
916 return STATUS_SUCCESS;
917#else
918 NTSTATUS Status = STATUS_SUCCESS;
919 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
920 {
921 if (g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
922 {
923 Assert(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject == g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook.pDrvObj);
924 LOG(("Unhooking from %p...\n", g_VBoxUsbMonGlobals.pDrivers[i].DriverObject));
925 Status = VBoxUsbHookUninstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
926 if (!NT_SUCCESS(Status))
927 {
928 /*
929 * We failed to uninstall the hook, so we keep the reference to the driver
930 * in order to prevent another driver re-using this slot because we are
931 * going to mark this hook as fUninitFailed.
932 */
933 //AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
934 LOG(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed));
935 g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed = true;
936 }
937 else
938 {
939 /* The hook was removed successfully, now we can forget about this driver. */
940 ObDereferenceObject(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject);
941 g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = NULL;
942 }
943 }
944 }
945 return Status;
946#endif
947}
948
949
950static NTSTATUS vboxUsbMonCheckTermStuff()
951{
952 NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
953 Executive, KernelMode,
954 FALSE, /* BOOLEAN Alertable */
955 NULL /* IN PLARGE_INTEGER Timeout */
956 );
957 AssertRelease(Status == STATUS_SUCCESS);
958
959 do
960 {
961 if (--g_VBoxUsbMonGlobals.cOpens)
962 break;
963
964 Status = vboxUsbMonHookUninstall();
965
966 NTSTATUS tmpStatus = VBoxUsbFltTerm();
967 if (!NT_SUCCESS(tmpStatus))
968 {
969 /* this means a driver state is screwed up, KeBugCheckEx here ? */
970 AssertReleaseFailed();
971 }
972 } while (0);
973
974 KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
975
976 return Status;
977}
978
979static NTSTATUS vboxUsbMonCheckInitStuff()
980{
981 NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
982 Executive, KernelMode,
983 FALSE, /* BOOLEAN Alertable */
984 NULL /* IN PLARGE_INTEGER Timeout */
985 );
986 if (Status == STATUS_SUCCESS)
987 {
988 do
989 {
990 if (g_VBoxUsbMonGlobals.cOpens++)
991 {
992 LOG(("opens: %d, success", g_VBoxUsbMonGlobals.cOpens));
993 break;
994 }
995
996 Status = VBoxUsbFltInit();
997 if (NT_SUCCESS(Status))
998 {
999 Status = vboxUsbMonHookCheckInit();
1000 if (NT_SUCCESS(Status))
1001 {
1002 Status = vboxUsbMonHookInstall();
1003 if (NT_SUCCESS(Status))
1004 {
1005 Status = STATUS_SUCCESS;
1006 LOG(("succeded!!"));
1007 break;
1008 }
1009 else
1010 {
1011 WARN(("vboxUsbMonHookInstall failed, Status (0x%x)", Status));
1012 }
1013 }
1014 else
1015 {
1016 WARN(("vboxUsbMonHookCheckInit failed, Status (0x%x)", Status));
1017 }
1018 VBoxUsbFltTerm();
1019 }
1020 else
1021 {
1022 WARN(("VBoxUsbFltInit failed, Status (0x%x)", Status));
1023 }
1024
1025 --g_VBoxUsbMonGlobals.cOpens;
1026 Assert(!g_VBoxUsbMonGlobals.cOpens);
1027 } while (0);
1028
1029 KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
1030 }
1031 else
1032 {
1033 WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
1034 }
1035 return Status;
1036}
1037
1038static NTSTATUS vboxUsbMonContextCreate(PVBOXUSBMONCTX *ppCtx)
1039{
1040 NTSTATUS Status;
1041 *ppCtx = NULL;
1042 PVBOXUSBMONCTX pFileCtx = (PVBOXUSBMONCTX)VBoxUsbMonMemAllocZ(sizeof (*pFileCtx));
1043 if (pFileCtx)
1044 {
1045 Status = vboxUsbMonCheckInitStuff();
1046 if (Status == STATUS_SUCCESS)
1047 {
1048 Status = VBoxUsbFltCreate(&pFileCtx->FltCtx);
1049 if (Status == STATUS_SUCCESS)
1050 {
1051 *ppCtx = pFileCtx;
1052 LOG(("succeeded!!"));
1053 return STATUS_SUCCESS;
1054 }
1055 else
1056 {
1057 WARN(("VBoxUsbFltCreate failed"));
1058 }
1059 vboxUsbMonCheckTermStuff();
1060 }
1061 else
1062 {
1063 WARN(("vboxUsbMonCheckInitStuff failed"));
1064 }
1065 VBoxUsbMonMemFree(pFileCtx);
1066 }
1067 else
1068 {
1069 WARN(("VBoxUsbMonMemAllocZ failed"));
1070 Status = STATUS_NO_MEMORY;
1071 }
1072
1073 return Status;
1074}
1075
1076static NTSTATUS vboxUsbMonContextClose(PVBOXUSBMONCTX pCtx)
1077{
1078 NTSTATUS Status = VBoxUsbFltClose(&pCtx->FltCtx);
1079 if (Status == STATUS_SUCCESS)
1080 {
1081 Status = vboxUsbMonCheckTermStuff();
1082 Assert(Status == STATUS_SUCCESS);
1083 /* ignore the failure */
1084 VBoxUsbMonMemFree(pCtx);
1085 }
1086
1087 return Status;
1088}
1089
1090static NTSTATUS _stdcall VBoxUsbMonClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1091{
1092 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1093 PFILE_OBJECT pFileObj = pStack->FileObject;
1094 Assert(pFileObj->FsContext);
1095 PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
1096
1097 LOG(("VBoxUsbMonClose"));
1098
1099 NTSTATUS Status = vboxUsbMonContextClose(pCtx);
1100 if (Status != STATUS_SUCCESS)
1101 {
1102 WARN(("vboxUsbMonContextClose failed, Status (0x%x), prevent unload", Status));
1103 if (!InterlockedExchange(&g_VBoxUsbMonGlobals.ulPreventUnloadOn, 1))
1104 {
1105 LOGREL(("ulPreventUnloadOn not set, preventing unload"));
1106 UNICODE_STRING UniName;
1107 PDEVICE_OBJECT pTmpDevObj;
1108 RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
1109 NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
1110 AssertRelease(NT_SUCCESS(tmpStatus));
1111 AssertRelease(pTmpDevObj == pDevObj);
1112 }
1113 else
1114 {
1115 WARN(("ulPreventUnloadOn already set"));
1116 }
1117 LOG(("success!!"));
1118 Status = STATUS_SUCCESS;
1119 }
1120 pFileObj->FsContext = NULL;
1121 pIrp->IoStatus.Status = Status;
1122 pIrp->IoStatus.Information = 0;
1123 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1124 return Status;
1125}
1126
1127
1128static NTSTATUS _stdcall VBoxUsbMonCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1129{
1130 RT_NOREF1(pDevObj);
1131 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1132 PFILE_OBJECT pFileObj = pStack->FileObject;
1133 NTSTATUS Status;
1134
1135 LOG(("VBoxUSBMonCreate"));
1136
1137 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1138 {
1139 WARN(("trying to open as a directory"));
1140 pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
1141 pIrp->IoStatus.Information = 0;
1142 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1143 return STATUS_NOT_A_DIRECTORY;
1144 }
1145
1146 pFileObj->FsContext = NULL;
1147 PVBOXUSBMONCTX pCtx = NULL;
1148 Status = vboxUsbMonContextCreate(&pCtx);
1149 if (Status == STATUS_SUCCESS)
1150 {
1151 Assert(pCtx);
1152 pFileObj->FsContext = pCtx;
1153 }
1154 else
1155 {
1156 WARN(("vboxUsbMonContextCreate failed Status (0x%x)", Status));
1157 }
1158
1159 pIrp->IoStatus.Status = Status;
1160 pIrp->IoStatus.Information = 0;
1161 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1162 return Status;
1163}
1164
1165static int VBoxUsbMonFltAdd(PVBOXUSBMONCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
1166{
1167#ifdef VBOXUSBMON_DBG_NO_FILTERS
1168 static uintptr_t idDummy = 1;
1169 *pId = idDummy;
1170 ++idDummy;
1171 return VINF_SUCCESS;
1172#else
1173 int rc = VBoxUsbFltAdd(&pContext->FltCtx, pFilter, pId);
1174 return rc;
1175#endif
1176}
1177
1178static int VBoxUsbMonFltRemove(PVBOXUSBMONCTX pContext, uintptr_t uId)
1179{
1180#ifdef VBOXUSBMON_DBG_NO_FILTERS
1181 return VINF_SUCCESS;
1182#else
1183 int rc = VBoxUsbFltRemove(&pContext->FltCtx, uId);
1184 return rc;
1185#endif
1186}
1187
1188static NTSTATUS VBoxUsbMonRunFilters(PVBOXUSBMONCTX pContext)
1189{
1190 NTSTATUS Status = VBoxUsbFltFilterCheck(&pContext->FltCtx);
1191 return Status;
1192}
1193
1194static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
1195{
1196 NTSTATUS Status = VBoxUsbFltGetDevice(&pContext->FltCtx, hDevice, pInfo);
1197 return Status;
1198}
1199
1200static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer,
1201 ULONG cbOutBuffer, ULONG_PTR *pInfo)
1202{
1203 NTSTATUS Status = STATUS_SUCCESS;
1204 ULONG_PTR Info = 0;
1205 switch (Ctl)
1206 {
1207 case SUPUSBFLT_IOCTL_GET_VERSION:
1208 {
1209 PUSBSUP_VERSION pOut = (PUSBSUP_VERSION)pvBuffer;
1210
1211 LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
1212 if (!pvBuffer || cbOutBuffer != sizeof(*pOut) || cbInBuffer != 0)
1213 {
1214 WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1215 cbInBuffer, 0, cbOutBuffer, sizeof (*pOut)));
1216 Status = STATUS_INVALID_PARAMETER;
1217 break;
1218 }
1219 pOut->u32Major = USBMON_MAJOR_VERSION;
1220 pOut->u32Minor = USBMON_MINOR_VERSION;
1221 Info = sizeof (*pOut);
1222 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1223 break;
1224 }
1225
1226 case SUPUSBFLT_IOCTL_ADD_FILTER:
1227 {
1228 PUSBFILTER pFilter = (PUSBFILTER)pvBuffer;
1229 PUSBSUP_FLTADDOUT pOut = (PUSBSUP_FLTADDOUT)pvBuffer;
1230 uintptr_t uId = 0;
1231 int rc;
1232 if (RT_UNLIKELY(!pvBuffer || cbInBuffer != sizeof (*pFilter) || cbOutBuffer != sizeof (*pOut)))
1233 {
1234 WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1235 cbInBuffer, sizeof (*pFilter), cbOutBuffer, sizeof (*pOut)));
1236 Status = STATUS_INVALID_PARAMETER;
1237 break;
1238 }
1239
1240 rc = VBoxUsbMonFltAdd(pContext, pFilter, &uId);
1241 pOut->rc = rc;
1242 pOut->uId = uId;
1243 Info = sizeof (*pOut);
1244 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1245 break;
1246 }
1247
1248 case SUPUSBFLT_IOCTL_REMOVE_FILTER:
1249 {
1250 uintptr_t *pIn = (uintptr_t *)pvBuffer;
1251 int *pRc = (int *)pvBuffer;
1252
1253 if (!pvBuffer || cbInBuffer != sizeof (*pIn) || (cbOutBuffer && cbOutBuffer != sizeof (*pRc)))
1254 {
1255 WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1256 cbInBuffer, sizeof (*pIn), cbOutBuffer, 0));
1257 Status = STATUS_INVALID_PARAMETER;
1258 break;
1259 }
1260 LOG(("SUPUSBFLT_IOCTL_REMOVE_FILTER %x", *pIn));
1261 int rc = VBoxUsbMonFltRemove(pContext, *pIn);
1262 if (cbOutBuffer)
1263 {
1264 /* we've validated that already */
1265 Assert(cbOutBuffer == (ULONG)*pRc);
1266 *pRc = rc;
1267 Info = sizeof (*pRc);
1268 }
1269 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1270 break;
1271 }
1272
1273 case SUPUSBFLT_IOCTL_RUN_FILTERS:
1274 {
1275 if (pvBuffer || cbInBuffer || cbOutBuffer)
1276 {
1277 WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1278 cbInBuffer, 0, cbOutBuffer, 0));
1279 Status = STATUS_INVALID_PARAMETER;
1280 break;
1281 }
1282 LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
1283 Status = VBoxUsbMonRunFilters(pContext);
1284 ASSERT_WARN(Status != STATUS_PENDING, ("status pending!"));
1285 break;
1286 }
1287
1288 case SUPUSBFLT_IOCTL_GET_DEVICE:
1289 {
1290 HVBOXUSBDEVUSR hDevice = *((HVBOXUSBDEVUSR*)pvBuffer);
1291 PUSBSUP_GETDEV_MON pOut = (PUSBSUP_GETDEV_MON)pvBuffer;
1292 if (!pvBuffer || cbInBuffer != sizeof (hDevice) || cbOutBuffer < sizeof (*pOut))
1293 {
1294 WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes! cbIn=%d expected %d. cbOut=%d expected >= %d.",
1295 cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
1296 Status = STATUS_INVALID_PARAMETER;
1297 break;
1298 }
1299 if (!hDevice)
1300 {
1301 WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: hDevice is NULL!",
1302 cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
1303 Status = STATUS_INVALID_PARAMETER;
1304 break;
1305 }
1306
1307 Status = VBoxUsbMonGetDevice(pContext, hDevice, pOut);
1308
1309 if (NT_SUCCESS(Status))
1310 {
1311 Info = sizeof (*pOut);
1312 }
1313 else
1314 {
1315 WARN(("VBoxUsbMonGetDevice fail 0x%x", Status));
1316 }
1317 break;
1318 }
1319
1320 default:
1321 WARN(("Unknown code 0x%x", Ctl));
1322 Status = STATUS_INVALID_PARAMETER;
1323 break;
1324 }
1325
1326 ASSERT_WARN(Status != STATUS_PENDING, ("Status pending!"));
1327
1328 *pInfo = Info;
1329 return Status;
1330}
1331
1332static NTSTATUS _stdcall VBoxUsbMonDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1333{
1334 ULONG_PTR Info = 0;
1335 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1336 if (NT_SUCCESS(Status))
1337 {
1338 PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
1339 PFILE_OBJECT pFileObj = pSl->FileObject;
1340 Assert(pFileObj);
1341 Assert(pFileObj->FsContext);
1342 PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
1343 Assert(pCtx);
1344 Status = vboxUsbMonIoctlDispatch(pCtx,
1345 pSl->Parameters.DeviceIoControl.IoControlCode,
1346 pIrp->AssociatedIrp.SystemBuffer,
1347 pSl->Parameters.DeviceIoControl.InputBufferLength,
1348 pSl->Parameters.DeviceIoControl.OutputBufferLength,
1349 &Info);
1350 ASSERT_WARN(Status != STATUS_PENDING, ("Status pending"));
1351
1352 IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1353 }
1354 else
1355 {
1356 WARN(("IoAcquireRemoveLock failed Status (0x%x)", Status));
1357 }
1358
1359 pIrp->IoStatus.Information = Info;
1360 pIrp->IoStatus.Status = Status;
1361 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
1362 return Status;
1363}
1364
1365static NTSTATUS vboxUsbMonInternalIoctlDispatch(ULONG Ctl, PVOID pvBuffer, ULONG_PTR *pInfo)
1366{
1367 NTSTATUS Status = STATUS_SUCCESS;
1368 *pInfo = 0;
1369 switch (Ctl)
1370 {
1371 case VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION:
1372 {
1373 PVBOXUSBIDC_VERSION pOut = (PVBOXUSBIDC_VERSION)pvBuffer;
1374
1375 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
1376 if (!pvBuffer)
1377 {
1378 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
1379 Status = STATUS_INVALID_PARAMETER;
1380 break;
1381 }
1382 pOut->u32Major = VBOXUSBIDC_VERSION_MAJOR;
1383 pOut->u32Minor = VBOXUSBIDC_VERSION_MINOR;
1384 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1385 break;
1386 }
1387
1388 case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP:
1389 {
1390 PVBOXUSBIDC_PROXY_STARTUP pOut = (PVBOXUSBIDC_PROXY_STARTUP)pvBuffer;
1391
1392 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
1393 if (!pvBuffer)
1394 {
1395 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
1396 Status = STATUS_INVALID_PARAMETER;
1397 break;
1398 }
1399
1400 PDEVICE_OBJECT pDevObj = pOut->u.pPDO;
1401 pOut->u.hDev = VBoxUsbFltProxyStarted(pDevObj);
1402
1403 /* If we couldn't find the PDO in our list, that's a real problem and
1404 * the capturing will not really work. Log an error.
1405 */
1406 if (!pOut->u.hDev)
1407 vboxUsbMonLogError(IO_ERR_DRIVER_ERROR, STATUS_SUCCESS, 2, sizeof("INTERNAL_IOCTL_PROXY_STARTUP"), "INTERNAL_IOCTL_PROXY_STARTUP");
1408
1409 ASSERT_WARN(pOut->u.hDev, ("zero hDev"));
1410 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1411 break;
1412 }
1413
1414 case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN:
1415 {
1416 PVBOXUSBIDC_PROXY_TEARDOWN pOut = (PVBOXUSBIDC_PROXY_TEARDOWN)pvBuffer;
1417
1418 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
1419 if (!pvBuffer)
1420 {
1421 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
1422 Status = STATUS_INVALID_PARAMETER;
1423 break;
1424 }
1425
1426 ASSERT_WARN(pOut->hDev, ("zero hDev"));
1427 VBoxUsbFltProxyStopped(pOut->hDev);
1428 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1429 break;
1430 }
1431
1432 default:
1433 {
1434 WARN(("Unknown code 0x%x", Ctl));
1435 Status = STATUS_INVALID_PARAMETER;
1436 break;
1437 }
1438 }
1439
1440 return Status;
1441}
1442
1443static NTSTATUS _stdcall VBoxUsbMonInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1444{
1445 ULONG_PTR Info = 0;
1446 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1447 if (NT_SUCCESS(Status))
1448 {
1449 PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
1450 Status = vboxUsbMonInternalIoctlDispatch(pSl->Parameters.DeviceIoControl.IoControlCode,
1451 pSl->Parameters.Others.Argument1,
1452 &Info);
1453 Assert(Status != STATUS_PENDING);
1454
1455 IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1456 }
1457
1458 pIrp->IoStatus.Information = Info;
1459 pIrp->IoStatus.Status = Status;
1460 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
1461 return Status;
1462}
1463
1464/**
1465 * Unload the driver.
1466 *
1467 * @param pDrvObj Driver object.
1468 */
1469static void _stdcall VBoxUsbMonUnload(PDRIVER_OBJECT pDrvObj)
1470{
1471 RT_NOREF1(pDrvObj);
1472 LOG(("VBoxUSBMonUnload pDrvObj (0x%p)", pDrvObj));
1473
1474 IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1475
1476 Assert(!g_VBoxUsbMonGlobals.cOpens);
1477
1478 UNICODE_STRING DosName;
1479 RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
1480 IoDeleteSymbolicLink(&DosName);
1481
1482 IoDeleteDevice(g_VBoxUsbMonGlobals.pDevObj);
1483
1484 /* cleanup the logger */
1485 PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
1486 if (pLogger)
1487 RTLogDestroy(pLogger);
1488 pLogger = RTLogSetDefaultInstance(NULL);
1489 if (pLogger)
1490 RTLogDestroy(pLogger);
1491}
1492
1493RT_C_DECLS_BEGIN
1494NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
1495RT_C_DECLS_END
1496
1497/**
1498 * Driver entry point.
1499 *
1500 * @returns appropriate status code.
1501 * @param pDrvObj Pointer to driver object.
1502 * @param pRegPath Registry base path.
1503 */
1504NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1505{
1506 RT_NOREF1(pRegPath);
1507#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
1508 RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
1509 RTLogDestinations(0, "debugger");
1510#endif
1511
1512 LOGREL(("Built %s %s", __DATE__, __TIME__));
1513
1514 memset (&g_VBoxUsbMonGlobals, 0, sizeof (g_VBoxUsbMonGlobals));
1515
1516 VBOX_PNPHOOKSTUB_INIT(0);
1517 VBOX_PNPHOOKSTUB_INIT(1);
1518 VBOX_PNPHOOKSTUB_INIT(2);
1519 VBOX_PNPHOOKSTUB_INIT(3);
1520 VBOX_PNPHOOKSTUB_INIT(4);
1521 AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
1522
1523 KeInitializeEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, SynchronizationEvent, TRUE /* signaled */);
1524 IoInitializeRemoveLock(&g_VBoxUsbMonGlobals.RmLock, VBOXUSBMON_MEMTAG, 1, 100);
1525 UNICODE_STRING DevName;
1526 PDEVICE_OBJECT pDevObj;
1527 /* create the device */
1528 RtlInitUnicodeString(&DevName, USBMON_DEVICE_NAME_NT);
1529 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1530 if (NT_SUCCESS(Status))
1531 {
1532 Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
1533 if (NT_SUCCESS(Status))
1534 {
1535 UNICODE_STRING DosName;
1536 RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
1537 Status = IoCreateSymbolicLink(&DosName, &DevName);
1538 if (NT_SUCCESS(Status))
1539 {
1540 PVBOXUSBMONINS pDevExt = (PVBOXUSBMONINS)pDevObj->DeviceExtension;
1541 memset(pDevExt, 0, sizeof(*pDevExt));
1542
1543 pDrvObj->DriverUnload = VBoxUsbMonUnload;
1544 pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxUsbMonCreate;
1545 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxUsbMonClose;
1546 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxUsbMonDeviceControl;
1547 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxUsbMonInternalDeviceControl;
1548
1549 g_VBoxUsbMonGlobals.pDevObj = pDevObj;
1550 LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
1551 return STATUS_SUCCESS;
1552 }
1553 IoDeleteDevice(pDevObj);
1554 }
1555 IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1556 }
1557
1558 return Status;
1559}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette