On USB Driver Coding #6

Hi,

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)
{

PDEVICE_EXTENSION   deviceExtension;
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.

NTSTATUS
FdoHookDispatchPnp(PDEVICE_OBJECT DeviceObject,  // The FDO Device Object
PIRP Irp
)

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 )
{
if( IsEqualGUIDAligned(
*irpStack->Parameters.QueryInterface.InterfaceType,
USB_BUS_INTERFACE_HUB_GUID
)
)
_wanted_operations_

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;
USBD_STATUS lUsbdStatus;

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 🙂

switch(wFunction)
{
case URB_FUNCTION_SELECT_CONFIGURATION:
{
#define URB_SELECT_CONFIGURATION_SIZE 24

struct _URB_SELECT_CONFIGURATION *pSelectConfiguration = (struct

_URB_SELECT_CONFIGURATION*) pUrb;
BufferPrintf(b,”– URB_FUNCTION_SELECT_CONFIGURATION:\n”);
if(pSelectConfiguration->Hdr.Length < URB_SELECT_CONFIGURATION_SIZE)
{
BufferPrintf(b,”!!! Hdr.Length is wrong! (is: %d, should be at least: %d)\n”,

pSelectConfiguration->Hdr.Length,URB_SELECT_CONFIGURATION_SIZE);
return ;
}

PUSB_CONFIGURATION_DESCRIPTOR pCD =

pSelectConfiguration->ConfigurationDescriptor;

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,
pSelectConfiguration->ConfigurationHandle.

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:
URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL
URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL
URB_FUNCTION_GET_FRAME_LENGTH
URB_FUNCTION_SET_FRAME_LENGTH
URB_FUNCTION_GET_CURRENT_FRAME_NUMBER

and Data Transaction Structs:
URB_FUNCTION_ISOCH_TRANSFER
URB_FUNCTION_RESET_PIPE
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER
URB_FUNCTION_CONTROL_TRANSFER

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.. 🙂

2 Responses to On USB Driver Coding #6

  1. Pn says:

    Evil did you just see this project??
    #usblib (SharpUSBLib). One goal is to provide a platform independent (Linux/Win32 solution) USB access layer for .NET.

    http://www.icsharpcode.net/ 😉

  2. evilcodecave says:

    Yeah man it’s a good lib. for User Mode Apps 😉

Leave a reply to Pn Cancel reply