VirtualBox

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

Last change on this file since 99828 was 99828, checked in by vboxsync, 20 months ago

*: A bunch of adjustments that allows using /permissive- with Visual C++ (qt 6.x necessity).

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

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