VirtualBox

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

Last change on this file since 96954 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • 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 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBox USB Monitor
4 */
5
6/*
7 * Copyright (C) 2011-2022 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 pDumpData Pointer to data that will be added to the message (see 'details' tab).
807 *
808 * NB: We only use IoLogMsg.dll as the message file, limiting
809 * ErrCode to status codes and messages defined in ntiologc.h
810 */
811static void vboxUsbMonLogError(NTSTATUS ErrCode, NTSTATUS ReturnedStatus, ULONG uErrId, USHORT cbDumpData, PVOID pDumpData)
812{
813 PIO_ERROR_LOG_PACKET pErrEntry;
814
815
816 /* Truncate dumps that do not fit into IO_ERROR_LOG_PACKET. */
817 if (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData > ERROR_LOG_MAXIMUM_SIZE)
818 cbDumpData = ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
819
820 pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxUsbMonGlobals.pDevObj,
821 FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData);
822 if (pErrEntry)
823 {
824 uint8_t *pDump = (uint8_t *)pErrEntry->DumpData;
825 if (cbDumpData)
826 memcpy(pDump, pDumpData, cbDumpData);
827 pErrEntry->MajorFunctionCode = 0;
828 pErrEntry->RetryCount = 0;
829 pErrEntry->DumpDataSize = cbDumpData;
830 pErrEntry->NumberOfStrings = 0;
831 pErrEntry->StringOffset = 0;
832 pErrEntry->ErrorCode = ErrCode;
833 pErrEntry->UniqueErrorValue = uErrId;
834 pErrEntry->FinalStatus = ReturnedStatus;
835 pErrEntry->IoControlCode = 0;
836 IoWriteErrorLogEntry(pErrEntry);
837 }
838 else
839 {
840 LOG(("Failed to allocate error log entry (cb=%d)\n", FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData));
841 }
842}
843
844static DECLCALLBACK(BOOLEAN) vboxUsbMonHookDrvObjWalker(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext)
845{
846 RT_NOREF2(pHubFile, pvContext);
847 PDRIVER_OBJECT pDrvObj = pHubDo->DriverObject;
848
849 /* First we try to figure out if we are already hooked to this driver. */
850 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
851 if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
852 {
853 LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
854 /* We've already hooked to this one -- nothing to do. */
855 return TRUE;
856 }
857 /* We are not hooked yet, find an empty slot. */
858 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
859 {
860 if (!g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
861 {
862 /* Found an emtpy slot, use it. */
863 g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = pDrvObj;
864 ObReferenceObject(pDrvObj);
865 LOG(("pDrivers[%d] = %p, installing the hook...\n", i, pDrvObj));
866 VBoxUsbHookInit(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook,
867 pDrvObj,
868 IRP_MJ_PNP,
869 g_VBoxUsbMonGlobals.pDrivers[i].pfnHookStub);
870 VBoxUsbHookInstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
871 return TRUE; /* Must continue to find all drivers. */
872 }
873 if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
874 {
875 LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
876 /* We've already hooked to this one -- nothing to do. */
877 return TRUE;
878 }
879 }
880 /* No empty slots! No reason to continue. */
881 LOG(("No empty slots!\n"));
882 ANSI_STRING ansiDrvName;
883 NTSTATUS Status = RtlUnicodeStringToAnsiString(&ansiDrvName, &pDrvObj->DriverName, true);
884 if (Status != STATUS_SUCCESS)
885 {
886 ansiDrvName.Length = 0;
887 LOG(("RtlUnicodeStringToAnsiString failed with 0x%x", Status));
888 }
889 vboxUsbMonLogError(IO_ERR_INSUFFICIENT_RESOURCES, STATUS_SUCCESS, 1, ansiDrvName.Length, ansiDrvName.Buffer);
890 if (Status == STATUS_SUCCESS)
891 RtlFreeAnsiString(&ansiDrvName);
892 return FALSE;
893}
894
895/**
896 * Finds all USB drivers in the system and installs hooks if haven't done already.
897 */
898static NTSTATUS vboxUsbMonInstallAllHooks()
899{
900 vboxUsbMonHubDevWalk(vboxUsbMonHookDrvObjWalker, NULL);
901 return STATUS_SUCCESS;
902}
903
904static NTSTATUS vboxUsbMonHookCheckInit()
905{
906 static bool fIsHookInited = false;
907 if (fIsHookInited)
908 {
909 LOG(("hook inited already, success"));
910 return STATUS_SUCCESS;
911 }
912 return vboxUsbMonInstallAllHooks();
913}
914
915static NTSTATUS vboxUsbMonHookInstall()
916{
917 /* Nothing to do here as we have already installed all hooks in vboxUsbMonHookCheckInit(). */
918 return STATUS_SUCCESS;
919}
920
921static NTSTATUS vboxUsbMonHookUninstall()
922{
923#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
924 return STATUS_SUCCESS;
925#else
926 NTSTATUS Status = STATUS_SUCCESS;
927 for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
928 {
929 if (g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
930 {
931 Assert(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject == g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook.pDrvObj);
932 LOG(("Unhooking from %p...\n", g_VBoxUsbMonGlobals.pDrivers[i].DriverObject));
933 Status = VBoxUsbHookUninstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
934 if (!NT_SUCCESS(Status))
935 {
936 /*
937 * We failed to uninstall the hook, so we keep the reference to the driver
938 * in order to prevent another driver re-using this slot because we are
939 * going to mark this hook as fUninitFailed.
940 */
941 //AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
942 LOG(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed));
943 g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed = true;
944 }
945 else
946 {
947 /* The hook was removed successfully, now we can forget about this driver. */
948 ObDereferenceObject(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject);
949 g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = NULL;
950 }
951 }
952 }
953 return Status;
954#endif
955}
956
957
958static NTSTATUS vboxUsbMonCheckTermStuff()
959{
960 NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
961 Executive, KernelMode,
962 FALSE, /* BOOLEAN Alertable */
963 NULL /* IN PLARGE_INTEGER Timeout */
964 );
965 AssertRelease(Status == STATUS_SUCCESS);
966
967 do
968 {
969 if (--g_VBoxUsbMonGlobals.cOpens)
970 break;
971
972 Status = vboxUsbMonHookUninstall();
973
974 NTSTATUS tmpStatus = VBoxUsbFltTerm();
975 if (!NT_SUCCESS(tmpStatus))
976 {
977 /* this means a driver state is screwed up, KeBugCheckEx here ? */
978 AssertReleaseFailed();
979 }
980 } while (0);
981
982 KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
983
984 return Status;
985}
986
987static NTSTATUS vboxUsbMonCheckInitStuff()
988{
989 NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
990 Executive, KernelMode,
991 FALSE, /* BOOLEAN Alertable */
992 NULL /* IN PLARGE_INTEGER Timeout */
993 );
994 if (Status == STATUS_SUCCESS)
995 {
996 do
997 {
998 if (g_VBoxUsbMonGlobals.cOpens++)
999 {
1000 LOG(("opens: %d, success", g_VBoxUsbMonGlobals.cOpens));
1001 break;
1002 }
1003
1004 Status = VBoxUsbFltInit();
1005 if (NT_SUCCESS(Status))
1006 {
1007 Status = vboxUsbMonHookCheckInit();
1008 if (NT_SUCCESS(Status))
1009 {
1010 Status = vboxUsbMonHookInstall();
1011 if (NT_SUCCESS(Status))
1012 {
1013 Status = STATUS_SUCCESS;
1014 LOG(("succeded!!"));
1015 break;
1016 }
1017 else
1018 {
1019 WARN(("vboxUsbMonHookInstall failed, Status (0x%x)", Status));
1020 }
1021 }
1022 else
1023 {
1024 WARN(("vboxUsbMonHookCheckInit failed, Status (0x%x)", Status));
1025 }
1026 VBoxUsbFltTerm();
1027 }
1028 else
1029 {
1030 WARN(("VBoxUsbFltInit failed, Status (0x%x)", Status));
1031 }
1032
1033 --g_VBoxUsbMonGlobals.cOpens;
1034 Assert(!g_VBoxUsbMonGlobals.cOpens);
1035 } while (0);
1036
1037 KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
1038 }
1039 else
1040 {
1041 WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
1042 }
1043 return Status;
1044}
1045
1046static NTSTATUS vboxUsbMonContextCreate(PVBOXUSBMONCTX *ppCtx)
1047{
1048 NTSTATUS Status;
1049 *ppCtx = NULL;
1050 PVBOXUSBMONCTX pFileCtx = (PVBOXUSBMONCTX)VBoxUsbMonMemAllocZ(sizeof (*pFileCtx));
1051 if (pFileCtx)
1052 {
1053 Status = vboxUsbMonCheckInitStuff();
1054 if (Status == STATUS_SUCCESS)
1055 {
1056 Status = VBoxUsbFltCreate(&pFileCtx->FltCtx);
1057 if (Status == STATUS_SUCCESS)
1058 {
1059 *ppCtx = pFileCtx;
1060 LOG(("succeeded!!"));
1061 return STATUS_SUCCESS;
1062 }
1063 else
1064 {
1065 WARN(("VBoxUsbFltCreate failed"));
1066 }
1067 vboxUsbMonCheckTermStuff();
1068 }
1069 else
1070 {
1071 WARN(("vboxUsbMonCheckInitStuff failed"));
1072 }
1073 VBoxUsbMonMemFree(pFileCtx);
1074 }
1075 else
1076 {
1077 WARN(("VBoxUsbMonMemAllocZ failed"));
1078 Status = STATUS_NO_MEMORY;
1079 }
1080
1081 return Status;
1082}
1083
1084static NTSTATUS vboxUsbMonContextClose(PVBOXUSBMONCTX pCtx)
1085{
1086 NTSTATUS Status = VBoxUsbFltClose(&pCtx->FltCtx);
1087 if (Status == STATUS_SUCCESS)
1088 {
1089 Status = vboxUsbMonCheckTermStuff();
1090 Assert(Status == STATUS_SUCCESS);
1091 /* ignore the failure */
1092 VBoxUsbMonMemFree(pCtx);
1093 }
1094
1095 return Status;
1096}
1097
1098static NTSTATUS _stdcall VBoxUsbMonClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1099{
1100 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1101 PFILE_OBJECT pFileObj = pStack->FileObject;
1102 Assert(pFileObj->FsContext);
1103 PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
1104
1105 LOG(("VBoxUsbMonClose"));
1106
1107 NTSTATUS Status = vboxUsbMonContextClose(pCtx);
1108 if (Status != STATUS_SUCCESS)
1109 {
1110 WARN(("vboxUsbMonContextClose failed, Status (0x%x), prevent unload", Status));
1111 if (!InterlockedExchange(&g_VBoxUsbMonGlobals.ulPreventUnloadOn, 1))
1112 {
1113 LOGREL(("ulPreventUnloadOn not set, preventing unload"));
1114 UNICODE_STRING UniName;
1115 PDEVICE_OBJECT pTmpDevObj;
1116 RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
1117 NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
1118 AssertRelease(NT_SUCCESS(tmpStatus));
1119 AssertRelease(pTmpDevObj == pDevObj);
1120 }
1121 else
1122 {
1123 WARN(("ulPreventUnloadOn already set"));
1124 }
1125 LOG(("success!!"));
1126 Status = STATUS_SUCCESS;
1127 }
1128 pFileObj->FsContext = NULL;
1129 pIrp->IoStatus.Status = Status;
1130 pIrp->IoStatus.Information = 0;
1131 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1132 return Status;
1133}
1134
1135
1136static NTSTATUS _stdcall VBoxUsbMonCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1137{
1138 RT_NOREF1(pDevObj);
1139 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1140 PFILE_OBJECT pFileObj = pStack->FileObject;
1141 NTSTATUS Status;
1142
1143 LOG(("VBoxUSBMonCreate"));
1144
1145 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1146 {
1147 WARN(("trying to open as a directory"));
1148 pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
1149 pIrp->IoStatus.Information = 0;
1150 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1151 return STATUS_NOT_A_DIRECTORY;
1152 }
1153
1154 pFileObj->FsContext = NULL;
1155 PVBOXUSBMONCTX pCtx = NULL;
1156 Status = vboxUsbMonContextCreate(&pCtx);
1157 if (Status == STATUS_SUCCESS)
1158 {
1159 Assert(pCtx);
1160 pFileObj->FsContext = pCtx;
1161 }
1162 else
1163 {
1164 WARN(("vboxUsbMonContextCreate failed Status (0x%x)", Status));
1165 }
1166
1167 pIrp->IoStatus.Status = Status;
1168 pIrp->IoStatus.Information = 0;
1169 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1170 return Status;
1171}
1172
1173static int VBoxUsbMonFltAdd(PVBOXUSBMONCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
1174{
1175#ifdef VBOXUSBMON_DBG_NO_FILTERS
1176 static uintptr_t idDummy = 1;
1177 *pId = idDummy;
1178 ++idDummy;
1179 return VINF_SUCCESS;
1180#else
1181 int rc = VBoxUsbFltAdd(&pContext->FltCtx, pFilter, pId);
1182 return rc;
1183#endif
1184}
1185
1186static int VBoxUsbMonFltRemove(PVBOXUSBMONCTX pContext, uintptr_t uId)
1187{
1188#ifdef VBOXUSBMON_DBG_NO_FILTERS
1189 return VINF_SUCCESS;
1190#else
1191 int rc = VBoxUsbFltRemove(&pContext->FltCtx, uId);
1192 return rc;
1193#endif
1194}
1195
1196static NTSTATUS VBoxUsbMonRunFilters(PVBOXUSBMONCTX pContext)
1197{
1198 NTSTATUS Status = VBoxUsbFltFilterCheck(&pContext->FltCtx);
1199 return Status;
1200}
1201
1202static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
1203{
1204 NTSTATUS Status = VBoxUsbFltGetDevice(&pContext->FltCtx, hDevice, pInfo);
1205 return Status;
1206}
1207
1208static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer,
1209 ULONG cbOutBuffer, ULONG_PTR *pInfo)
1210{
1211 NTSTATUS Status = STATUS_SUCCESS;
1212 ULONG_PTR Info = 0;
1213 switch (Ctl)
1214 {
1215 case SUPUSBFLT_IOCTL_GET_VERSION:
1216 {
1217 PUSBSUP_VERSION pOut = (PUSBSUP_VERSION)pvBuffer;
1218
1219 LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
1220 if (!pvBuffer || cbOutBuffer != sizeof(*pOut) || cbInBuffer != 0)
1221 {
1222 WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1223 cbInBuffer, 0, cbOutBuffer, sizeof (*pOut)));
1224 Status = STATUS_INVALID_PARAMETER;
1225 break;
1226 }
1227 pOut->u32Major = USBMON_MAJOR_VERSION;
1228 pOut->u32Minor = USBMON_MINOR_VERSION;
1229 Info = sizeof (*pOut);
1230 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1231 break;
1232 }
1233
1234 case SUPUSBFLT_IOCTL_ADD_FILTER:
1235 {
1236 PUSBFILTER pFilter = (PUSBFILTER)pvBuffer;
1237 PUSBSUP_FLTADDOUT pOut = (PUSBSUP_FLTADDOUT)pvBuffer;
1238 uintptr_t uId = 0;
1239 int rc;
1240 if (RT_UNLIKELY(!pvBuffer || cbInBuffer != sizeof (*pFilter) || cbOutBuffer != sizeof (*pOut)))
1241 {
1242 WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1243 cbInBuffer, sizeof (*pFilter), cbOutBuffer, sizeof (*pOut)));
1244 Status = STATUS_INVALID_PARAMETER;
1245 break;
1246 }
1247
1248 rc = VBoxUsbMonFltAdd(pContext, pFilter, &uId);
1249 pOut->rc = rc;
1250 pOut->uId = uId;
1251 Info = sizeof (*pOut);
1252 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1253 break;
1254 }
1255
1256 case SUPUSBFLT_IOCTL_REMOVE_FILTER:
1257 {
1258 uintptr_t *pIn = (uintptr_t *)pvBuffer;
1259 int *pRc = (int *)pvBuffer;
1260
1261 if (!pvBuffer || cbInBuffer != sizeof (*pIn) || (cbOutBuffer && cbOutBuffer != sizeof (*pRc)))
1262 {
1263 WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1264 cbInBuffer, sizeof (*pIn), cbOutBuffer, 0));
1265 Status = STATUS_INVALID_PARAMETER;
1266 break;
1267 }
1268 LOG(("SUPUSBFLT_IOCTL_REMOVE_FILTER %x", *pIn));
1269 int rc = VBoxUsbMonFltRemove(pContext, *pIn);
1270 if (cbOutBuffer)
1271 {
1272 /* we've validated that already */
1273 Assert(cbOutBuffer == (ULONG)*pRc);
1274 *pRc = rc;
1275 Info = sizeof (*pRc);
1276 }
1277 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1278 break;
1279 }
1280
1281 case SUPUSBFLT_IOCTL_RUN_FILTERS:
1282 {
1283 if (pvBuffer || cbInBuffer || cbOutBuffer)
1284 {
1285 WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
1286 cbInBuffer, 0, cbOutBuffer, 0));
1287 Status = STATUS_INVALID_PARAMETER;
1288 break;
1289 }
1290 LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
1291 Status = VBoxUsbMonRunFilters(pContext);
1292 ASSERT_WARN(Status != STATUS_PENDING, ("status pending!"));
1293 break;
1294 }
1295
1296 case SUPUSBFLT_IOCTL_GET_DEVICE:
1297 {
1298 HVBOXUSBDEVUSR hDevice;
1299 PUSBSUP_GETDEV_MON pOut = (PUSBSUP_GETDEV_MON)pvBuffer;
1300 if (!pvBuffer || cbInBuffer != sizeof (hDevice) || cbOutBuffer < sizeof (*pOut))
1301 {
1302 WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes! cbIn=%d expected %d. cbOut=%d expected >= %d.",
1303 cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
1304 Status = STATUS_INVALID_PARAMETER;
1305 break;
1306 }
1307 hDevice = *(HVBOXUSBDEVUSR*)pvBuffer;
1308 if (!hDevice)
1309 {
1310 WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: hDevice is NULL!",
1311 cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
1312 Status = STATUS_INVALID_PARAMETER;
1313 break;
1314 }
1315
1316 Status = VBoxUsbMonGetDevice(pContext, hDevice, pOut);
1317
1318 if (NT_SUCCESS(Status))
1319 {
1320 Info = sizeof (*pOut);
1321 }
1322 else
1323 {
1324 WARN(("VBoxUsbMonGetDevice fail 0x%x", Status));
1325 }
1326 break;
1327 }
1328
1329 default:
1330 WARN(("Unknown code 0x%x", Ctl));
1331 Status = STATUS_INVALID_PARAMETER;
1332 break;
1333 }
1334
1335 ASSERT_WARN(Status != STATUS_PENDING, ("Status pending!"));
1336
1337 *pInfo = Info;
1338 return Status;
1339}
1340
1341static NTSTATUS _stdcall VBoxUsbMonDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1342{
1343 ULONG_PTR Info = 0;
1344 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1345 if (NT_SUCCESS(Status))
1346 {
1347 PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
1348 PFILE_OBJECT pFileObj = pSl->FileObject;
1349 Assert(pFileObj);
1350 Assert(pFileObj->FsContext);
1351 PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
1352 Assert(pCtx);
1353 Status = vboxUsbMonIoctlDispatch(pCtx,
1354 pSl->Parameters.DeviceIoControl.IoControlCode,
1355 pIrp->AssociatedIrp.SystemBuffer,
1356 pSl->Parameters.DeviceIoControl.InputBufferLength,
1357 pSl->Parameters.DeviceIoControl.OutputBufferLength,
1358 &Info);
1359 ASSERT_WARN(Status != STATUS_PENDING, ("Status pending"));
1360
1361 IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1362 }
1363 else
1364 {
1365 WARN(("IoAcquireRemoveLock failed Status (0x%x)", Status));
1366 }
1367
1368 pIrp->IoStatus.Information = Info;
1369 pIrp->IoStatus.Status = Status;
1370 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
1371 return Status;
1372}
1373
1374static NTSTATUS vboxUsbMonInternalIoctlDispatch(ULONG Ctl, PVOID pvBuffer, ULONG_PTR *pInfo)
1375{
1376 NTSTATUS Status = STATUS_SUCCESS;
1377 *pInfo = 0;
1378 switch (Ctl)
1379 {
1380 case VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION:
1381 {
1382 PVBOXUSBIDC_VERSION pOut = (PVBOXUSBIDC_VERSION)pvBuffer;
1383
1384 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
1385 if (!pvBuffer)
1386 {
1387 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
1388 Status = STATUS_INVALID_PARAMETER;
1389 break;
1390 }
1391 pOut->u32Major = VBOXUSBIDC_VERSION_MAJOR;
1392 pOut->u32Minor = VBOXUSBIDC_VERSION_MINOR;
1393 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1394 break;
1395 }
1396
1397 case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP:
1398 {
1399 PVBOXUSBIDC_PROXY_STARTUP pOut = (PVBOXUSBIDC_PROXY_STARTUP)pvBuffer;
1400
1401 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
1402 if (!pvBuffer)
1403 {
1404 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
1405 Status = STATUS_INVALID_PARAMETER;
1406 break;
1407 }
1408
1409 PDEVICE_OBJECT pDevObj = pOut->u.pPDO;
1410 pOut->u.hDev = VBoxUsbFltProxyStarted(pDevObj);
1411
1412 /* If we couldn't find the PDO in our list, that's a real problem and
1413 * the capturing will not really work. Log an error.
1414 */
1415 if (!pOut->u.hDev)
1416 vboxUsbMonLogError(IO_ERR_DRIVER_ERROR, STATUS_SUCCESS, 2, sizeof("INTERNAL_IOCTL_PROXY_STARTUP"), "INTERNAL_IOCTL_PROXY_STARTUP");
1417
1418 ASSERT_WARN(pOut->u.hDev, ("zero hDev"));
1419 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1420 break;
1421 }
1422
1423 case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN:
1424 {
1425 PVBOXUSBIDC_PROXY_TEARDOWN pOut = (PVBOXUSBIDC_PROXY_TEARDOWN)pvBuffer;
1426
1427 LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
1428 if (!pvBuffer)
1429 {
1430 WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
1431 Status = STATUS_INVALID_PARAMETER;
1432 break;
1433 }
1434
1435 ASSERT_WARN(pOut->hDev, ("zero hDev"));
1436 VBoxUsbFltProxyStopped(pOut->hDev);
1437 ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
1438 break;
1439 }
1440
1441 default:
1442 {
1443 WARN(("Unknown code 0x%x", Ctl));
1444 Status = STATUS_INVALID_PARAMETER;
1445 break;
1446 }
1447 }
1448
1449 return Status;
1450}
1451
1452static NTSTATUS _stdcall VBoxUsbMonInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1453{
1454 ULONG_PTR Info = 0;
1455 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1456 if (NT_SUCCESS(Status))
1457 {
1458 PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
1459 Status = vboxUsbMonInternalIoctlDispatch(pSl->Parameters.DeviceIoControl.IoControlCode,
1460 pSl->Parameters.Others.Argument1,
1461 &Info);
1462 Assert(Status != STATUS_PENDING);
1463
1464 IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
1465 }
1466
1467 pIrp->IoStatus.Information = Info;
1468 pIrp->IoStatus.Status = Status;
1469 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
1470 return Status;
1471}
1472
1473/**
1474 * Unload the driver.
1475 *
1476 * @param pDrvObj Driver object.
1477 */
1478static void _stdcall VBoxUsbMonUnload(PDRIVER_OBJECT pDrvObj)
1479{
1480 RT_NOREF1(pDrvObj);
1481 LOG(("VBoxUSBMonUnload pDrvObj (0x%p)", pDrvObj));
1482
1483 IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1484
1485 Assert(!g_VBoxUsbMonGlobals.cOpens);
1486
1487 UNICODE_STRING DosName;
1488 RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
1489 IoDeleteSymbolicLink(&DosName);
1490
1491 IoDeleteDevice(g_VBoxUsbMonGlobals.pDevObj);
1492
1493 /* cleanup the logger */
1494 PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
1495 if (pLogger)
1496 RTLogDestroy(pLogger);
1497 pLogger = RTLogSetDefaultInstance(NULL);
1498 if (pLogger)
1499 RTLogDestroy(pLogger);
1500}
1501
1502RT_C_DECLS_BEGIN
1503NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
1504RT_C_DECLS_END
1505
1506/**
1507 * Driver entry point.
1508 *
1509 * @returns appropriate status code.
1510 * @param pDrvObj Pointer to driver object.
1511 * @param pRegPath Registry base path.
1512 */
1513NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1514{
1515 RT_NOREF1(pRegPath);
1516#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
1517 RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
1518 RTLogDestinations(0, "debugger");
1519#endif
1520
1521 LOGREL(("Built %s %s", __DATE__, __TIME__));
1522
1523 memset (&g_VBoxUsbMonGlobals, 0, sizeof (g_VBoxUsbMonGlobals));
1524
1525 VBOX_PNPHOOKSTUB_INIT(0);
1526 VBOX_PNPHOOKSTUB_INIT(1);
1527 VBOX_PNPHOOKSTUB_INIT(2);
1528 VBOX_PNPHOOKSTUB_INIT(3);
1529 VBOX_PNPHOOKSTUB_INIT(4);
1530 AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
1531
1532 KeInitializeEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, SynchronizationEvent, TRUE /* signaled */);
1533 IoInitializeRemoveLock(&g_VBoxUsbMonGlobals.RmLock, VBOXUSBMON_MEMTAG, 1, 100);
1534 UNICODE_STRING DevName;
1535 PDEVICE_OBJECT pDevObj;
1536 /* create the device */
1537 RtlInitUnicodeString(&DevName, USBMON_DEVICE_NAME_NT);
1538 NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1539 if (NT_SUCCESS(Status))
1540 {
1541 Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
1542 if (NT_SUCCESS(Status))
1543 {
1544 UNICODE_STRING DosName;
1545 RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
1546 Status = IoCreateSymbolicLink(&DosName, &DevName);
1547 if (NT_SUCCESS(Status))
1548 {
1549 PVBOXUSBMONINS pDevExt = (PVBOXUSBMONINS)pDevObj->DeviceExtension;
1550 memset(pDevExt, 0, sizeof(*pDevExt));
1551
1552 pDrvObj->DriverUnload = VBoxUsbMonUnload;
1553 pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxUsbMonCreate;
1554 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxUsbMonClose;
1555 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxUsbMonDeviceControl;
1556 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxUsbMonInternalDeviceControl;
1557
1558 g_VBoxUsbMonGlobals.pDevObj = pDevObj;
1559 LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
1560 return STATUS_SUCCESS;
1561 }
1562 IoDeleteDevice(pDevObj);
1563 }
1564 IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
1565 }
1566
1567 return Status;
1568}
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