summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB/win/mon
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/win/mon')
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf98
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp1763
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h75
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp218
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h96
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp1568
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h77
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc70
9 files changed, 3965 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
new file mode 100644
index 00000000..d54bb101
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
@@ -0,0 +1,98 @@
+; $Id: VBoxUSBMon.inf $
+;; @file
+; VBox USB Monitor driver - Installation file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=System
+ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+DriverPackageType=KernelService
+;cat CatalogFile=VBoxUSBMon.cat
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[DefaultInstall@DOT-NT-ARCH@]
+CopyFiles=VBoxUSBMon_CopyFiles
+
+[DefaultInstall@DOT-NT-ARCH@.Services]
+AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+;; Cannot get this to work (same works fine for VBoxDrv):
+;; [Manufacturer]
+;; %ORACLE%=VBoxUSBMon@COMMA-NT-ARCH@
+;;
+;; ; Models section (referenced by [Manufacturer]).
+;; [VBoxUSBMon@DOT-NT-ARCH@]
+;; %VBoxUSBMon.DRVDESC%=VBoxUSBMon_Install,root\VBoxUSBMon
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@]
+;; CopyFiles=VBoxUSBMon_CopyFiles
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@.Services]
+;; AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+[SourceDisksFiles]
+VBoxUSBMon.sys=1
+
+[SourceDisksNames]
+1=%VBoxUSBMon.DSKDESC%,
+
+[VBoxUSBMon_CopyFiles]
+VBoxUSBMon.sys
+
+[VBoxUSBMon_Service]
+DisplayName = %VBoxUSBMon.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+;StartType = 3 ; SERVICE_DEMAND_START
+StartType = 1 ; autostart to fix Vista problem
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxUSBMon.sys
+
+[VBoxUSBMon_AddEventLog]
+AddReg = VBoxUSBMon_AddEventLogRegistry
+
+[VBoxUSBMon_AddEventLogRegistry]
+HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+HKR,,TypesSupported,0x00010001,7
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxUSBMon.SVCDESC = "VirtualBox USB Monitor Service"
+VBoxUSBMon.DRVDESC = "VirtualBox USB Monitor Driver"
+VBoxUSBMon.DSKDESC = "VirtualBox USB Monitor Driver Installation Disk"
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
new file mode 100644
index 00000000..23d4d088
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
@@ -0,0 +1,1763 @@
+/* $Id: VBoxUsbFlt.cpp $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <iprt/assert.h>
+
+#pragma warning(disable : 4200)
+#include "usbdi.h"
+#pragma warning(default : 4200)
+#include "usbdlib.h"
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/usblib.h>
+#include <devguid.h>
+#include <devpkey.h>
+
+
+/* We should be including ntifs.h but that's not as easy as it sounds. */
+extern "C" {
+NTKERNELAPI PDEVICE_OBJECT IoGetDeviceAttachmentBaseRef(__in PDEVICE_OBJECT DeviceObject);
+}
+
+/*
+ * state transitions:
+ *
+ * (we are not filtering this device )
+ * ADDED --> UNCAPTURED ------------------------------->-
+ * | |
+ * | (we are filtering this device, | (the device is being
+ * | waiting for our device driver | re-plugged to perform
+ * | to pick it up) | capture-uncapture transition)
+ * |-> CAPTURING -------------------------------->|---> REPLUGGING -----
+ * ^ | (device driver picked | |
+ * | | up the device) | (remove cased | (device is removed
+ * | ->---> CAPTURED ---------------------->| by "real" removal | the device info is removed form the list)
+ * | | |------------------->->--> REMOVED
+ * | | |
+ * |-----------<->---> USED_BY_GUEST ------->|
+ * | |
+ * |------------------------<-
+ *
+ * NOTE: the order of enums DOES MATTER!!
+ * Do not blindly modify!! as the code assumes the state is ordered this way.
+ */
+typedef enum
+{
+ VBOXUSBFLT_DEVSTATE_UNKNOWN = 0,
+ VBOXUSBFLT_DEVSTATE_REMOVED,
+ VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ VBOXUSBFLT_DEVSTATE_ADDED,
+ VBOXUSBFLT_DEVSTATE_UNCAPTURED,
+ VBOXUSBFLT_DEVSTATE_CAPTURING,
+ VBOXUSBFLT_DEVSTATE_CAPTURED,
+ VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ VBOXUSBFLT_DEVSTATE_32BIT_HACK = 0x7fffffff
+} VBOXUSBFLT_DEVSTATE;
+
+typedef struct VBOXUSBFLT_DEVICE
+{
+ LIST_ENTRY GlobalLe;
+ /* auxiliary list to be used for gathering devices to be re-plugged
+ * only thread that puts the device to the REPLUGGING state can use this list */
+ LIST_ENTRY RepluggingLe;
+ /* Owning session. Each matched device has an owning session. */
+ struct VBOXUSBFLTCTX *pOwner;
+ /* filter id - if NULL AND device has an owner - the filter is destroyed */
+ uintptr_t uFltId;
+ /* true iff device is filtered with a one-shot filter */
+ bool fIsFilterOneShot;
+ /* true if descriptors could not be read and only inferred from PnP Manager data */
+ bool fInferredDesc;
+ /* The device state. If the non-owner session is requesting the state while the device is grabbed,
+ * the USBDEVICESTATE_USED_BY_HOST is returned. */
+ VBOXUSBFLT_DEVSTATE enmState;
+ volatile uint32_t cRefs;
+ PDEVICE_OBJECT Pdo;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint16_t bPort;
+ uint8_t bClass;
+ uint8_t bSubClass;
+ uint8_t bProtocol;
+ char szSerial[MAX_USB_SERIAL_STRING];
+ char szMfgName[MAX_USB_SERIAL_STRING];
+ char szProduct[MAX_USB_SERIAL_STRING];
+ WCHAR szLocationPath[768];
+#if 0
+ char szDrvKeyName[512];
+ BOOLEAN fHighSpeed;
+#endif
+} VBOXUSBFLT_DEVICE, *PVBOXUSBFLT_DEVICE;
+
+#define PVBOXUSBFLT_DEVICE_FROM_LE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, GlobalLe) ) )
+#define PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, RepluggingLe) ) )
+#define PVBOXUSBFLTCTX_FROM_LE(_pLe) ( (PVBOXUSBFLTCTX)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLTCTX, ListEntry) ) )
+
+typedef struct VBOXUSBFLT_LOCK
+{
+ KSPIN_LOCK Lock;
+ KIRQL OldIrql;
+} VBOXUSBFLT_LOCK, *PVBOXUSBFLT_LOCK;
+
+#define VBOXUSBFLT_LOCK_INIT() \
+ KeInitializeSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock)
+#define VBOXUSBFLT_LOCK_TERM() do { } while (0)
+#define VBOXUSBFLT_LOCK_ACQUIRE() \
+ KeAcquireSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, &g_VBoxUsbFltGlobals.Lock.OldIrql);
+#define VBOXUSBFLT_LOCK_RELEASE() \
+ KeReleaseSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, g_VBoxUsbFltGlobals.Lock.OldIrql);
+
+
+typedef struct VBOXUSBFLT_BLDEV
+{
+ LIST_ENTRY ListEntry;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+} VBOXUSBFLT_BLDEV, *PVBOXUSBFLT_BLDEV;
+
+#define PVBOXUSBFLT_BLDEV_FROM_LE(_pLe) ( (PVBOXUSBFLT_BLDEV)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_BLDEV, ListEntry) ) )
+
+typedef struct VBOXUSBFLTGLOBALS
+{
+ LIST_ENTRY DeviceList;
+ LIST_ENTRY ContextList;
+ /* devices known to misbehave */
+ LIST_ENTRY BlackDeviceList;
+ VBOXUSBFLT_LOCK Lock;
+ /** Flag whether to force replugging a device we can't query descirptors from.
+ * Short term workaround for @bugref{9479}. */
+ ULONG dwForceReplugWhenDevPopulateFails;
+} VBOXUSBFLTGLOBALS, *PVBOXUSBFLTGLOBALS;
+static VBOXUSBFLTGLOBALS g_VBoxUsbFltGlobals;
+
+static bool vboxUsbFltBlDevMatchLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_BLDEV pDev = PVBOXUSBFLT_BLDEV_FROM_LE(pEntry);
+ if (pDev->idVendor != idVendor)
+ continue;
+ if (pDev->idProduct != idProduct)
+ continue;
+ if (pDev->bcdDevice != bcdDevice)
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
+static NTSTATUS vboxUsbFltBlDevAddLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ if (vboxUsbFltBlDevMatchLocked(idVendor, idProduct, bcdDevice))
+ return STATUS_SUCCESS;
+ PVBOXUSBFLT_BLDEV pDev = (PVBOXUSBFLT_BLDEV)VBoxUsbMonMemAllocZ(sizeof (*pDev));
+ if (!pDev)
+ {
+ AssertFailed();
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pDev->idVendor = idVendor;
+ pDev->idProduct = idProduct;
+ pDev->bcdDevice = bcdDevice;
+ InsertHeadList(&g_VBoxUsbFltGlobals.BlackDeviceList, &pDev->ListEntry);
+ return STATUS_SUCCESS;
+}
+
+static void vboxUsbFltBlDevClearLocked()
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ VBoxUsbMonMemFree(pEntry);
+ }
+}
+
+static void vboxUsbFltBlDevPopulateWithKnownLocked()
+{
+ /* this one halts when trying to get string descriptors from it */
+ vboxUsbFltBlDevAddLocked(0x5ac, 0x921c, 0x115);
+}
+
+
+DECLINLINE(void) vboxUsbFltDevRetain(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(pDevice->cRefs);
+ ASMAtomicIncU32(&pDevice->cRefs);
+}
+
+static void vboxUsbFltDevDestroy(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(!pDevice->cRefs);
+ Assert(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED);
+ VBoxUsbMonMemFree(pDevice);
+}
+
+DECLINLINE(void) vboxUsbFltDevRelease(PVBOXUSBFLT_DEVICE pDevice)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pDevice->cRefs);
+ Assert(cRefs < UINT32_MAX/2);
+ if (!cRefs)
+ {
+ vboxUsbFltDevDestroy(pDevice);
+ }
+}
+
+static void vboxUsbFltDevOwnerSetLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ ASSERT_WARN(!pDevice->pOwner, ("device 0x%p has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ++pContext->cActiveFilters;
+ pDevice->pOwner = pContext;
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+}
+
+static void vboxUsbFltDevOwnerClearLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ ASSERT_WARN(pDevice->pOwner, ("no owner for device 0x%p", pDevice));
+ --pDevice->pOwner->cActiveFilters;
+ ASSERT_WARN(pDevice->pOwner->cActiveFilters < UINT32_MAX/2, ("cActiveFilters (%d)", pDevice->pOwner->cActiveFilters));
+ pDevice->pOwner = NULL;
+ pDevice->uFltId = 0;
+}
+
+static void vboxUsbFltDevOwnerUpdateLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ if (pDevice->pOwner != pContext)
+ {
+ if (pDevice->pOwner)
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+ if (pContext)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pContext, uFltId, fIsOneShot);
+ }
+ else if (pContext)
+ {
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+ }
+}
+
+static PVBOXUSBFLT_DEVICE vboxUsbFltDevGetLocked(PDEVICE_OBJECT pPdo)
+{
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ for (PLIST_ENTRY pEntry2 = pEntry->Flink;
+ pEntry2 != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry2 = pEntry2->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice2 = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry2);
+ ASSERT_WARN( pDevice->idVendor != pDevice2->idVendor
+ || pDevice->idProduct != pDevice2->idProduct
+ || pDevice->bcdDevice != pDevice2->bcdDevice, ("duplicate devices in a list!!"));
+ }
+ }
+#endif
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ ASSERT_WARN( pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ ("Invalid device state(%d) for device(0x%p) PDO(0x%p)", pDevice->enmState, pDevice, pDevice->Pdo));
+ if (pDevice->Pdo == pPdo)
+ return pDevice;
+ }
+ return NULL;
+}
+
+static NTSTATUS vboxUsbFltPdoReplug(PDEVICE_OBJECT pDo)
+{
+ LOG(("Replugging PDO(0x%p)", pDo));
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_CYCLE_PORT, NULL, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("replugging PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Replugging PDO(0x%p) done with Status(0x%x)", pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCanBeCaptured(PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (pDevice->bClass == USB_DEVICE_CLASS_HUB)
+ {
+ LOG(("device (0x%p), pdo (0x%p) is a hub, can not be captured", pDevice, pDevice->Pdo));
+ return false;
+ }
+ return true;
+}
+
+static PVBOXUSBFLTCTX vboxUsbFltDevMatchLocked(PVBOXUSBFLT_DEVICE pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
+{
+ *puId = 0;
+ *pfFilter = false;
+ *pfIsOneShot = false;
+ if (!vboxUsbFltDevCanBeCaptured(pDevice))
+ {
+ LOG(("vboxUsbFltDevCanBeCaptured returned false"));
+ return NULL;
+ }
+
+ USBFILTER DevFlt;
+ USBFilterInit(&DevFlt, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_VENDOR_ID, pDevice->idVendor, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PRODUCT_ID, pDevice->idProduct, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_REV, pDevice->bcdDevice, true);
+
+ /* If we could not read a string descriptor, don't set the filter item at all. */
+ if (pDevice->szMfgName[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_MANUFACTURER_STR, pDevice->szMfgName, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szProduct[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_PRODUCT_STR, pDevice->szProduct, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szSerial[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_SERIAL_NUMBER_STR, pDevice->szSerial, true /*fMustBePresent*/, true /*fPurge*/);
+
+ /* If device descriptor had to be inferred from PnP Manager data, the class/subclass/protocol may be wrong.
+ * When Windows reports CompatibleIDs 'USB\Class_03&SubClass_00&Prot_00', the device descriptor might be
+ * reporting class 3 (HID), *or* the device descriptor might be reporting class 0 (specified by interface)
+ * and the device's interface reporting class 3. Ignore the class/subclass/protocol in such case, since
+ * we are more or less guaranteed to rely on VID/PID anyway.
+ * See @bugref{9479}.
+ */
+ if (pDevice->fInferredDesc)
+ {
+ LOG(("Device descriptor was not read, only inferred; ignoring class/subclass/protocol!"));
+ }
+ else
+ {
+ LOG(("Setting filter class/subclass/protocol %02X/%02X/%02X\n", pDevice->bClass, pDevice->bSubClass, pDevice->bProtocol));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_CLASS, pDevice->bClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_SUB_CLASS, pDevice->bSubClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_PROTOCOL, pDevice->bProtocol, true);
+ }
+
+ /* If the port number looks valid, add it to the filter. */
+ if (pDevice->bPort)
+ {
+ LOG(("Setting filter port %04X\n", pDevice->bPort));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PORT, pDevice->bPort, true);
+ }
+ else
+ LOG(("Port number not known, ignoring!"));
+
+ /* Run filters on the thing. */
+ PVBOXUSBFLTCTX pOwner = VBoxUSBFilterMatchEx(&DevFlt, puId, fRemoveFltIfOneShot, pfFilter, pfIsOneShot);
+ USBFilterDelete(&DevFlt);
+ return pOwner;
+}
+
+static void vboxUsbFltDevStateMarkReplugLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, NULL, 0, false);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REPLUGGING;
+}
+
+static bool vboxUsbFltDevStateIsNotFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+}
+
+static bool vboxUsbFltDevStateIsFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+static uint16_t vboxUsbParseHexNumU16(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (12 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static uint8_t vboxUsbParseHexNumU8(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (4 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static bool vboxUsbParseHardwareID(WCHAR *pchIdStr, uint16_t *pVid, uint16_t *pPid, uint16_t *pRev)
+{
+#define VID_PREFIX L"USB\\VID_"
+#define PID_PREFIX L"&PID_"
+#define REV_PREFIX L"&REV_"
+
+ *pVid = *pPid = *pRev = 0xFFFF;
+
+ /* The Hardware ID is in the format USB\VID_xxxx&PID_xxxx&REV_xxxx, with 'xxxx'
+ * being 16-bit hexadecimal numbers. The string is coming from the
+ * Windows PnP manager so OEMs should have no opportunity to mess it up.
+ */
+
+ if (wcsncmp(pchIdStr, VID_PREFIX, wcslen(VID_PREFIX)))
+ return false;
+ /* Point to the start of the vendor ID number and parse it. */
+ pchIdStr += wcslen(VID_PREFIX);
+ *pVid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PID_PREFIX, wcslen(PID_PREFIX)))
+ return false;
+ /* Point to the start of the product ID number and parse it. */
+ pchIdStr += wcslen(PID_PREFIX);
+ *pPid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ /* The revision might not be there; the Windows documentation is not
+ * entirely clear if it will be always present for USB devices or not.
+ * If it's not there, still consider this a success. */
+ if (wcsncmp(pchIdStr, REV_PREFIX, wcslen(REV_PREFIX)))
+ return true;
+
+ /* Point to the start of the revision number and parse it. */
+ pchIdStr += wcslen(REV_PREFIX);
+ *pRev = vboxUsbParseHexNumU16(&pchIdStr);
+
+ return true;
+#undef VID_PREFIX
+#undef PID_PREFIX
+#undef REV_PREFIX
+}
+
+static bool vboxUsbParseCompatibleIDs(WCHAR *pchIdStr, uint8_t *pClass, uint8_t *pSubClass, uint8_t *pProt)
+{
+#define CLS_PREFIX L"USB\\Class_"
+#define SUB_PREFIX L"&SubClass_"
+#define PRO_PREFIX L"&Prot_"
+
+ *pClass = *pSubClass = *pProt = 0xFF;
+
+ /* The Compatible IDs string is in the format USB\Class_xx&SubClass_xx&Prot_xx,
+ * with 'xx' being 8-bit hexadecimal numbers. Since this string is provided by the
+ * PnP manager and USB devices always report these as part of the basic USB device
+ * descriptor, we assume all three must be present.
+ */
+
+ if (wcsncmp(pchIdStr, CLS_PREFIX, wcslen(CLS_PREFIX)))
+ return false;
+ /* Point to the start of the device class and parse it. */
+ pchIdStr += wcslen(CLS_PREFIX);
+ *pClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, SUB_PREFIX, wcslen(SUB_PREFIX)))
+ return false;
+
+ /* Point to the start of the subclass and parse it. */
+ pchIdStr += wcslen(SUB_PREFIX);
+ *pSubClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PRO_PREFIX, wcslen(PRO_PREFIX)))
+ return false;
+
+ /* Point to the start of the protocol and parse it. */
+ pchIdStr += wcslen(PRO_PREFIX);
+ *pProt = vboxUsbParseHexNumU8(&pchIdStr);
+
+ return true;
+#undef CLS_PREFIX
+#undef SUB_PREFIX
+#undef PRO_PREFIX
+}
+
+#define VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS 10000
+
+static NTSTATUS vboxUsbFltDevPopulate(PVBOXUSBFLT_DEVICE pDevice, PDEVICE_OBJECT pDo /*, BOOLEAN bPopulateNonFilterProps*/)
+{
+ NTSTATUS Status;
+ USB_TOPOLOGY_ADDRESS TopoAddr;
+ PUSB_DEVICE_DESCRIPTOR pDevDr = 0;
+ ULONG ulResultLen;
+ DEVPROPTYPE type;
+ WCHAR wchPropBuf[256];
+ uint16_t port;
+ bool rc;
+
+ pDevice->Pdo = pDo;
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p)", pDevice, pDo));
+
+ pDevDr = (PUSB_DEVICE_DESCRIPTOR)VBoxUsbMonMemAllocZ(sizeof(*pDevDr));
+ if (pDevDr == NULL)
+ {
+ WARN(("Failed to alloc mem for urb"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ do
+ {
+ pDevice->fInferredDesc = false;
+ Status = VBoxUsbToolGetDescriptor(pDo, pDevDr, sizeof(*pDevDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ WARN(("getting device descriptor failed, Status (0x%x); falling back to IoGetDeviceProperty", Status));
+
+ /* Try falling back to IoGetDevicePropertyData. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_HardwareIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This just isn't our day. We have no idea what the device is. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_HardwareIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseHardwareID(wchPropBuf, &vid, &pid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ /* Now grab the Compatible IDs to get the class/subclass/protocol. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_CompatibleIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We really kind of need these. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_CompatibleIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseCompatibleIDs(wchPropBuf, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ LOG(("Parsed HardwareID: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+ if (vid == 0xFFFF || pid == 0xFFFF)
+ break;
+
+ LOG(("Successfully fell back to IoGetDeviceProperty result"));
+ pDevDr->idVendor = vid;
+ pDevDr->idProduct = pid;
+ pDevDr->bcdDevice = rev;
+ pDevDr->bDeviceClass = cls;
+ pDevDr->bDeviceSubClass = sub;
+ pDevDr->bDeviceProtocol = prt;
+
+ /* The USB device class/subclass/protocol may not be accurate. We have to be careful when comparing
+ * and not take mismatches too seriously.
+ */
+ pDevice->fInferredDesc = true;
+ }
+
+ /* Query the location path. The path is purely a function of the physical device location
+ * and does not change if the device changes, and also does not change depending on
+ * whether the device is captured or not.
+ * NB: We ignore any additional strings and only look at the first one.
+ */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_LocationPaths, LOCALE_NEUTRAL, 0, sizeof(pDevice->szLocationPath), pDevice->szLocationPath, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We do need this, but not critically. On Windows 7, we may get STATUS_OBJECT_NAME_NOT_FOUND. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_LocationPaths, Status (0x%x)", Status));
+ }
+ else
+ {
+ LOG_STRW(pDevice->szLocationPath);
+ }
+
+ // Disabled, but could be used as a fallback instead of IoGetDevicePropertyData; it should work even
+ // when this code is entered from the PnP IRP processing path.
+#if 0
+ {
+ HUB_DEVICE_CONFIG_INFO HubInfo;
+
+ memset(&HubInfo, 0, sizeof(HubInfo));
+ HubInfo.Version = 1;
+ HubInfo.Length = sizeof(HubInfo);
+
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_DEVICE_CONFIG_INFO, &HubInfo, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_DEVICE_CONFIG_INFO for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying hub device config info for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ LOG(("Hub flags: %X\n", HubInfo.HubFlags));
+ LOG_STRW(HubInfo.HardwareIds.Buffer);
+ LOG_STRW(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ LOG_STRW(HubInfo.DeviceDescription.Buffer);
+
+ rc = vboxUsbParseHardwareID(HubInfo.HardwareIds.Buffer, &pid, &vid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ }
+
+ /* The CompatibleID the IOCTL gives is not always the same as what the PnP Manager uses
+ * (thanks, Microsoft). It might look like "USB\DevClass_00&SubClass_00&Prot_00" or like
+ * "USB\USB30_HUB". In such cases, we must consider the class/subclass/protocol
+ * information simply unavailable.
+ */
+ rc = vboxUsbParseCompatibleIDs(HubInfo.CompatibleIds.Buffer, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This is unfortunate but not fatal. */
+ WARN(("Failed to parse Compatible ID"));
+ }
+ LOG(("Parsed HardwareID from IOCTL: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+
+ ExFreePool(HubInfo.HardwareIds.Buffer);
+ ExFreePool(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ ExFreePool(HubInfo.DeviceDescription.Buffer);
+ }
+ }
+#endif
+
+ /* Query the topology address from the hub driver. This is not trivial to translate to the location
+ * path, but at least we can get the port number this way.
+ */
+ memset(&TopoAddr, 0, sizeof(TopoAddr));
+ Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_TOPOLOGY_ADDRESS, &TopoAddr, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_TOPOLOGY_ADDRESS for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying topology address for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ port = 0;
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t *pPort = &TopoAddr.RootHubPortNumber;
+
+ /* The last non-zero port number is the one we're looking for. It might be on the
+ * root hub directly, or on some downstream hub.
+ */
+ for (int i = 0; i < RT_ELEMENTS(TopoAddr.HubPortNumber) + 1; ++i) {
+ if (*pPort)
+ port = *pPort;
+ pPort++;
+ }
+ LOG(("PCI bus/dev/fn: %02X:%02X:%02X, parsed port: %u\n", TopoAddr.PciBusNumber, TopoAddr.PciDeviceNumber, TopoAddr.PciFunctionNumber, port));
+ LOG(("RH port: %u, hub ports: %u/%u/%u/%u/%u/%u\n", TopoAddr.RootHubPortNumber, TopoAddr.HubPortNumber[0],
+ TopoAddr.HubPortNumber[1], TopoAddr.HubPortNumber[2], TopoAddr.HubPortNumber[3], TopoAddr.HubPortNumber[4], TopoAddr.HubPortNumber[5]));
+
+ /* In the extremely unlikely case that the port number does not fit into 8 bits, force
+ * it to zero to indicate that we can't use it.
+ */
+ if (port > 255)
+ port = 0;
+ }
+
+ if (vboxUsbFltBlDevMatchLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice))
+ {
+ WARN(("found a known black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ LOG(("Device pid=%x vid=%x rev=%x port=%x", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice, port));
+ pDevice->bPort = port;
+ pDevice->idVendor = pDevDr->idVendor;
+ pDevice->idProduct = pDevDr->idProduct;
+ pDevice->bcdDevice = pDevDr->bcdDevice;
+ pDevice->bClass = pDevDr->bDeviceClass;
+ pDevice->bSubClass = pDevDr->bDeviceSubClass;
+ pDevice->bProtocol = pDevDr->bDeviceProtocol;
+ pDevice->szSerial[0] = 0;
+ pDevice->szMfgName[0] = 0;
+ pDevice->szProduct[0] = 0;
+
+ /* If there are no strings, don't even try to get any string descriptors. */
+ if (pDevDr->iSerialNumber || pDevDr->iManufacturer || pDevDr->iProduct)
+ {
+ int langId;
+
+ Status = VBoxUsbToolGetLangID(pDo, &langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading language ID failed"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szSerial, sizeof (pDevice->szSerial), pDevDr->iSerialNumber, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading serial number failed"));
+ ASSERT_WARN(pDevice->szSerial[0] == '\0', ("serial is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iManufacturer)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szMfgName, sizeof (pDevice->szMfgName), pDevDr->iManufacturer, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading manufacturer name failed"));
+ ASSERT_WARN(pDevice->szMfgName[0] == '\0', ("szMfgName is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iProduct)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szProduct, sizeof (pDevice->szProduct), pDevDr->iProduct, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading product name failed"));
+ ASSERT_WARN(pDevice->szProduct[0] == '\0', ("szProduct is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ LOG((": strings: '%s':'%s':'%s' (lang ID %x)",
+ pDevice->szMfgName, pDevice->szProduct, pDevice->szSerial, langId));
+ }
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Succeeded", pDevice, pDo));
+ Status = STATUS_SUCCESS;
+ } while (0);
+
+ VBoxUsbMonMemFree(pDevDr);
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Done, Status (0x%x)", pDevice, pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCheckReplugLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext)
+{
+ ASSERT_WARN(pContext, ("context is NULL!"));
+
+ LOG(("Current context is (0x%p)", pContext));
+ LOG(("Current Device owner is (0x%p)", pDevice->pOwner));
+
+ /* check if device is already replugging */
+ if (pDevice->enmState <= VBOXUSBFLT_DEVSTATE_ADDED)
+ {
+ LOG(("Device (0x%p) is already replugging, return..", pDevice));
+ /* it is, do nothing */
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ ("Device (0x%p) state is NOT REPLUGGING (%d)", pDevice, pDevice->enmState));
+ return false;
+ }
+
+ if (pDevice->pOwner && pContext != pDevice->pOwner)
+ {
+ LOG(("Device (0x%p) is owned by another context(0x%p), current is(0x%p)", pDevice, pDevice->pOwner, pContext));
+ /* this device is owned by another context, we're not allowed to do anything */
+ return false;
+ }
+
+ uintptr_t uId = 0;
+ bool bNeedReplug = false;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pNewOwner = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), NewOwner(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pNewOwner, (int)fFilter, (int)fIsOneShot));
+ if (pDevice->pOwner && pNewOwner && pDevice->pOwner != pNewOwner)
+ {
+ LOG(("Matching: Device (0x%p) is requested another owner(0x%p), current is(0x%p)", pDevice, pNewOwner, pDevice->pOwner));
+ /* the device is owned by another owner, we can not change the owner here */
+ return false;
+ }
+
+ if (!fFilter)
+ {
+ LOG(("Matching: Device (0x%p) should NOT be filtered", pDevice));
+ /* the device should NOT be filtered, check the current state */
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) is NOT filtered", pDevice));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ ASSERT_WARN(pNewOwner, ("no new owner"));
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ if (!pDevice->pOwner)
+ {
+ LOG(("Matching: updating the one-shot owner to (0x%p), fltId(0x%p)", pNewOwner, uId));
+ /* update owner for one-shot if the owner is changed (i.e. assigned) */
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, true);
+ }
+ else
+ {
+ LOG(("Matching: device already has owner (0x%p) assigned", pDevice->pOwner));
+ }
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), newOwner(0x%p)", uId, pNewOwner));
+ if (pNewOwner)
+ {
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, false);
+ }
+ }
+ }
+ else
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is currently filtered, we should release it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be released bue to a one-shot filter
+ * or
+ * 3. it is NOT grabbed by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ LOG(("Matching: Need replug"));
+ bNeedReplug = true;
+ }
+ }
+ }
+ else
+ {
+ LOG(("Matching: Device (0x%p) SHOULD be filtered", pDevice));
+ /* the device should be filtered, check the current state */
+ ASSERT_WARN(uId, ("zero uid"));
+ ASSERT_WARN(pNewOwner, ("zero pNewOwner"));
+ if (vboxUsbFltDevStateIsFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is filtered */
+ if (pNewOwner == pDevice->pOwner)
+ {
+ LOG(("Device owner match"));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), Owner(0x%p)", uId, pDevice->pOwner));
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pDevice->pOwner, uId, false);
+ }
+ }
+ else
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("device should NOT have owner"));
+ LOG(("Matching: Need replug"));
+ /* the device needs to be filtered, but the owner changes, replug needed */
+ bNeedReplug = true;
+ }
+ }
+ else
+ {
+ /* the device is currently NOT filtered,
+ * we should replug it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be captured due to a one-shot filter
+ * or
+ * 3. it is NOT released by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ bNeedReplug = true;
+ LOG(("Matching: Need replug"));
+ }
+ }
+ }
+
+ if (bNeedReplug)
+ {
+ LOG(("Matching: Device needs replugging, marking as such"));
+ vboxUsbFltDevStateMarkReplugLocked(pDevice);
+ }
+ else
+ {
+ LOG(("Matching: Device does NOT need replugging"));
+ }
+
+ return bNeedReplug;
+}
+
+static void vboxUsbFltReplugList(PLIST_ENTRY pList)
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = pList->Flink;
+ pEntry != pList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(pEntry);
+ LOG(("replugging matched PDO(0x%p), pDevice(0x%p)", pDevice->Pdo, pDevice));
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED,
+ ("invalid state(0x%x) for device(0x%p)", pDevice->enmState, pDevice));
+
+ vboxUsbFltPdoReplug(pDevice->Pdo);
+ ObDereferenceObject(pDevice->Pdo);
+ vboxUsbFltDevRelease(pDevice);
+ }
+}
+
+typedef struct VBOXUSBFLTCHECKWALKER
+{
+ PVBOXUSBFLTCTX pContext;
+} VBOXUSBFLTCHECKWALKER, *PVBOXUSBFLTCHECKWALKER;
+
+static DECLCALLBACK(BOOLEAN) vboxUsbFltFilterCheckWalker(PFILE_OBJECT pHubFile,
+ PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ PVBOXUSBFLTCHECKWALKER pData = (PVBOXUSBFLTCHECKWALKER)pvContext;
+ PVBOXUSBFLTCTX pContext = pData->pContext;
+
+ LOG(("Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ PDEVICE_RELATIONS pDevRelations = NULL;
+
+ NTSTATUS Status = VBoxUsbMonQueryBusRelations(pHubDo, pHubFile, &pDevRelations);
+ if (Status == STATUS_SUCCESS && pDevRelations)
+ {
+ ULONG cReplugPdos = pDevRelations->Count;
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ PDEVICE_OBJECT pDevObj;
+
+ /* Grab the PDO+reference. We won't need the upper layer device object
+ * anymore, so dereference that right here, and drop the PDO ref later.
+ */
+ pDevObj = IoGetDeviceAttachmentBaseRef(pDevRelations->Objects[k]);
+ LOG(("DevObj=%p, PDO=%p\n", pDevRelations->Objects[k], pDevObj));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ pDevRelations->Objects[k] = pDevObj;
+
+ LOG(("Found existing USB PDO 0x%p", pDevObj));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pDevObj);
+ if (pDevice)
+ {
+ LOG(("Found existing device info (0x%p) for PDO 0x%p", pDevice, pDevObj));
+ bool bReplug = vboxUsbFltDevCheckReplugLocked(pDevice, pContext);
+ if (bReplug)
+ {
+ LOG(("Replug needed for device (0x%p)", pDevice));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ vboxUsbFltDevRetain(pDevice);
+ /* do not dereference object since we will use it later */
+ }
+ else
+ {
+ LOG(("Replug NOT needed for device (0x%p)", pDevice));
+ ObDereferenceObject(pDevObj);
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos(%d) state broken", cReplugPdos));
+ continue;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("NO device info found for PDO 0x%p", pDevObj));
+ VBOXUSBFLT_DEVICE Device;
+ Status = vboxUsbFltDevPopulate(&Device, pDevObj /*, FALSE /* only need filter properties */);
+ if (NT_SUCCESS(Status))
+ {
+ uintptr_t uId = 0;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLTCTX pCtx = vboxUsbFltDevMatchLocked(&Device, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ VBOXUSBFLT_LOCK_RELEASE();
+ NOREF(pCtx);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Matching: This device SHOULD be filtered"));
+ /* this device needs to be filtered, but it's not,
+ * leave the PDO in array to issue a replug request for it
+ * later on */
+ continue;
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbFltDevPopulate for PDO 0x%p failed with Status 0x%x", pDevObj, Status));
+ if ( Status == STATUS_CANCELLED
+ && g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ {
+ /*
+ * This can happen if the device got suspended and is in D3 state where we can't query any strings.
+ * There is no known way to set the power state of the device, especially if there is no driver attached yet.
+ * The sledgehammer approach is to just replug the device to force it out of suspend, see bugref @{9479}.
+ */
+ continue;
+ }
+ }
+
+ LOG(("Matching: This device should NOT be filtered"));
+ /* this device should not be filtered, and it's not */
+ ObDereferenceObject(pDevObj);
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos is %d", cReplugPdos));
+ }
+
+ LOG(("(%d) non-matched PDOs to be replugged", cReplugPdos));
+
+ if (cReplugPdos)
+ {
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ if (!pDevRelations->Objects[k])
+ continue;
+
+ Status = vboxUsbFltPdoReplug(pDevRelations->Objects[k]);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("vboxUsbFltPdoReplug failed! Status(0x%x)", Status));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ if (!--cReplugPdos)
+ break;
+ }
+
+ ASSERT_WARN(!cReplugPdos, ("cReplugPdos reached zero!"));
+ }
+
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ ExFreePool(pDevRelations);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonQueryBusRelations failed for hub DO(0x%p), Status(0x%x), pDevRelations(0x%p)",
+ pHubDo, Status, pDevRelations));
+ }
+
+ LOG(("Done Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+
+ return TRUE;
+}
+
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext)
+{
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ LOG(("Running filters, Context (0x%p)..", pContext));
+
+ VBOXUSBFLTCHECKWALKER Data;
+ Data.pContext = pContext;
+ vboxUsbMonHubDevWalk(vboxUsbFltFilterCheckWalker, &Data);
+
+ LOG(("DONE Running filters, Context (0x%p)", pContext));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Closing context(0x%p)", pContext));
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+
+ ASSERT_WARN(pContext, ("null context"));
+
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("irql==(%d)", Irql));
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ pContext->bRemoved = TRUE;
+ RemoveEntryList(&pContext->ListEntry);
+
+ LOG(("removing owner filters"));
+ /* now re-arrange the filters */
+ /* 1. remove filters */
+ VBoxUSBFilterRemoveOwner(pContext);
+
+ LOG(("enumerating devices.."));
+ /* 2. check if there are devices owned */
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+
+ if (vboxUsbFltDevCheckReplugLocked(pDevice, pContext))
+ {
+ LOG(("device needs replug"));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ /* retain to ensure the device is not removed before we issue a replug */
+ vboxUsbFltDevRetain(pDevice);
+ /* keep the PDO alive */
+ ObReferenceObject(pDevice->Pdo);
+ }
+ else
+ {
+ LOG(("device does NOT need replug"));
+ }
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* this should replug all devices that were either skipped or grabbed due to the context's */
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ LOG(("SUCCESS done context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Creating context(0x%p)", pContext));
+ memset(pContext, 0, sizeof (*pContext));
+ pContext->Process = RTProcSelf();
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ InsertHeadList(&g_VBoxUsbFltGlobals.ContextList, &pContext->ListEntry);
+ VBOXUSBFLT_LOCK_RELEASE();
+ LOG(("SUCCESS context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+ LOG(("adding filter, Context (0x%p)..", pContext));
+ *pId = 0;
+ /* LOG the filter details. */
+ LOG((__FUNCTION__": %s %s %s",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x Type%#x",
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PORT),
+ USBFilterGetFilterType(pFilter)));
+#endif
+
+ /* We can't get the bus/port numbers. Ignore them while matching. */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PORT, false);
+
+ /* We may not be able to reconstruct the class/subclass/protocol if we aren't able to
+ * read the device descriptor. Don't require these to be present. See also the fInferredDesc flag.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_PROTOCOL, false);
+
+ /* We may also be unable to read string descriptors. Often the userland can't read the
+ * string descriptors either because the device is in a low-power state, but it can happen
+ * that the userland gets lucky and reads the strings, but by the time we get to read them
+ * they're inaccessible due to power management. So, don't require the strings to be present.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_MANUFACTURER_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PRODUCT_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR, false);
+
+ uintptr_t uId = 0;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* Add the filter. */
+ int rc = VBoxUSBFilterAdd(pFilter, pContext, &uId);
+ VBOXUSBFLT_LOCK_RELEASE();
+ if (RT_SUCCESS(rc))
+ {
+ LOG(("ADDED filter id 0x%p", uId));
+ ASSERT_WARN(uId, ("uid is NULL"));
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ else
+ {
+ WARN(("VBoxUSBFilterAdd failed rc (%d)", rc));
+ ASSERT_WARN(!uId, ("uid is not NULL"));
+ }
+
+ *pId = uId;
+ return rc;
+}
+
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId)
+{
+ LOG(("removing filter id(0x%p), Context (0x%p)..", pContext, uId));
+ Assert(uId);
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ int rc = VBoxUSBFilterRemove(pContext, uId);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxUSBFilterRemove failed rc (%d)", rc));
+ VBOXUSBFLT_LOCK_RELEASE();
+ return rc;
+ }
+
+ LOG(("enumerating devices.."));
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->fIsFilterOneShot)
+ {
+ ASSERT_WARN(!pDevice->uFltId, ("oneshot filter on device(0x%p): unexpected uFltId(%d)", pDevice, pDevice->uFltId));
+ }
+
+ if (pDevice->uFltId != uId)
+ continue;
+
+ ASSERT_WARN(pDevice->pOwner == pContext, ("Device(0x%p) owner(0x%p) not match to (0x%p)", pDevice, pDevice->pOwner, pContext));
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(!pDevice->fIsFilterOneShot, ("device(0x%p) is filtered with a oneshot filter", pDevice));
+ pDevice->uFltId = 0;
+ /* clear the fIsFilterOneShot flag to ensure the device is replugged on the next VBoxUsbFltFilterCheck call */
+ pDevice->fIsFilterOneShot = false;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("done enumerating devices"));
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ return rc;
+}
+
+static USBDEVICESTATE vboxUsbDevGetUserState(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* the device is filtered, or replugging */
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("replugging device(0x%p) still has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ASSERT_WARN(!pDevice->uFltId, ("replugging device(0x%p) still has filter(0x%p)", pDevice, pDevice->uFltId));
+ /* no user state for this, we should not return it tu the user */
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ /* the device is filtered, if owner differs from the context, return as USED_BY_HOST */
+ ASSERT_WARN(pDevice->pOwner, ("device(0x%p) has noowner", pDevice));
+ /* the id can be null if a filter is removed */
+// Assert(pDevice->uFltId);
+
+ if (pDevice->pOwner != pContext)
+ {
+ LOG(("Device owner differs from the current context, returning used by host"));
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ switch (pDevice->enmState)
+ {
+ case VBOXUSBFLT_DEVSTATE_UNCAPTURED:
+ case VBOXUSBFLT_DEVSTATE_CAPTURING:
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+ case VBOXUSBFLT_DEVSTATE_CAPTURED:
+ return USBDEVICESTATE_HELD_BY_PROXY;
+ case VBOXUSBFLT_DEVSTATE_USED_BY_GUEST:
+ return USBDEVICESTATE_USED_BY_GUEST;
+ default:
+ WARN(("unexpected device state(%d) for device(0x%p)", pDevice->enmState, pDevice));
+ return USBDEVICESTATE_UNSUPPORTED;
+ }
+}
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ if (!hDevice)
+ return STATUS_INVALID_PARAMETER;
+
+ memset (pInfo, 0, sizeof (*pInfo));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED);
+
+ if (pDevice != hDevice)
+ continue;
+
+ USBDEVICESTATE enmUsrState = vboxUsbDevGetUserState(pContext, pDevice);
+ pInfo->enmState = enmUsrState;
+ VBOXUSBFLT_LOCK_RELEASE();
+ return STATUS_SUCCESS;
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* We should not get this far with valid input. */
+ return STATUS_INVALID_PARAMETER;
+}
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered)
+{
+ *pbFiltered = FALSE;
+ PVBOXUSBFLT_DEVICE pDevice;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ /* first check if device is in the a already */
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ return STATUS_SUCCESS;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ pDevice = (PVBOXUSBFLT_DEVICE)VBoxUsbMonMemAllocZ(sizeof (*pDevice));
+ if (!pDevice)
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ ObDereferenceObject(pPdo);
+ return STATUS_NO_MEMORY;
+ }
+
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_ADDED;
+ pDevice->cRefs = 1;
+ NTSTATUS Status = vboxUsbFltDevPopulate(pDevice, pPdo /* , TRUE /* need all props */);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("vboxUsbFltDevPopulate failed, Status 0x%x", Status));
+ ObDereferenceObject(pPdo);
+ VBoxUsbMonMemFree(pDevice);
+ return Status;
+ }
+
+ uintptr_t uId;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pCtx;
+ PVBOXUSBFLT_DEVICE pTmpDev;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* (paranoia) re-check the device is still not here */
+ pTmpDev = vboxUsbFltDevGetLocked(pPdo);
+
+ /* Drop the PDO ref, now we won't need it anymore. */
+ ObDereferenceObject(pPdo);
+
+ if (pTmpDev)
+ {
+ LOG(("second try: found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("second try: VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("second try: VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pTmpDev->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ VBoxUsbMonMemFree(pDevice);
+ return STATUS_SUCCESS;
+ }
+
+ LOG(("Created Device 0x%p for PDO 0x%p", pDevice, pPdo));
+
+ pCtx = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ true, /* remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Created Device 0x%p should be filtered", pDevice));
+ ASSERT_WARN(pCtx, ("zero ctx"));
+ ASSERT_WARN(uId, ("zero uId"));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ LOG(("Created Device 0x%p should NOT be filtered", pDevice));
+ ASSERT_WARN(!uId == !pCtx, ("invalid uid(0x%p) - ctx(0x%p) pair", uId, pCtx)); /* either both zero or both not */
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+ }
+
+ if (pCtx)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pCtx, fIsOneShot ? 0 : uId, fIsOneShot);
+
+ InsertHeadList(&g_VBoxUsbFltGlobals.DeviceList, &pDevice->GlobalLe);
+
+ /* do not need to signal anything here -
+ * going to do that once the proxy device object starts */
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ *pbFiltered = fFilter;
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo)
+{
+ VBOXUSBFLT_DEVSTATE enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ enmState = pDevice->enmState;
+
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+
+ return enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_DEVSTATE enmOldState;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ RemoveEntryList(&pDevice->GlobalLe);
+ enmOldState = pDevice->enmState;
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ if (pDevice)
+ vboxUsbFltDevRelease(pDevice);
+ return STATUS_SUCCESS;
+}
+
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ /* NB: The USB proxy (VBoxUSB.sys) passes us the real PDO, not anything above that. */
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ /*
+ * Prevent a host crash when vboxUsbFltDevGetLocked fails to locate the matching PDO
+ * in g_VBoxUsbFltGlobals.DeviceList (see @bugref{6509}).
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("failed to get device for PDO(0x%p)", pPdo));
+ }
+ else if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING)
+ {
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURED;
+ LOG(("The proxy notified proxy start for the captured device 0x%p", pDevice));
+ vboxUsbFltDevRetain(pDevice);
+ }
+ else
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ pDevice = NULL;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ return pDevice;
+}
+
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev)
+{
+ PVBOXUSBFLT_DEVICE pDevice = (PVBOXUSBFLT_DEVICE)hDev;
+ /*
+ * Prevent a host crash when VBoxUsbFltProxyStarted fails, returning NULL.
+ * See @bugref{6509}.
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("VBoxUsbFltProxyStopped called with NULL device pointer"));
+ return;
+ }
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST)
+ {
+ /* this is due to devie was physically removed */
+ LOG(("The proxy notified proxy stop for the captured device 0x%p, current state %d", pDevice, pDevice->enmState));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ if (pDevice->enmState != VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ }
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ vboxUsbFltDevRelease(pDevice);
+}
+
+
+static NTSTATUS vboxUsbFltRegKeyQuery(PWSTR ValueName, ULONG ValueType, PVOID ValueData, ULONG ValueLength, PVOID Context, PVOID EntryContext)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ RT_NOREF(ValueName, Context);
+ if ( ValueType == REG_DWORD
+ && ValueLength == sizeof(ULONG))
+ *(ULONG *)EntryContext = *(ULONG *)ValueData;
+ else
+ Status = STATUS_OBJECT_TYPE_MISMATCH;
+
+ return Status;
+}
+
+
+NTSTATUS VBoxUsbFltInit()
+{
+ int rc = VBoxUSBFilterInit();
+ if (RT_FAILURE(rc))
+ {
+ WARN(("VBoxUSBFilterInit failed, rc (%d)", rc));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ memset(&g_VBoxUsbFltGlobals, 0, sizeof (g_VBoxUsbFltGlobals));
+ InitializeListHead(&g_VBoxUsbFltGlobals.DeviceList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.ContextList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.BlackDeviceList);
+ vboxUsbFltBlDevPopulateWithKnownLocked();
+ VBOXUSBFLT_LOCK_INIT();
+
+ /*
+ * Check whether the setting to force replugging USB devices when
+ * querying string descriptors fail is set in the registry,
+ * see @bugref{9479}.
+ */
+ RTL_QUERY_REGISTRY_TABLE aParams[] =
+ {
+ {vboxUsbFltRegKeyQuery, 0, L"ForceReplugWhenDevPopulateFails", &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, REG_DWORD, &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, sizeof(ULONG) },
+ { NULL, 0, NULL, NULL, 0, 0, 0 }
+ };
+ UNICODE_STRING UnicodePath = RTL_CONSTANT_STRING(L"\\VBoxUSB");
+
+ NTSTATUS Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, UnicodePath.Buffer, &aParams[0], NULL, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ LOG(("Forcing replug of USB devices where querying the descriptors fail\n"));
+ }
+ else
+ LOG(("RtlQueryRegistryValues() -> %#x, assuming defaults\n", Status));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltTerm()
+{
+ bool bBusy = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ do
+ {
+ if (!IsListEmpty(&g_VBoxUsbFltGlobals.ContextList))
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+
+ PLIST_ENTRY pNext = NULL;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(!pDevice->uFltId);
+ Assert(!pDevice->pOwner);
+ if (pDevice->cRefs != 1)
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+ }
+ } while (0);
+
+ VBOXUSBFLT_LOCK_RELEASE()
+
+ if (bBusy)
+ {
+ return STATUS_DEVICE_BUSY;
+ }
+
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink)
+ {
+ RemoveEntryList(pEntry);
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ vboxUsbFltDevRelease(pDevice);
+ }
+
+ vboxUsbFltBlDevClearLocked();
+
+ VBOXUSBFLT_LOCK_TERM();
+
+ VBoxUSBFilterTerm();
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
new file mode 100644
index 00000000..2e8c7501
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
@@ -0,0 +1,75 @@
+/* $Id: VBoxUsbFlt.h $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+#include <VBoxUSBFilterMgr.h>
+
+#include <VBox/usblib-win.h>
+
+typedef struct VBOXUSBFLTCTX
+{
+ LIST_ENTRY ListEntry;
+ RTPROCESS Process; // Purely informational, no function?
+ uint32_t cActiveFilters;
+ BOOLEAN bRemoved; // For debugging only?
+} VBOXUSBFLTCTX, *PVBOXUSBFLTCTX;
+
+NTSTATUS VBoxUsbFltInit();
+NTSTATUS VBoxUsbFltTerm();
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext);
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext);
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId);
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId);
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext);
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo);
+
+typedef void* HVBOXUSBFLTDEV;
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo);
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev);
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered);
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo);
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
new file mode 100644
index 00000000..83dc8de9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
@@ -0,0 +1,218 @@
+/* $Id: VBoxUsbHook.cpp $ */
+/** @file
+ * Driver Dispatch Table Hooking API
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBHOOK_MEMTAG 'HUBV'
+
+
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (pHook->fIsInstalled)
+ {
+ WARN(("hook is marked installed, returning failure"));
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->pfnOldHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnHook);
+ Assert(pHook->pfnOldHandler);
+ Assert(pHook->pfnHook != pHook->pfnOldHandler);
+ pHook->fIsInstalled = TRUE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+
+}
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+ }
+
+ PDRIVER_DISPATCH pfnOldVal = (PDRIVER_DISPATCH)InterlockedCompareExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnOldHandler, pHook->pfnHook);
+ Assert(pfnOldVal == pHook->pfnHook);
+ if (pfnOldVal != pHook->pfnHook)
+ {
+ AssertMsgFailed(("unhook failed!!!\n"));
+ /* this is bad! this could happen if someone else has chained another hook,
+ * or (which is even worse) restored the "initial" entry value it saved when doing a hooking before us
+ * return the failure and don't do anything else
+ * the best thing to do if this happens is to leave everything as is
+ * and to prevent the driver from being unloaded to ensure no one references our unloaded hook routine */
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->fIsInstalled = FALSE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+
+ /* wait for the current handlers to exit */
+ VBoxDrvToolRefWaitEqual(&pHook->HookRef, 1);
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ BOOLEAN fIsInstalled;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ fIsInstalled = pHook->fIsInstalled;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return fIsInstalled;
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook)
+{
+ Assert(pDrvObj);
+ Assert(iMjFunction <= IRP_MJ_MAXIMUM_FUNCTION);
+ Assert(pfnHook);
+ memset(pHook, 0, sizeof (*pHook));
+ InitializeListHead(&pHook->RequestList);
+ KeInitializeSpinLock(&pHook->Lock);
+ VBoxDrvToolRefInit(&pHook->HookRef);
+ pHook->pDrvObj = pDrvObj;
+ pHook->iMjFunction = iMjFunction;
+ pHook->pfnHook = pfnHook;
+ Assert(!pHook->pfnOldHandler);
+ Assert(!pHook->fIsInstalled);
+
+}
+
+static void vboxUsbHookRequestRegisterCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ Assert(pRequest);
+ Assert(pDevObj);
+ Assert(pIrp);
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ memset(pRequest, 0, sizeof (*pRequest));
+ pRequest->pHook = pHook;
+ pRequest->OldLocation = *pSl;
+ pRequest->pDevObj = pDevObj;
+ pRequest->pIrp = pIrp;
+ pRequest->bCompletionStopped = FALSE;
+ pSl->CompletionRoutine = pfnCompletion;
+ pSl->Context = pRequest;
+ pSl->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ InsertTailList(&pHook->RequestList, &pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ vboxUsbHookRequestRegisterCompletion(pHook, pDevObj, pIrp, pfnCompletion, pRequest);
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp,
+ PVBOXUSBHOOK_REQUEST pRequest)
+{
+ RT_NOREF3(pHook, pDevObj, pIrp);
+ Assert(!pRequest->bCompletionStopped);
+ pRequest->bCompletionStopped = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (pRequest->OldLocation.CompletionRoutine && pRequest->OldLocation.Control)
+ {
+ Status = pRequest->OldLocation.CompletionRoutine(pDevObj, pIrp, pRequest->OldLocation.Context);
+ }
+
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ if (pRequest->bCompletionStopped)
+ {
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+ }
+ /*
+ * else - in case driver returned STATUS_MORE_PROCESSING_REQUIRED,
+ * it will call IoCompleteRequest itself
+ */
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ RemoveEntryList(&pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+ return Status;
+}
+
+#define PVBOXUSBHOOK_REQUEST_FROM_LE(_pLe) ( (PVBOXUSBHOOK_REQUEST)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBHOOK_REQUEST, ListEntry) ) )
+
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp)
+{
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ for (PLIST_ENTRY pLe = pHook->RequestList.Flink; pLe != &pHook->RequestList; pLe = pLe->Flink)
+ {
+ PVBOXUSBHOOK_REQUEST pCur = PVBOXUSBHOOK_REQUEST_FROM_LE(pLe);
+ if (pCur != pRequest)
+ continue;
+ if (pCur->pIrp != pIrp)
+ continue;
+ WARN(("found pending IRP(0x%p) when it should not be", pIrp));
+ }
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
new file mode 100644
index 00000000..f9207297
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
@@ -0,0 +1,96 @@
+/* $Id: VBoxUsbHook.h $ */
+/** @file
+ * Driver Dispatch Table Hooking API impl
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+
+typedef struct VBOXUSBHOOK_ENTRY
+{
+ LIST_ENTRY RequestList;
+ KSPIN_LOCK Lock;
+ BOOLEAN fIsInstalled;
+ PDRIVER_DISPATCH pfnOldHandler;
+ VBOXDRVTOOL_REF HookRef;
+ PDRIVER_OBJECT pDrvObj;
+ UCHAR iMjFunction;
+ PDRIVER_DISPATCH pfnHook;
+} VBOXUSBHOOK_ENTRY, *PVBOXUSBHOOK_ENTRY;
+
+typedef struct VBOXUSBHOOK_REQUEST
+{
+ LIST_ENTRY ListEntry;
+ PVBOXUSBHOOK_ENTRY pHook;
+ IO_STACK_LOCATION OldLocation;
+ PDEVICE_OBJECT pDevObj;
+ PIRP pIrp;
+ BOOLEAN bCompletionStopped;
+} VBOXUSBHOOK_REQUEST, *PVBOXUSBHOOK_REQUEST;
+
+DECLINLINE(BOOLEAN) VBoxUsbHookRetain(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return FALSE;
+ }
+
+ VBoxDrvToolRefRetain(&pHook->HookRef);
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return TRUE;
+}
+
+DECLINLINE(VOID) VBoxUsbHookRelease(PVBOXUSBHOOK_ENTRY pHook)
+{
+ VBoxDrvToolRefRelease(&pHook->HookRef);
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook);
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook);
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp);
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
new file mode 100644
index 00000000..349893bd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
@@ -0,0 +1,1568 @@
+/* $Id: VBoxUsbMon.cpp $ */
+/** @file
+ * VBox USB Monitor
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ *
+ * Theory of Operation
+ * - or -
+ * The Document I Wish The Original Author Had Written
+ *
+ *
+ * The USB Monitor (VBoxUSBMon.sys) serves to capture and uncapture USB
+ * devices. Its job is to ensure that the USB proxy (VBoxUSB.sys) gets installed
+ * for captured devices and removed again when not needed, restoring the regular
+ * driver (if any).
+ *
+ * The USB Monitor does not handle any actual USB traffic; that is the role of
+ * VBoxUSB.sys, the USB proxy. A typical solution for installing such USB proxy
+ * is using a filter driver, but that approach was rejected because filter drivers
+ * cannot be dynamically added and removed. What VBoxUSBMon does instead is hook
+ * into the dispatch routine of the bus driver, i.e. USB hub driver, and alter
+ * the PnP information returned by the bus driver.
+ *
+ * The key functionality for capturing is cycling a USB port (which causes a USB
+ * device reset and triggers re-enumeration in the Windows USB driver stack), and
+ * then modifying IRP_MN_QUERY_ID / BusQueryHardwareIDs and related requests so
+ * that they return the synthetic USB VID/PID that VBoxUSB.sys handles rather than
+ * the true hardware VID/PID. That causes Windows to install VBoxUSB.sys for the
+ * device.
+ *
+ * Uncapturing again cycles the USB port but returns unmodified hardware IDs,
+ * causing Windows to load the normal driver for the device.
+ *
+ * Identifying devices to capture or release (uncapture) is done through USB filters,
+ * a cross-platform concept which matches USB device based on their VID/PID, class,
+ * and other criteria.
+ *
+ * There is an IOCTL interface for adding/removing USB filters and applying them.
+ * The IOCTLs are normally issued by VBoxSVC.
+ *
+ * USB devices are enumerated by finding all USB hubs (GUID_DEVINTERFACE_USB_HUB)
+ * and querying their child devices (i.e. USB devices or other hubs) by sending
+ * IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations. This is done when
+ * applying existing filters.
+ *
+ * Newly arrived USB devices are intercepted early in their PnP enumeration
+ * through the hooked bus driver dispatch routine. Devices which satisty the
+ * filter matching criteria are morphed (see above) such that VBoxUSB.sys loads
+ * for them before any default driver does.
+ *
+ * There is an IDC interface to VBoxUSB.sys which allows the USB proxy to report
+ * that it's installed for a given USB device, and also report when the USB proxy
+ * is unloaded (typically caused by either unplugging the device or uncapturing
+ * and cycling the port). VBoxUSBMon.sys relies on these IDC calls to track
+ * captured devices and be informed when VBoxUSB.sys unloads.
+ *
+ * Windows 8+ complicates the USB Monitor's life by automatically putting some
+ * USB devices to a low-power state where they are unable to respond to any USB
+ * requests and VBoxUSBMon can't read any of their descriptors (note that in
+ * userland, the device descriptor can always be read, but string descriptors
+ * can't). Such devices' USB VID/PID/revision is recovered using the Windows
+ * PnP Manager from their DevicePropertyHardwareID, but their USB class/subclass
+ * and protocol unfortunately cannot be unambiguously recovered from their
+ * DevicePropertyCompatibleIDs.
+ *
+ * Filter drivers add another complication. With filter drivers in place, the
+ * device objects returned by the BusRelations query (or passing through the PnP
+ * hooks) may not be PDOs but rather filter DOs higher in the stack. To avoid
+ * confusion, we flatten the references to their base, i.e. the real PDO, which
+ * should remain the same for the lifetime of a device. Note that VBoxUSB.sys
+ * always passes its own PDO in the proxy startup IOCTL.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbIdc.h"
+#include <iprt/errcore.h>
+#include <VBox/usblib.h>
+#include <excpt.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBMON_MEMTAG 'MUBV'
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXUSBMONINS
+{
+ void * pvDummy;
+} VBOXUSBMONINS, *PVBOXUSBMONINS;
+
+typedef struct VBOXUSBMONCTX
+{
+ VBOXUSBFLTCTX FltCtx;
+} VBOXUSBMONCTX, *PVBOXUSBMONCTX;
+
+typedef struct VBOXUSBHUB_PNPHOOK
+{
+ VBOXUSBHOOK_ENTRY Hook;
+ bool fUninitFailed;
+} VBOXUSBHUB_PNPHOOK, *PVBOXUSBHUB_PNPHOOK;
+
+typedef struct VBOXUSBHUB_PNPHOOK_COMPLETION
+{
+ VBOXUSBHOOK_REQUEST Rq;
+} VBOXUSBHUB_PNPHOOK_COMPLETION, *PVBOXUSBHUB_PNPHOOK_COMPLETION;
+
+#define VBOXUSBMON_MAXDRIVERS 5
+typedef struct VBOXUSB_PNPDRIVER
+{
+ PDRIVER_OBJECT DriverObject;
+ VBOXUSBHUB_PNPHOOK UsbHubPnPHook;
+ PDRIVER_DISPATCH pfnHookStub;
+} VBOXUSB_PNPDRIVER, *PVBOXUSB_PNPDRIVER;
+
+typedef struct VBOXUSBMONGLOBALS
+{
+ PDEVICE_OBJECT pDevObj;
+ VBOXUSB_PNPDRIVER pDrivers[VBOXUSBMON_MAXDRIVERS];
+ KEVENT OpenSynchEvent;
+ IO_REMOVE_LOCK RmLock;
+ uint32_t cOpens;
+ volatile LONG ulPreventUnloadOn;
+ PFILE_OBJECT pPreventUnloadFileObj;
+} VBOXUSBMONGLOBALS, *PVBOXUSBMONGLOBALS;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXUSBMONGLOBALS g_VBoxUsbMonGlobals;
+
+/*
+ * Note: Must match the VID & PID in the USB driver .inf file!!
+ */
+/*
+ BusQueryDeviceID USB\Vid_80EE&Pid_CAFE
+ BusQueryInstanceID 2
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE&Rev_0100
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00&Prot_00
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00
+ BusQueryCompatibleIDs USB\Class_ff
+*/
+
+static WCHAR const g_szBusQueryDeviceId[] = L"USB\\Vid_80EE&Pid_CAFE";
+static WCHAR const g_szBusQueryHardwareIDs[] = L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0";
+static WCHAR const g_szBusQueryCompatibleIDs[] = L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0";
+static WCHAR const g_szDeviceTextDescription[] = L"VirtualBox USB";
+
+
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBMON_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = VBoxUsbMonMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+VOID VBoxUsbMonMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSBMON_MEMTAG);
+}
+
+#define VBOXUSBDBG_STRCASE(_t) \
+ case _t: return #_t
+#define VBOXUSBDBG_STRCASE_UNKNOWN(_v) \
+ default: LOG((__FUNCTION__": Unknown Value (0n%d), (0x%x)", _v, _v)); return "Unknown"
+
+/* These minor code are semi-undocumented. */
+#ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION
+#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
+#endif
+#ifndef IRP_MN_DEVICE_ENUMERATED
+#define IRP_MN_DEVICE_ENUMERATED 0x19
+#endif
+
+static const char* vboxUsbDbgStrPnPMn(UCHAR uMn)
+{
+ switch (uMn)
+ {
+ VBOXUSBDBG_STRCASE(IRP_MN_START_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_RELATIONS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_INTERFACE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_CAPABILITIES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_TEXT);
+ VBOXUSBDBG_STRCASE(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_READ_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_WRITE_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_EJECT);
+ VBOXUSBDBG_STRCASE(IRP_MN_SET_LOCK);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_ID);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_PNP_DEVICE_STATE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_USAGE_NOTIFICATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_SURPRISE_REMOVAL);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_ENUMERATED);
+ VBOXUSBDBG_STRCASE_UNKNOWN(uMn);
+ }
+}
+
+/**
+ * Send IRP_MN_QUERY_DEVICE_RELATIONS
+ *
+ * @returns NT Status
+ * @param pDevObj USB device pointer
+ * @param pFileObj Valid file object pointer
+ * @param pDevRelations Pointer to DEVICE_RELATIONS pointer (out)
+ */
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations)
+{
+ IO_STATUS_BLOCK IoStatus;
+ KEVENT Event;
+ NTSTATUS Status;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pSl;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Assert(pDevRelations);
+ *pDevRelations = NULL;
+
+ pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pDevObj, NULL, 0, NULL, &Event, &IoStatus);
+ if (!pIrp)
+ {
+ WARN(("IoBuildDeviceIoControlRequest failed!!"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_PNP;
+ pSl->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
+ pSl->Parameters.QueryDeviceRelations.Type = BusRelations;
+ pSl->FileObject = pFileObj;
+
+ Status = IoCallDriver(pDevObj, pIrp);
+ if (Status == STATUS_PENDING)
+ {
+ LOG(("IoCallDriver returned STATUS_PENDING!!"));
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)IoStatus.Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ *pDevRelations = pRel;
+ else
+ {
+ WARN(("Invalid pointer %p", pRel));
+ }
+ }
+ else
+ {
+ WARN(("IRP_MN_QUERY_DEVICE_RELATIONS failed Status(0x%x)", Status));
+ }
+
+ LOG(("IoCallDriver returned %x", Status));
+ return Status;
+}
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker)
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PWSTR szwHubList;
+ Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_USB_HUB, NULL, 0, &szwHubList);
+ if (Status != STATUS_SUCCESS)
+ {
+ LOG(("IoGetDeviceInterfaces failed with %d\n", Status));
+ return;
+ }
+ if (szwHubList)
+ {
+ UNICODE_STRING UnicodeName;
+ PDEVICE_OBJECT pHubDevObj;
+ PFILE_OBJECT pHubFileObj;
+ PWSTR szwHubName = szwHubList;
+ while (*szwHubName != UNICODE_NULL)
+ {
+ RtlInitUnicodeString(&UnicodeName, szwHubName);
+ Status = IoGetDeviceObjectPointer(&UnicodeName, FILE_READ_DATA, &pHubFileObj, &pHubDevObj);
+ if (Status == STATUS_SUCCESS)
+ {
+ /* We could not log hub name here.
+ * It is the paged memory and we cannot use it in logger cause it increases the IRQL
+ */
+ LOG(("IoGetDeviceObjectPointer returned %p %p", pHubDevObj, pHubFileObj));
+ if (!pfnWalker(pHubFileObj, pHubDevObj, pvWalker))
+ {
+ LOG(("the walker said to stop"));
+ ObDereferenceObject(pHubFileObj);
+ break;
+ }
+
+ LOG(("going forward.."));
+ ObDereferenceObject(pHubFileObj);
+ }
+ szwHubName += wcslen(szwHubName) + 1;
+ }
+ ExFreePool(szwHubList);
+ }
+}
+
+/* NOTE: the stack location data is not the "actual" IRP stack location,
+ * but a copy being preserved on the IRP way down.
+ * See the note in VBoxUsbPnPCompletion for detail */
+static NTSTATUS vboxUsbMonHandlePnPIoctl(PDEVICE_OBJECT pDevObj, PIO_STACK_LOCATION pSl, PIO_STATUS_BLOCK pIoStatus)
+{
+ LOG(("IRQL = %d", KeGetCurrentIrql()));
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ {
+ LOG(("IRP_MN_QUERY_DEVICE_TEXT: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+ switch (pSl->Parameters.QueryDeviceText.DeviceTextType)
+ {
+ case DeviceTextLocationInformation:
+ LOG(("DeviceTextLocationInformation"));
+ LOG_STRW(pId);
+ break;
+
+ case DeviceTextDescription:
+ LOG(("DeviceTextDescription"));
+ LOG_STRW(pId);
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ WCHAR *pId2 = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szDeviceTextDescription));
+ AssertBreak(pId2);
+ memcpy(pId2, g_szDeviceTextDescription, sizeof(g_szDeviceTextDescription));
+ LOG(("NEW szDeviceTextDescription"));
+ LOG_STRW(pId2);
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId2;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+ default:
+ LOG(("DeviceText %d", pSl->Parameters.QueryDeviceText.DeviceTextType));
+ break;
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pId));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_ID:
+ {
+ LOG(("IRP_MN_QUERY_ID: Irp->pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS && pDevObj)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ WCHAR *pTmp;
+#endif
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+
+ switch (pSl->Parameters.QueryId.IdType)
+ {
+ case BusQueryInstanceID:
+ LOG(("BusQueryInstanceID"));
+ LOG_STRW(pId);
+ break;
+
+ case BusQueryDeviceID:
+ {
+ LOG(("BusQueryDeviceID"));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryDeviceId));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ ExFreePool((PVOID)pIoStatus->Information);
+ memcpy(pId, g_szBusQueryDeviceId, sizeof(g_szBusQueryDeviceId));
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryHardwareIDs:
+ {
+ LOG(("BusQueryHardwareIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryHardwareIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+
+ memcpy(pId, g_szBusQueryHardwareIDs, sizeof(g_szBusQueryHardwareIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryHardwareIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryCompatibleIDs:
+ LOG(("BusQueryCompatibleIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryCompatibleIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+ memcpy(pId, g_szBusQueryCompatibleIDs, sizeof(g_szBusQueryCompatibleIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryCompatibleIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+
+ default:
+ /** @todo r=bird: handle BusQueryContainerID and whatever else we might see */
+ break;
+ }
+ }
+ else
+ {
+ LOG(("Invalid pointer %p", pId));
+ }
+ }
+ break;
+ }
+
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ {
+ switch(pSl->Parameters.QueryDeviceRelations.Type)
+ {
+ case BusRelations:
+ LOG(("BusRelations"));
+
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)pIoStatus->Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ {
+ for (unsigned i=0;i<pRel->Count;i++)
+ {
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ LOG(("New PDO %p", pRel->Objects[i]));
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pRel));
+ }
+ break;
+ case TargetDeviceRelation:
+ LOG(("TargetDeviceRelation"));
+ break;
+ case RemovalRelations:
+ LOG(("RemovalRelations"));
+ break;
+ case EjectionRelations:
+ LOG(("EjectionRelations"));
+ break;
+ default:
+ LOG(("QueryDeviceRelations.Type=%d", pSl->Parameters.QueryDeviceRelations.Type));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ {
+ LOG(("IRP_MN_QUERY_CAPABILITIES: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_CAPABILITIES pCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
+ if (RT_VALID_PTR(pCaps))
+ {
+ LOG(("Caps.SilentInstall = %d", pCaps->SilentInstall));
+ LOG(("Caps.UniqueID = %d", pCaps->UniqueID ));
+ LOG(("Caps.Address = %d", pCaps->Address ));
+ LOG(("Caps.UINumber = %d", pCaps->UINumber ));
+ }
+ else
+ LOG(("Invalid pointer %p", pCaps));
+ }
+ break;
+ }
+
+ default:
+ break;
+#endif
+ } /*switch */
+
+ LOG(("Done returns %x (IRQL = %d)", pIoStatus->Status, KeGetCurrentIrql()));
+ return pIoStatus->Status;
+}
+
+NTSTATUS _stdcall VBoxUsbPnPCompletion(DEVICE_OBJECT *pDevObj, IRP *pIrp, void *pvContext)
+{
+ LOG(("Completion PDO(0x%p), IRP(0x%p), Status(0x%x)", pDevObj, pIrp, pIrp->IoStatus.Status));
+ ASSERT_WARN(pvContext, ("zero context"));
+
+ PVBOXUSBHOOK_REQUEST pRequest = (PVBOXUSBHOOK_REQUEST)pvContext;
+ /* NOTE: despite a regular IRP processing the stack location in our completion
+ * differs from those of the PnP hook since the hook is invoked in the "context" of the calle,
+ * while the completion is in the "coller" context in terms of IRP,
+ * so the completion stack location is one level "up" here.
+ *
+ * Moreover we CAN NOT access irp stack location in the completion because we might not have one at all
+ * in case the hooked driver is at the top of the irp call stack
+ *
+ * This is why we use the stack location we saved on IRP way down.
+ * */
+ PIO_STACK_LOCATION pSl = &pRequest->OldLocation;
+ ASSERT_WARN(pIrp == pRequest->pIrp, ("completed IRP(0x%x) not match request IRP(0x%x)", pIrp, pRequest->pIrp));
+ /* NOTE: we can not rely on pDevObj passed in IoCompletion since it may be zero
+ * in case IRP was created with extra stack locations and the caller did not initialize
+ * the IO_STACK_LOCATION::DeviceObject */
+ DEVICE_OBJECT *pRealDevObj = pRequest->pDevObj;
+// Assert(!pDevObj || pDevObj == pRealDevObj);
+// Assert(pSl->DeviceObject == pDevObj);
+
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ case IRP_MN_QUERY_ID:
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ case IRP_MN_QUERY_CAPABILITIES:
+#endif
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ vboxUsbMonHandlePnPIoctl(pRealDevObj, pSl, &pIrp->IoStatus);
+ }
+ else
+ {
+ ASSERT_WARN(pIrp->IoStatus.Status == STATUS_NOT_SUPPORTED, ("Irp failed with status(0x%x)", pIrp->IoStatus.Status));
+ }
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ case IRP_MN_REMOVE_DEVICE:
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ VBoxUsbFltPdoRemove(pRealDevObj);
+ }
+ else
+ {
+ AssertFailed();
+ }
+ break;
+
+ /* These two IRPs are received when the PnP subsystem has determined the id of the newly arrived device */
+ /* IRP_MN_START_DEVICE only arrives if it's a USB device of a known class or with a present host driver */
+ case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+ case IRP_MN_QUERY_RESOURCES:
+ /* There used to be code to support SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT but it was not reliable. */
+
+ default:
+ break;
+ }
+
+ LOG(("<==PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x), Sl PDO(0x%p), Compl PDO(0x%p)",
+ vboxUsbDbgStrPnPMn(pSl->MinorFunction),
+ pRealDevObj, pIrp, pIrp->IoStatus.Status,
+ pSl->DeviceObject, pDevObj));
+#ifdef DEBUG_misha
+ NTSTATUS tmpStatus = pIrp->IoStatus.Status;
+#endif
+ PVBOXUSBHOOK_ENTRY pHook = pRequest->pHook;
+ NTSTATUS Status = VBoxUsbHookRequestComplete(pHook, pDevObj, pIrp, pRequest);
+ VBoxUsbMonMemFree(pRequest);
+#ifdef DEBUG_misha
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ Assert(pIrp->IoStatus.Status == tmpStatus);
+ }
+#endif
+ VBoxUsbHookRelease(pHook);
+ return Status;
+}
+
+/**
+ * Device PnP hook
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS vboxUsbMonPnPHook(IN PVBOXUSBHOOK_ENTRY pHook, IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ LOG(("==>PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x)", vboxUsbDbgStrPnPMn(IoGetCurrentIrpStackLocation(pIrp)->MinorFunction), pDevObj, pIrp, pIrp->IoStatus.Status));
+
+ if (!VBoxUsbHookRetain(pHook))
+ {
+ WARN(("VBoxUsbHookRetain failed"));
+ return VBoxUsbHookRequestPassDownHookSkip(pHook, pDevObj, pIrp);
+ }
+
+ PVBOXUSBHUB_PNPHOOK_COMPLETION pCompletion = (PVBOXUSBHUB_PNPHOOK_COMPLETION)VBoxUsbMonMemAlloc(sizeof (*pCompletion));
+ if (!pCompletion)
+ {
+ WARN(("VBoxUsbMonMemAlloc failed"));
+ VBoxUsbHookRelease(pHook);
+ pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NTSTATUS Status = VBoxUsbHookRequestPassDownHookCompletion(pHook, pDevObj, pIrp, VBoxUsbPnPCompletion, &pCompletion->Rq);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ if (Status != STATUS_PENDING)
+ {
+ LOG(("Request completed, Status(0x%x)", Status));
+ VBoxUsbHookVerifyCompletion(pHook, &pCompletion->Rq, pIrp);
+ }
+ else
+ {
+ LOG(("Request pending"));
+ }
+#endif
+ return Status;
+}
+
+/**
+ * Device PnP hook stubs.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+#define VBOX_PNPHOOKSTUB(n) NTSTATUS _stdcall VBoxUsbMonPnPHook##n(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) \
+{ \
+ return vboxUsbMonPnPHook(&g_VBoxUsbMonGlobals.pDrivers[n].UsbHubPnPHook.Hook, pDevObj, pIrp); \
+}
+
+#define VBOX_PNPHOOKSTUB_INIT(n) g_VBoxUsbMonGlobals.pDrivers[n].pfnHookStub = VBoxUsbMonPnPHook##n
+
+VBOX_PNPHOOKSTUB(0)
+VBOX_PNPHOOKSTUB(1)
+VBOX_PNPHOOKSTUB(2)
+VBOX_PNPHOOKSTUB(3)
+VBOX_PNPHOOKSTUB(4)
+AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+typedef struct VBOXUSBMONHOOKDRIVERWALKER
+{
+ PDRIVER_OBJECT pDrvObj;
+} VBOXUSBMONHOOKDRIVERWALKER, *PVBOXUSBMONHOOKDRIVERWALKER;
+
+/**
+ * Logs an error to the system event log.
+ *
+ * @param ErrCode Error to report to event log.
+ * @param ReturnedStatus Error that was reported by the driver to the caller.
+ * @param uErrId Unique error id representing the location in the driver.
+ * @param cbDumpData Number of bytes at pDumpData.
+ * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
+ *
+ * NB: We only use IoLogMsg.dll as the message file, limiting
+ * ErrCode to status codes and messages defined in ntiologc.h
+ */
+static void vboxUsbMonLogError(NTSTATUS ErrCode, NTSTATUS ReturnedStatus, ULONG uErrId, USHORT cbDumpData, PVOID pDumpData)
+{
+ PIO_ERROR_LOG_PACKET pErrEntry;
+
+
+ /* Truncate dumps that do not fit into IO_ERROR_LOG_PACKET. */
+ if (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData > ERROR_LOG_MAXIMUM_SIZE)
+ cbDumpData = ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
+
+ pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxUsbMonGlobals.pDevObj,
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData);
+ if (pErrEntry)
+ {
+ uint8_t *pDump = (uint8_t *)pErrEntry->DumpData;
+ if (cbDumpData)
+ memcpy(pDump, pDumpData, cbDumpData);
+ pErrEntry->MajorFunctionCode = 0;
+ pErrEntry->RetryCount = 0;
+ pErrEntry->DumpDataSize = cbDumpData;
+ pErrEntry->NumberOfStrings = 0;
+ pErrEntry->StringOffset = 0;
+ pErrEntry->ErrorCode = ErrCode;
+ pErrEntry->UniqueErrorValue = uErrId;
+ pErrEntry->FinalStatus = ReturnedStatus;
+ pErrEntry->IoControlCode = 0;
+ IoWriteErrorLogEntry(pErrEntry);
+ }
+ else
+ {
+ LOG(("Failed to allocate error log entry (cb=%d)\n", FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData));
+ }
+}
+
+static DECLCALLBACK(BOOLEAN) vboxUsbMonHookDrvObjWalker(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ RT_NOREF2(pHubFile, pvContext);
+ PDRIVER_OBJECT pDrvObj = pHubDo->DriverObject;
+
+ /* First we try to figure out if we are already hooked to this driver. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ /* We are not hooked yet, find an empty slot. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (!g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ /* Found an emtpy slot, use it. */
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = pDrvObj;
+ ObReferenceObject(pDrvObj);
+ LOG(("pDrivers[%d] = %p, installing the hook...\n", i, pDrvObj));
+ VBoxUsbHookInit(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook,
+ pDrvObj,
+ IRP_MJ_PNP,
+ g_VBoxUsbMonGlobals.pDrivers[i].pfnHookStub);
+ VBoxUsbHookInstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ return TRUE; /* Must continue to find all drivers. */
+ }
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ }
+ /* No empty slots! No reason to continue. */
+ LOG(("No empty slots!\n"));
+ ANSI_STRING ansiDrvName;
+ NTSTATUS Status = RtlUnicodeStringToAnsiString(&ansiDrvName, &pDrvObj->DriverName, true);
+ if (Status != STATUS_SUCCESS)
+ {
+ ansiDrvName.Length = 0;
+ LOG(("RtlUnicodeStringToAnsiString failed with 0x%x", Status));
+ }
+ vboxUsbMonLogError(IO_ERR_INSUFFICIENT_RESOURCES, STATUS_SUCCESS, 1, ansiDrvName.Length, ansiDrvName.Buffer);
+ if (Status == STATUS_SUCCESS)
+ RtlFreeAnsiString(&ansiDrvName);
+ return FALSE;
+}
+
+/**
+ * Finds all USB drivers in the system and installs hooks if haven't done already.
+ */
+static NTSTATUS vboxUsbMonInstallAllHooks()
+{
+ vboxUsbMonHubDevWalk(vboxUsbMonHookDrvObjWalker, NULL);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookCheckInit()
+{
+ static bool fIsHookInited = false;
+ if (fIsHookInited)
+ {
+ LOG(("hook inited already, success"));
+ return STATUS_SUCCESS;
+ }
+ return vboxUsbMonInstallAllHooks();
+}
+
+static NTSTATUS vboxUsbMonHookInstall()
+{
+ /* Nothing to do here as we have already installed all hooks in vboxUsbMonHookCheckInit(). */
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookUninstall()
+{
+#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
+ return STATUS_SUCCESS;
+#else
+ NTSTATUS Status = STATUS_SUCCESS;
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ Assert(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject == g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook.pDrvObj);
+ LOG(("Unhooking from %p...\n", g_VBoxUsbMonGlobals.pDrivers[i].DriverObject));
+ Status = VBoxUsbHookUninstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ if (!NT_SUCCESS(Status))
+ {
+ /*
+ * We failed to uninstall the hook, so we keep the reference to the driver
+ * in order to prevent another driver re-using this slot because we are
+ * going to mark this hook as fUninitFailed.
+ */
+ //AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
+ LOG(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed));
+ g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed = true;
+ }
+ else
+ {
+ /* The hook was removed successfully, now we can forget about this driver. */
+ ObDereferenceObject(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject);
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = NULL;
+ }
+ }
+ }
+ return Status;
+#endif
+}
+
+
+static NTSTATUS vboxUsbMonCheckTermStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ AssertRelease(Status == STATUS_SUCCESS);
+
+ do
+ {
+ if (--g_VBoxUsbMonGlobals.cOpens)
+ break;
+
+ Status = vboxUsbMonHookUninstall();
+
+ NTSTATUS tmpStatus = VBoxUsbFltTerm();
+ if (!NT_SUCCESS(tmpStatus))
+ {
+ /* this means a driver state is screwed up, KeBugCheckEx here ? */
+ AssertReleaseFailed();
+ }
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonCheckInitStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ if (Status == STATUS_SUCCESS)
+ {
+ do
+ {
+ if (g_VBoxUsbMonGlobals.cOpens++)
+ {
+ LOG(("opens: %d, success", g_VBoxUsbMonGlobals.cOpens));
+ break;
+ }
+
+ Status = VBoxUsbFltInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookCheckInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookInstall();
+ if (NT_SUCCESS(Status))
+ {
+ Status = STATUS_SUCCESS;
+ LOG(("succeded!!"));
+ break;
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookInstall failed, Status (0x%x)", Status));
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookCheckInit failed, Status (0x%x)", Status));
+ }
+ VBoxUsbFltTerm();
+ }
+ else
+ {
+ WARN(("VBoxUsbFltInit failed, Status (0x%x)", Status));
+ }
+
+ --g_VBoxUsbMonGlobals.cOpens;
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+ }
+ else
+ {
+ WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
+ }
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextCreate(PVBOXUSBMONCTX *ppCtx)
+{
+ NTSTATUS Status;
+ *ppCtx = NULL;
+ PVBOXUSBMONCTX pFileCtx = (PVBOXUSBMONCTX)VBoxUsbMonMemAllocZ(sizeof (*pFileCtx));
+ if (pFileCtx)
+ {
+ Status = vboxUsbMonCheckInitStuff();
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = VBoxUsbFltCreate(&pFileCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ *ppCtx = pFileCtx;
+ LOG(("succeeded!!"));
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ WARN(("VBoxUsbFltCreate failed"));
+ }
+ vboxUsbMonCheckTermStuff();
+ }
+ else
+ {
+ WARN(("vboxUsbMonCheckInitStuff failed"));
+ }
+ VBoxUsbMonMemFree(pFileCtx);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ Status = STATUS_NO_MEMORY;
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextClose(PVBOXUSBMONCTX pCtx)
+{
+ NTSTATUS Status = VBoxUsbFltClose(&pCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = vboxUsbMonCheckTermStuff();
+ Assert(Status == STATUS_SUCCESS);
+ /* ignore the failure */
+ VBoxUsbMonMemFree(pCtx);
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+
+ LOG(("VBoxUsbMonClose"));
+
+ NTSTATUS Status = vboxUsbMonContextClose(pCtx);
+ if (Status != STATUS_SUCCESS)
+ {
+ WARN(("vboxUsbMonContextClose failed, Status (0x%x), prevent unload", Status));
+ if (!InterlockedExchange(&g_VBoxUsbMonGlobals.ulPreventUnloadOn, 1))
+ {
+ LOGREL(("ulPreventUnloadOn not set, preventing unload"));
+ UNICODE_STRING UniName;
+ PDEVICE_OBJECT pTmpDevObj;
+ RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
+ AssertRelease(NT_SUCCESS(tmpStatus));
+ AssertRelease(pTmpDevObj == pDevObj);
+ }
+ else
+ {
+ WARN(("ulPreventUnloadOn already set"));
+ }
+ LOG(("success!!"));
+ Status = STATUS_SUCCESS;
+ }
+ pFileObj->FsContext = NULL;
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+
+static NTSTATUS _stdcall VBoxUsbMonCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ NTSTATUS Status;
+
+ LOG(("VBoxUSBMonCreate"));
+
+ if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+ {
+ WARN(("trying to open as a directory"));
+ pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ pFileObj->FsContext = NULL;
+ PVBOXUSBMONCTX pCtx = NULL;
+ Status = vboxUsbMonContextCreate(&pCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pCtx);
+ pFileObj->FsContext = pCtx;
+ }
+ else
+ {
+ WARN(("vboxUsbMonContextCreate failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static int VBoxUsbMonFltAdd(PVBOXUSBMONCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ static uintptr_t idDummy = 1;
+ *pId = idDummy;
+ ++idDummy;
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltAdd(&pContext->FltCtx, pFilter, pId);
+ return rc;
+#endif
+}
+
+static int VBoxUsbMonFltRemove(PVBOXUSBMONCTX pContext, uintptr_t uId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltRemove(&pContext->FltCtx, uId);
+ return rc;
+#endif
+}
+
+static NTSTATUS VBoxUsbMonRunFilters(PVBOXUSBMONCTX pContext)
+{
+ NTSTATUS Status = VBoxUsbFltFilterCheck(&pContext->FltCtx);
+ return Status;
+}
+
+static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ NTSTATUS Status = VBoxUsbFltGetDevice(&pContext->FltCtx, hDevice, pInfo);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer,
+ ULONG cbOutBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG_PTR Info = 0;
+ switch (Ctl)
+ {
+ case SUPUSBFLT_IOCTL_GET_VERSION:
+ {
+ PUSBSUP_VERSION pOut = (PUSBSUP_VERSION)pvBuffer;
+
+ LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
+ if (!pvBuffer || cbOutBuffer != sizeof(*pOut) || cbInBuffer != 0)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = USBMON_MAJOR_VERSION;
+ pOut->u32Minor = USBMON_MINOR_VERSION;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_ADD_FILTER:
+ {
+ PUSBFILTER pFilter = (PUSBFILTER)pvBuffer;
+ PUSBSUP_FLTADDOUT pOut = (PUSBSUP_FLTADDOUT)pvBuffer;
+ uintptr_t uId = 0;
+ int rc;
+ if (RT_UNLIKELY(!pvBuffer || cbInBuffer != sizeof (*pFilter) || cbOutBuffer != sizeof (*pOut)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pFilter), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = VBoxUsbMonFltAdd(pContext, pFilter, &uId);
+ pOut->rc = rc;
+ pOut->uId = uId;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_REMOVE_FILTER:
+ {
+ uintptr_t *pIn = (uintptr_t *)pvBuffer;
+ int *pRc = (int *)pvBuffer;
+
+ if (!pvBuffer || cbInBuffer != sizeof (*pIn) || (cbOutBuffer && cbOutBuffer != sizeof (*pRc)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pIn), cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_REMOVE_FILTER %x", *pIn));
+ int rc = VBoxUsbMonFltRemove(pContext, *pIn);
+ if (cbOutBuffer)
+ {
+ /* we've validated that already */
+ Assert(cbOutBuffer == (ULONG)*pRc);
+ *pRc = rc;
+ Info = sizeof (*pRc);
+ }
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_RUN_FILTERS:
+ {
+ if (pvBuffer || cbInBuffer || cbOutBuffer)
+ {
+ WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
+ Status = VBoxUsbMonRunFilters(pContext);
+ ASSERT_WARN(Status != STATUS_PENDING, ("status pending!"));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_GET_DEVICE:
+ {
+ HVBOXUSBDEVUSR hDevice;
+ PUSBSUP_GETDEV_MON pOut = (PUSBSUP_GETDEV_MON)pvBuffer;
+ if (!pvBuffer || cbInBuffer != sizeof (hDevice) || cbOutBuffer < sizeof (*pOut))
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes! cbIn=%d expected %d. cbOut=%d expected >= %d.",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ hDevice = *(HVBOXUSBDEVUSR*)pvBuffer;
+ if (!hDevice)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: hDevice is NULL!",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = VBoxUsbMonGetDevice(pContext, hDevice, pOut);
+
+ if (NT_SUCCESS(Status))
+ {
+ Info = sizeof (*pOut);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonGetDevice fail 0x%x", Status));
+ }
+ break;
+ }
+
+ default:
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending!"));
+
+ *pInfo = Info;
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pSl->FileObject;
+ Assert(pFileObj);
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+ Assert(pCtx);
+ Status = vboxUsbMonIoctlDispatch(pCtx,
+ pSl->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer,
+ pSl->Parameters.DeviceIoControl.InputBufferLength,
+ pSl->Parameters.DeviceIoControl.OutputBufferLength,
+ &Info);
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending"));
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+ else
+ {
+ WARN(("IoAcquireRemoveLock failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonInternalIoctlDispatch(ULONG Ctl, PVOID pvBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ *pInfo = 0;
+ switch (Ctl)
+ {
+ case VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION:
+ {
+ PVBOXUSBIDC_VERSION pOut = (PVBOXUSBIDC_VERSION)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = VBOXUSBIDC_VERSION_MAJOR;
+ pOut->u32Minor = VBOXUSBIDC_VERSION_MINOR;
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP:
+ {
+ PVBOXUSBIDC_PROXY_STARTUP pOut = (PVBOXUSBIDC_PROXY_STARTUP)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ PDEVICE_OBJECT pDevObj = pOut->u.pPDO;
+ pOut->u.hDev = VBoxUsbFltProxyStarted(pDevObj);
+
+ /* If we couldn't find the PDO in our list, that's a real problem and
+ * the capturing will not really work. Log an error.
+ */
+ if (!pOut->u.hDev)
+ vboxUsbMonLogError(IO_ERR_DRIVER_ERROR, STATUS_SUCCESS, 2, sizeof("INTERNAL_IOCTL_PROXY_STARTUP"), "INTERNAL_IOCTL_PROXY_STARTUP");
+
+ ASSERT_WARN(pOut->u.hDev, ("zero hDev"));
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN:
+ {
+ PVBOXUSBIDC_PROXY_TEARDOWN pOut = (PVBOXUSBIDC_PROXY_TEARDOWN)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(pOut->hDev, ("zero hDev"));
+ VBoxUsbFltProxyStopped(pOut->hDev);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ default:
+ {
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ Status = vboxUsbMonInternalIoctlDispatch(pSl->Parameters.DeviceIoControl.IoControlCode,
+ pSl->Parameters.Others.Argument1,
+ &Info);
+ Assert(Status != STATUS_PENDING);
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+static void _stdcall VBoxUsbMonUnload(PDRIVER_OBJECT pDrvObj)
+{
+ RT_NOREF1(pDrvObj);
+ LOG(("VBoxUSBMonUnload pDrvObj (0x%p)", pDrvObj));
+
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ IoDeleteSymbolicLink(&DosName);
+
+ IoDeleteDevice(g_VBoxUsbMonGlobals.pDevObj);
+
+ /* cleanup the logger */
+ PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+ pLogger = RTLogSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+}
+
+RT_C_DECLS_BEGIN
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+RT_C_DECLS_END
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
+ RTLogDestinations(0, "debugger");
+#endif
+
+ LOGREL(("Built %s %s", __DATE__, __TIME__));
+
+ memset (&g_VBoxUsbMonGlobals, 0, sizeof (g_VBoxUsbMonGlobals));
+
+ VBOX_PNPHOOKSTUB_INIT(0);
+ VBOX_PNPHOOKSTUB_INIT(1);
+ VBOX_PNPHOOKSTUB_INIT(2);
+ VBOX_PNPHOOKSTUB_INIT(3);
+ VBOX_PNPHOOKSTUB_INIT(4);
+ AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+ KeInitializeEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, SynchronizationEvent, TRUE /* signaled */);
+ IoInitializeRemoveLock(&g_VBoxUsbMonGlobals.RmLock, VBOXUSBMON_MEMTAG, 1, 100);
+ UNICODE_STRING DevName;
+ PDEVICE_OBJECT pDevObj;
+ /* create the device */
+ RtlInitUnicodeString(&DevName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ if (NT_SUCCESS(Status))
+ {
+ Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ Status = IoCreateSymbolicLink(&DosName, &DevName);
+ if (NT_SUCCESS(Status))
+ {
+ PVBOXUSBMONINS pDevExt = (PVBOXUSBMONINS)pDevObj->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
+
+ pDrvObj->DriverUnload = VBoxUsbMonUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxUsbMonCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxUsbMonClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxUsbMonDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxUsbMonInternalDeviceControl;
+
+ g_VBoxUsbMonGlobals.pDevObj = pDevObj;
+ LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
+ return STATUS_SUCCESS;
+ }
+ IoDeleteDevice(pDevObj);
+ }
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
new file mode 100644
index 00000000..e67c6856
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
@@ -0,0 +1,77 @@
+/* $Id: VBoxUsbMon.h $ */
+/** @file
+ * VBox USB Monitor
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/assert.h>
+#include <VBox/sup.h>
+#include <iprt/asm.h>
+#include <VBox/log.h>
+
+#ifdef DEBUG
+/* disables filters */
+//#define VBOXUSBMON_DBG_NO_FILTERS
+/* disables pnp hooking */
+//#define VBOXUSBMON_DBG_NO_PNPHOOK
+#endif
+
+#include "../../../win/VBoxDbgLog.h"
+#include "../cmn/VBoxDrvTool.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include "VBoxUsbHook.h"
+#include "VBoxUsbFlt.h"
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes);
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes);
+VOID VBoxUsbMonMemFree(PVOID pvMem);
+
+NTSTATUS VBoxUsbMonGetDescriptor(PDEVICE_OBJECT pDevObj, void *buffer, int size, int type, int index, int language_id);
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations);
+
+void vboxUsbDbgPrintUnicodeString(PUNICODE_STRING pUnicodeString);
+
+typedef DECLCALLBACKTYPE(BOOLEAN, FNVBOXUSBMONDEVWALKER,(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext));
+typedef FNVBOXUSBMONDEVWALKER *PFNVBOXUSBMONDEVWALKER;
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
new file mode 100644
index 00000000..f22b3dd8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxUsbMon.rc $ */
+/** @file
+ * VBoxUSBMon - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox USB Monitor Driver\0"
+ VALUE "InternalName", "VBoxUSBMon\0"
+ VALUE "OriginalFilename", "VBoxUSBMon.sys\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END