VBoxUsbMon.cpp revision ef357854a9842b56c249eea7ebf2f66e7c9da7c1
/* $Id$ */
/** @file
* VBox USB Monitor
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "VBoxUsbMon.h"
#include "../cmn/VBoxUsbIdc.h"
#include <excpt.h>
#include <stdio.h>
/*
* 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
*/
#define szBusQueryDeviceId L"USB\\Vid_80EE&Pid_CAFE"
#define szBusQueryHardwareIDs L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0"
#define szBusQueryCompatibleIDs L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0"
#define szDeviceTextDescription L"VirtualBox USB"
typedef struct VBOXUSBMONINS
{
void * pvDummy;
typedef struct VBOXUSBMONCTX
{
typedef struct VBOXUSBHUB_PNPHOOK
{
bool fUninitFailed;
typedef struct VBOXUSBHUB_PNPHOOK_COMPLETION
{
typedef struct VBOXUSBMONGLOBALS
{
volatile LONG ulPreventUnloadOn;
static VBOXUSBMONGLOBALS g_VBoxUsbMonGlobals;
#define VBOXUSBMON_MEMTAG 'MUBV'
{
return pvMem;
}
{
if (pvMem)
{
}
return pvMem;
}
{
}
#define VBOXUSBDBG_STRCASE(_t) \
#define VBOXUSBDBG_STRCASE_UNKNOWN(_v) \
{
switch (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)
{
*pDevRelations = NULL;
if (!pIrp)
{
WARN(("IoBuildDeviceIoControlRequest failed!!"));
return STATUS_INSUFFICIENT_RESOURCES;
}
if (Status == STATUS_PENDING)
{
LOG(("IoCallDriver returned STATUS_PENDING!!"));
}
if (Status == STATUS_SUCCESS)
{
{
*pDevRelations = pRel;
}
else
{
}
}
else
{
}
return Status;
}
/* these two come from IFS Kit, which is not included in 2K DDK we use,
* although they are documented and exported in ntoskrnl,
* and both should be present for >= XP according to MSDN */
);
);
typedef DECLCALLBACK(VOID) FNVBOXUSBDEVNAMEMATCHER(PDEVICE_OBJECT pDo, PUNICODE_STRING pName, PVOID pvMatcher);
static NTSTATUS vboxUsbObjCheckName(PDEVICE_OBJECT pDo, PFNVBOXUSBDEVNAMEMATCHER pfnMatcher, PVOID pvMatcher)
{
union
{
char buf[1024];
} buf;
if (!NT_SUCCESS(Status))
{
if (STATUS_INFO_LENGTH_MISMATCH != Status)
{
return Status;
}
if (!pInfo)
{
WARN(("VBoxUsbMonMemAlloc failed"));
return STATUS_NO_MEMORY;
}
if (!NT_SUCCESS(Status))
{
return Status;
}
}
/* we've got the name! */
LOG(("got the name:"));
{
}
else
{
LOG(("no freeing info needed"));
}
return STATUS_SUCCESS;
}
typedef DECLCALLBACK(BOOLEAN) FNVBOXUSBDEVSTACKWALKER(PDEVICE_OBJECT pTopDo, PDEVICE_OBJECT pCurDo, PVOID pvContext);
{
do
{
{
union
{
char buf[1024];
} buf;
if (NT_SUCCESS(tmpStatus))
{
LOG((" Obj name:"));
}
else
{
if (STATUS_INFO_LENGTH_MISMATCH != tmpStatus)
{
}
else
{
}
}
if (pCurDo->DriverObject
{
}
else
{
if (pCurDo->DriverObject)
{
LOG((" driver name is zero, Length(%d), Buffer(0x%p)",
}
else
{
LOG((" driver object is NULL"));
}
}
}
#endif
{
LOG(("the walker said to stop"));
break;
}
if (!pLowerDo)
{
LOG(("IoGetLowerDeviceObject returnned NULL, stop"));
break;
}
} while (1);
LOG(("<==tree walk"));
}
static DECLCALLBACK(BOOLEAN) vboxUsbObjNamePrefixMatch(PUNICODE_STRING pName, PUNICODE_STRING pNamePrefix, BOOLEAN fCaseInSensitive)
{
LOG(("Matching prefix:"));
{
return FALSE;
}
if (!rc)
{
LOG(("prefix MATCHED!"));
return TRUE;
}
LOG(("prefix NOT matched!"));
return FALSE;
}
typedef struct VBOXUSBOBJNAMEPREFIXMATCHER
{
static DECLCALLBACK(VOID) vboxUsbObjDevNamePrefixMatcher(PDEVICE_OBJECT pDo, PUNICODE_STRING pName, PVOID pvMatcher)
{
}
typedef struct VBOXUSBOBJDRVOBJSEARCHER
{
static DECLCALLBACK(BOOLEAN) vboxUsbObjDevObjSearcherWalker(PDEVICE_OBJECT pTopDo, PDEVICE_OBJECT pCurDo, PVOID pvContext)
{
if (pCurDo->DriverObject
&& !RtlCompareUnicodeString(pData->pDrvName, &pCurDo->DriverObject->DriverName, TRUE /* case insensitive */))
{
LOG(("MATCHED driver:"));
{
VBOXUSBOBJNAMEPREFIXMATCHER Data = {0};
if (!NT_SUCCESS(Status))
{
return TRUE;
}
{
LOG(("VBOXUSBMONHUBWALK_F_FDO"));
{
LOG(("this is a PDO object, skip it and stop search"));
/* stop search as we will not find FDO here */
return FALSE;
}
LOG(("this is a FDO object, MATCHED!!"));
}
{
LOG(("VBOXUSBMONHUBWALK_F_PDO"));
{
LOG(("this is a FDO object, skip it and continue search"));
/* continue seach since since this could be a nested hub that would have a usbhub-originated PDO */
return TRUE;
}
LOG(("this is a PDO object, MATCHED!!"));
}
}
else
{
LOG(("VBOXUSBMONHUBWALK_F_ALL"));
LOG(("either PDO or FDO, MATCHED!!"));
}
/* ensure the dev object is not destroyed */
/* we are done */
return FALSE;
}
else
{
if (pCurDo->DriverObject)
{
{
LOG(("driver name not match, was:"));
LOG(("but expected:"));
}
else
{
LOG(("driver name is zero, Length(%d), Buffer(0x%p)",
}
}
else
{
LOG(("driver object is NULL"));
}
}
return TRUE;
}
{
szStandardHubName.Length = 0;
szStandardHubName.Buffer = 0;
for (int i = 0; i < 16; i++)
{
char szHubName[32] = {0};
UnicodeName.Length = 0;
if (Status == STATUS_SUCCESS)
{
if (Status == STATUS_SUCCESS)
{
VBOXUSBOBJDRVOBJSEARCHER Data = {0};
{
{
LOG(("the walker said to stop"));
break;
}
LOG(("going forward.."));
}
else
{
LOG(("no hub driver obj found"));
}
/* this will dereference both file and dev obj */
}
else
{
}
}
else
{
}
}
}
typedef struct VBOXUSBMONFINDHUBWALKER
{
static DECLCALLBACK(BOOLEAN) vboxUsbMonFindHubDrvObjWalker(PFILE_OBJECT pFile, PDEVICE_OBJECT pTopDo, PDEVICE_OBJECT pHubDo, PVOID pvContext)
{
if(pDrvObj)
{
return FALSE;
}
WARN(("null pDrvObj!"));
return TRUE;
}
{
szStandardHubName.Length = 0;
szStandardHubName.Buffer = 0;
LOG(("Search USB hub"));
VBOXUSBMONFINDHUBWALKER Data = {0};
{
}
else
{
WARN(("no hub driver object found!"));
}
}
/* 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)
{
switch(pSl->MinorFunction)
{
case IRP_MN_QUERY_DEVICE_TEXT:
{
{
{
/* IRQL should be always passive here */
{
LOG(("DeviceTextLocationInformation"));
break;
case DeviceTextDescription:
LOG(("DeviceTextDescription"));
{
if (!pId)
{
AssertFailed();
break;
}
LOG(("NEW szDeviceTextDescription"));
}
else
{
}
break;
default:
break;
}
}
else
}
break;
}
case IRP_MN_QUERY_ID:
{
{
#endif
{
/* IRQL should be always passive here */
{
case BusQueryInstanceID:
LOG(("BusQueryInstanceID"));
break;
case BusQueryDeviceID:
{
LOG(("BusQueryDeviceID"));
if (!pId)
{
WARN(("ExAllocatePool failed"));
break;
}
{
if(Status == STATUS_SUCCESS)
{
}
else
{
}
break;
}
break;
}
case BusQueryHardwareIDs:
{
LOG(("BusQueryHardwareIDs"));
while(*pId) //MULTI_SZ
{
pId++;
}
#endif
if (!pId)
{
WARN(("ExAllocatePool failed"));
break;
}
{
if(Status == STATUS_SUCCESS)
{
}
else
{
}
break;
}
LOG(("NEW BusQueryHardwareIDs"));
while(*pTmp) //MULTI_SZ
{
pTmp++;
}
#endif
break;
}
case BusQueryCompatibleIDs:
LOG(("BusQueryCompatibleIDs"));
while(*pId) //MULTI_SZ
{
pId++;
}
#endif
{
if (!pId)
{
WARN(("ExAllocatePool failed"));
break;
}
LOG(("NEW BusQueryCompatibleIDs"));
while(*pTmp) //MULTI_SZ
{
pTmp++;
}
#endif
}
else
{
}
break;
}
}
else
{
}
}
break;
}
{
{
case BusRelations:
{
LOG(("BusRelations"));
{
{
{
}
}
else
}
break;
}
case TargetDeviceRelation:
LOG(("TargetDeviceRelation"));
break;
case RemovalRelations:
LOG(("RemovalRelations"));
break;
case EjectionRelations:
LOG(("EjectionRelations"));
break;
}
break;
}
{
{
{
}
else
}
break;
}
default:
break;
#endif
} /*switch */
}
{
/* 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.
* */
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 */
// Assert(!pDevObj || pDevObj == pRealDevObj);
// Assert(pSl->DeviceObject == pDevObj);
switch(pSl->MinorFunction)
{
case IRP_MN_QUERY_DEVICE_TEXT:
case IRP_MN_QUERY_ID:
#endif
{
}
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:
{
}
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_RESOURCES:
{
}
else
{
AssertFailed();
}
break;
default:
break;
}
LOG(("<==PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x), Sl PDO(0x%p), Compl PDO(0x%p)",
#ifdef DEBUG_misha
#endif
NTSTATUS Status = VBoxUsbHookRequestComplete(&g_VBoxUsbMonGlobals.UsbHubPnPHook.Hook, pDevObj, pIrp, pRequest);
#ifdef DEBUG_misha
{
}
#endif
return Status;
}
/**
* Device PnP hook
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
LOG(("==>PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x)", vboxUsbDbgStrPnPMn(IoGetCurrentIrpStackLocation(pIrp)->MinorFunction), pDevObj, pIrp, pIrp->IoStatus.Status));
{
WARN(("VBoxUsbHookRetain failed"));
}
PVBOXUSBHUB_PNPHOOK_COMPLETION pCompletion = (PVBOXUSBHUB_PNPHOOK_COMPLETION)VBoxUsbMonMemAlloc(sizeof (*pCompletion));
if (!pCompletion)
{
WARN(("VBoxUsbMonMemAlloc failed"));
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS Status = VBoxUsbHookRequestPassDownHookCompletion(&g_VBoxUsbMonGlobals.UsbHubPnPHook.Hook, pDevObj, pIrp, VBoxUsbPnPCompletion, &pCompletion->Rq);
if (Status != STATUS_PENDING)
{
}
else
{
LOG(("Request pending"));
}
#endif
return Status;
}
static NTSTATUS vboxUsbMonHookCheckInit()
{
static bool fIsHookInited = false;
if (fIsHookInited)
{
LOG(("hook inited already, success"));
return STATUS_SUCCESS;
}
if (pDrvObj)
{
fIsHookInited = true;
LOG(("SUCCESS"));
return STATUS_SUCCESS;
}
WARN(("hub drv obj not found, fail"));
return STATUS_UNSUCCESSFUL;
}
static NTSTATUS vboxUsbMonHookInstall()
{
#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
return STATUS_SUCCESS;
#else
{
WARN(("trying to hook usbhub pnp after the unhook failed, do nothing & pretend success"));
return STATUS_SUCCESS;
}
#endif
}
static NTSTATUS vboxUsbMonHookUninstall()
{
#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
return STATUS_SUCCESS;
#else
if (!NT_SUCCESS(Status))
{
AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
}
return Status;
#endif
}
static NTSTATUS vboxUsbMonCheckTermStuff()
{
FALSE, /* BOOLEAN Alertable */
NULL /* IN PLARGE_INTEGER Timeout */
);
do
{
if (--g_VBoxUsbMonGlobals.cOpens)
break;
if (!NT_SUCCESS(tmpStatus))
{
/* this means a driver state is screwed up, KeBugCheckEx here ? */
}
} while (0);
return Status;
}
static NTSTATUS vboxUsbMonCheckInitStuff()
{
FALSE, /* BOOLEAN Alertable */
NULL /* IN PLARGE_INTEGER Timeout */
);
if (Status == STATUS_SUCCESS)
{
do
{
if (g_VBoxUsbMonGlobals.cOpens++)
{
break;
}
Status = VBoxUsbFltInit();
if (NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
LOG(("succeded!!"));
break;
}
else
{
}
}
else
{
}
}
else
{
}
} while (0);
}
else
{
}
return Status;
}
{
if (pFileCtx)
{
if (Status == STATUS_SUCCESS)
{
if (Status == STATUS_SUCCESS)
{
LOG(("succeeded!!"));
return STATUS_SUCCESS;
}
else
{
WARN(("VBoxUsbFltCreate failed"));
}
}
else
{
WARN(("vboxUsbMonCheckInitStuff failed"));
}
}
else
{
WARN(("VBoxUsbMonMemAllocZ failed"));
}
return Status;
}
{
if (Status == STATUS_SUCCESS)
{
/* ignore the failure */
}
return Status;
}
{
LOG(("VBoxUsbMonClose"));
if (Status != STATUS_SUCCESS)
{
{
LOGREL(("ulPreventUnloadOn not set, preventing unload"));
NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
}
else
{
WARN(("ulPreventUnloadOn already set"));
}
LOG(("success!!"));
}
return Status;
}
{
LOG(("VBoxUSBMonCreate"));
{
WARN(("trying to open as a directory"));
return STATUS_NOT_A_DIRECTORY;
}
if (Status == STATUS_SUCCESS)
{
}
else
{
}
return Status;
}
{
return rc;
}
{
#ifdef VBOXUSBMON_DBG_NO_FILTERS
++idDummy;
return VINF_SUCCESS;
#else
return rc;
#endif
}
{
#ifdef VBOXUSBMON_DBG_NO_FILTERS
return VINF_SUCCESS;
#else
return rc;
#endif
}
{
return Status;
}
static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
{
return Status;
}
static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer, ULONG cbOutBuffer, ULONG_PTR* pInfo)
{
switch (Ctl)
{
{
LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
{
WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
break;
}
break;
}
{
int rc;
{
WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
break;
}
break;
}
{
{
WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
break;
}
if (cbOutBuffer)
{
/* we've validated that already */
}
break;
}
{
{
WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
cbInBuffer, 0, cbOutBuffer, 0));
break;
}
LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
break;
}
{
{
WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected >= %d.",
break;
}
if (NT_SUCCESS(Status))
{
}
else
{
}
break;
}
{
{
WARN(("SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
break;
}
break;
}
default:
break;
}
return Status;
}
{
if (NT_SUCCESS(Status))
{
&Info);
}
else
{
}
return Status;
}
{
*pInfo = 0;
switch (Ctl)
{
{
LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
if (!pvBuffer)
{
WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
break;
}
break;
}
{
LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
if (!pvBuffer)
{
WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
break;
}
break;
}
{
LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
if (!pvBuffer)
{
WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
break;
}
break;
}
default:
{
break;
}
}
return Status;
}
{
if (NT_SUCCESS(Status))
{
&Info);
}
return Status;
}
/**
* Unload the driver.
*
* @param pDrvObj Driver object.
*/
{
/* cleanup the logger */
if (pLogger)
{
}
if (pLogger)
{
}
}
/**
* Driver entry point.
*
* @returns appropriate status code.
* @param pDrvObj Pointer to driver object.
* @param pRegPath Registry base path.
*/
{
RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
RTLogDestinations(0, "debugger");
#endif
/* create the device */
if (NT_SUCCESS(Status))
{
Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if (NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
return STATUS_SUCCESS;
}
}
}
return Status;
}