In the previous post we have seen the basical skeleton of a PnP Filter Driver, going a bit Off Topic, now we will see the Basical Data Transaction Capture Mechanism, so we will directly consider only IRP_MJ_DEVICE_CONTROL, to work with IOCTLs and pass down other IRPs.
After hooking the Next Lower Device (FDO) Dispatch Table obviously we have to implement the IOCTL management system.
NTSTATUS MyInternalIOCTLCompletion(PDEVICE_OBJECT fido, PIRP Irp, PVOID inContext)
PIRP_STACK_CONTEXT Context = (PIRP_STACK_CONTEXT )inContext;
Context->Stack->CompletionRoutine = Context->CompletionRoutine;
Context->Stack->Context = Context->Context;
Context->Stack->Control = Context->Control;
now we are in the IOCTL Routine so we can dump/log all what we want 🙂
Finally we have to spent some words about FDO Hooking and next you will be able to understand the basical structure of a Filter Driver.
Locating our FIDO DEVICE_EXTENSION, is accomplished by replacing the IRP_MJ_INTERNAL_DEVICE_CONTROL callback of the FDO (not our FDO), and then the FDO AttachedDevice field can be used to recover our filter’s device object.
FdoHookDispatchPnp(PDEVICE_OBJECT DeviceObject, // The FDO Device Object
PDEVICE_OBJECT FilterDeviceObject = DeviceObject->AttachedDevice;
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION )FilterDeviceObject->DeviceExtension;
// Provide Additional Information for IRP_MN_QUERY_INTERFACE
if( irpStack->MinorFunction == IRP_MN_QUERY_INTERFACE )
and final FDO Restoring
// Call The FDO’s Original IRP_MJ_PNP Callback
return (deviceExtension->OriginalLowerDriverObject)->MajorFunction[IRP_MJ_PNP]( DeviceObject,Irp );
Here ends our basical filter driver, now we need to study how to Dump and Interpret this Collected Data, to do this we have to analyse and study the URB format. An URB (USB Request Block) is the basic structure for every USB request, specifically this request is used to send
or receive data to or from a specific USB endpoint on a specific USB device in an asynchronous manner.
The best source to study URB structs is obviously MSDN the first important structure is _URB_HEADER, that provides basic information about the host controller driver.
void DumpURB(struct Buffer *b, PURB pUrb, BOOLEAN bReturnedFromHCD)
USHORT wFunction, wLength;
wFunction = pUrb->UrbHeader.Function;
wLength = pUrb->UrbHeader.Length;
lUsbdStatus = pUrb->UrbHeader.Status;
Status values are defined in usbdi.h as USBD_STATUS_XXX, and these values can be printed, so here begins our real Sniff Procedure 🙂
#define URB_SELECT_CONFIGURATION_SIZE 24
struct _URB_SELECT_CONFIGURATION *pSelectConfiguration = (struct
if(pSelectConfiguration->Hdr.Length < URB_SELECT_CONFIGURATION_SIZE)
BufferPrintf(b,”!!! Hdr.Length is wrong! (is: %d, should be at least: %d)\n”,
PUSB_CONFIGURATION_DESCRIPTOR pCD =
and now we can print the entire pCD struct such as: pCD->bLength, pCD->bDescriptorType, pCD->wTotalLength, pCD->bNumInterfaces, pCD->bConfigurationValue, pCD->iConfiguration, pCD->bmAttributes, pCD->MaxPower,
For interface information, we can print PUSBD_INTERFACE_INFORMATION as in the previous way any others informations can be retrived in this way, now we will consider the
URB_FUNCTION_SELECT_INTERFACE Switch’s case, where we can access to _URB_SELECT_INTERFACE, USB client drivers set up this structure to select an alternate setting for an interface or to change the maximum packet size of a pipe in the current configuration on a USB device.
URB_FUNCTION_SELECT_INTERFACE can be accessed in this way:
struct _URB_SELECT_INTERFACE *pSelectInterface = (struct _URB_SELECT_INTERFACE *) pUrb;
and next we can enumerate all members in this way..
The same thing can be done for:
and Data Transaction Structs:
that are a bit more complex because is used by USB client drivers to perform data transaction.
struct _URB_CONTROL_TRANSFER *pControlTransfer = (struct _URB_CONTROL_TRANSFER *) pUrb;
the complexity is caused by the TransferBuffer, that is a pointer to a resident buffer for the transfer, can be a resident buffer or an MDL.
So we have to dump, separately the TransferBuffer and/or the PipeHandle.
In the next post we will see some other struct and some other particularity to consider during the Sniffing Process 😉
See you to the next post.. 🙂