On USB Driver Coding #7

November 30, 2007

Hi,

In the previous post we have seen how to completely Dump an URB but as you should remember exists a particular structure _URB_CONTROL_TRANSFER, that USB client drivers sets up to transfer data to or from a control pipe. So we need to implement two external Dump Functions, DumpPipeHandle and DumpTransferBuffer.

void DumpPipeHandle(
in struct Buffer *b,const char *s,
in USBD_PIPE_HANDLE inPipeHandle
)
{
unsigned char ep;

if (GetEndpointInfo(inPipeHandle,&ep))
KPrintf(b,”%s = %p [endpoint 0x%x]\n”,s,inPipeHandle,ep);
else
KPrintf(b,”%s = %p\n”,s,inPipeHandle);
}

As you should remember it’s necessary for any PipeHandle to know the Endpoint. This can be accomplished by tracing the USBD_PIPE_HANDLE for each Endpoint (the number of Endpoints is declared at our choise (use for example MAXEP = 50).

Now is time to rip the TransferBuffer with DumpTransferBuffer:

void DumpTransferBuffer(
struct Buffer *b,
PUCHAR pBuffer,
PMDL pMdl,
ULONG uBufferSize,
in BOOLEAN bPrintHeader,
ULONG uBufferOffset = 0
)

if(pMdl)
{
DumpBuffer(b,pBuffer+uBufferOffset,uBufferSize);

else if(pMdl)
{
PUCHAR pMDLBuf = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl,NormalPagePriority);
if(pMDLBuf)
DumpBuffer(b,pMDLBuf+uBufferOffset,uBufferSize);
}
}

Here ends the USB Coding Series (source code I’ve used is taken from SniffUSB 2.0), but surely i’ll come back with other arguments related to USB..

See you to the next post.. :)


On USB Driver Coding #6

November 18, 2007

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.. :)


On USB Coding #5

November 17, 2007

Hi,

In the previous post We talked about the general foundamentals of USB Coding, many other thing should be said, for example about Power Management, but this will became a commentary upon USB_WDF.doc provided by Microsoft, our scope is a general overview of USB Coding and related USB Security/Forensics.

USB Security can be divided into two parts:

1) Intrusion Risk Security of USB Device Drivers
2) Forensics of USB Secure Devices

As you have seen USB Data Transactions does not have any kind of Encryption or Anti-Sniff protection by Default. Actually USB devices are extremely diffused,
these devs are applied in every kind of periferials, also in Secure Devices such as Fingerprint, Hardware Protectors, Sensitive Data Auth Devices and many many i
mportant as you know Usb is spreaded also for Mass Storage Devices. And where there are large collection of data born the problem of Sensitive Information Protection
and consequently we have to consider the Forensics Attacks that could be performed.

Around here you can download many USB-Tracers / UsbSniffers, such as Usbtrace and SnoopyPro, which sets an UsbFilter Driver and are make you able dump the entire code transaction. Let’s take a look at the general architecture..

Every Device Sniffer is based upon Data Filtering, because we need obviously to have a Dynamic Capture System, so he only way is to dispose a Filter Driver, this is the case of our USB Sniffer.

So we need to provide a Filter PNP Filter Driver, this kind of driver can be coded in two different ways, the Classical one without using any Framework, or by using KMDF, which is more easy and fast to code (just you have to provide WdfFdoInitSetFilter during EvtDeviceAdd Event)

We will follow the first “old school” way.

As every Filter Driver we the skeleton is based on a DeviceEntry, Device Adding, Dispatching and Unloading, so let’s view in detail each one of them.

NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)

in this nothing special, but inside the Method we have to provide, an IRP stack filtering context, that can be stored into an lookaside list

ExInitializeNPagedLookasideList(
&g_ContextLookasideList, NULL, NULL, 0, sizeof(IRP_STACK_CONTEXT),
IRP_STACK_CONTEXT_TAG, 0 );

After defining our context, as usual there is the Dispatch Procedure creation, in the particular case of a PNP Filter are used:

DriverObject->MajorFunction[IRP_MJ_PNP] = FilterDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER] = FilterDispatchPower;
DriverObject->DriverExtension->AddDevice = FilterAddDevice;

Cause the presence of multiple DeviceObject, is also necessary a Mutex, to synchronize all threads:

ExInitializeFastMutex(&ControlMutex);

Now is time to Add our Filter, so let’s study FilterAddDevice:

NTSTATUS
FilterAddDevice(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT PhysicalDeviceObject
)
At this point we are dealing directly with the PnP system, we need to determine if we need to be in the driver stack for the device, and next we have to initialize this device object.

{

PDEVICE_EXTENSION deviceExtension;
ULONG deviceType = FILE_DEVICE_UNKNOWN;

deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
deviceType = deviceObject->DeviceType;
ObDereferenceObject(deviceObject);

This because windows check if the filter attached to a storage device doesn’t specify the same DeviceType as the device that’s attempts to attach to it.

Now we can attach our Filter

IoCreateDevice (DriverObject,
sizeof (DEVICE_EXTENSION),
NULL, // No Name
deviceType,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);

Jump in the stack:

deviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack (deviceObject, PhysicalDeviceObject);

this is to define the supported I/O of our driver

deviceObject->Flags |= deviceExtension->LowerDeviceObject->Flags &
(DO_BUFFERED_IO | DO_DIRECT_IO |
DO_POWER_PAGABLE );

Device properties can be retrieved by calling

IoGetDeviceProperty(PhysicalDeviceObject,
DevicePropertyPhysicalDeviceObjectName, 0,
NULL, &nameLength);

Essentially we need a copy (slighty modified) of Next Lower Device’s Driver Object (FDO) so all transactions will pass also on our device. These are the modifications

deviceExtension->ModifiedLowerDriverObject.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = FdoHookDispatchInternalIoctl;
deviceExtension->ModifiedLowerDriverObject.MajorFunction[IRP_MJ_PNP] = FdoHookDispatchPnp;

and next we save our copy

InterlockedExchangePointer(
(PVOID * )&( (deviceExtension->LowerDeviceObject)->DriverObject ),
&deviceExtension->ModifiedLowerDriverObject
);

Now we can grab all IRPs with FilterDispatchAny

NTSTATUS
FilterDispatchAny (
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)

PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

if (irpStack->MajorFunction == IRP_MJ_PNP)
{
const char * MinorFunctionName = PnPMinorFunctionString(irpStack->MinorFunction);
if (MinorFunctionName != NULL)
LogTheCapturedIrp();

it’s important to remember that this is only a filter, so we don’t need to know ALL about data transactions, so unknown IRPs are delivered untouched.

NTSTATUS
FilterDispatchPnp (
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)

if( irpStack->MinorFunction == IRP_MN_QUERY_INTERFACE )
{
if( IsEqualGUIDAligned(
*irpStack->Parameters.QueryInterface.InterfaceType,
USB_BUS_INTERFACE_HUB_GUID
)
)
LogTheCapturedIrp();

with USB_BUS_INTERFACE_HUB_GUID we obtain the interface for USB Hub drivers

In the next post we will see the final part of the Filter Driver.

See you to the next post.. :)


Alive

November 16, 2007

Hi All,

I’m still Alive, soon I’ll publish the 5# part of USB Coding, and next a Rootkit Reversing Paper

See you to the next post


On USB Driver #4

November 3, 2007

Hi,

As you’ve seen, there are a series of Configuration to accomplish in order to have a fully transactional USB Driver, now is the time on I/O Configuration which sounds also as IO Implementation. Essentially we have to implement a Device IO Control Handler, in other words a mechanism that for each IO Request provides a different procedure.

This is the procedure to send an IO Request (directly taken from Microsoft USB_WDF Documentation):

  • Create the request or use a request that the framework delivered.
  • Set up the memory objects and buffers for the request.
  • Format the request.
  • Set an I/O completion callback for the request, if appropriate.
  • Send the request.

Let’s consider an InGoing requests are processed troughout In/Out Endpoints, so everything you put into In-Endpoint are pulled out the Out-Endpoint, we have the called Double Buffered Mechanism.

VOID
EvtDeviceIoWrite(
IN WDFQUEUE  Queue,
IN WDFREQUEST  Request,
IN size_t  Length
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
WDFMEMORY requestMem;

devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));

status = WdfRequestRetrieveInputMemory(Request, &requestMem);

status = WdfUsbTargetPipeFormatRequestForWrite(
devCtx->UsbBulkOutPipe,
Request,
requestMem,
NULL);

WdfRequestSetCompletionRoutine(Request,
EvtIoWriteComplete,
devCtx->UsbBulkOutPipe);

status = WdfRequestGetStatus(Request);
WdfRequestComplete(Request, status);

The EvtIoWriteComplete associated with our EvtDeviceIoWrite to fix unreferenced parameters as Context and Target.

Let’s se now an Asynchronous Read Request:

VOID EvtIoReadRequest(IN WDFQUEUE         Queue,
IN WDFREQUEST       Request,
IN size_t           Length
)
{
WDFUSBPIPE          pipe;
NTSTATUS            status;
WDFMEMORY           reqMemory;
PDEVICE_CONTEXT     pDeviceContext;

pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));
pipe = pDeviceContext->BulkReadPipe;
status = WdfRequestRetrieveOutputMemory(Request, &reqMemory);
status = WdfUsbTargetPipeFormatRequestForRead(pipe,
Request,
reqMemory,
NULL // Offsets
);
WdfRequestSetCompletionRoutine( Request,
EvtRequestReadCompletionRoutine,
pipe
);
if (WdfRequestSend(Request,
WdfUsbTargetPipeGetIoTarget(pipe),
WDF_NO_SEND_OPTIONS) == FALSE) {
status = WdfRequestGetStatus(Request);
goto Exit;
}
Exit:
if (!NT_SUCCESS(status)) {
WdfRequestCompleteWithInformation(Request, status, 0);
}
return;
}

[Code is taken from USB_WDF.doc]

As you’ve seen from the code, the involved Functions are WdfRequestRetrieveOutputMemory, WdfUsbTargetPipeFormatRequestForRead and WdfUsbTargetPipeGetIoTarget, for the Reading Process.

For write request we have WdfUsbTargetPipeFormatRequestForWrite and WdfUsbTargetPipeWriteSynchronously.

Here some coding reference:

http://www.microsoft.com/whdc/driver/wdf/default.mspx
http://www.microsoft.com/whdc/driver/wdf/kmdf-arch.mspx
http://go.microsoft.com/fwlink/?LinkId=80613

http://go.microsoft.com/fwlink/?LinkId=80619

With this post, ends the pure theoretical part, next post will contains 1 or 2 downloadable source code drivers, and observations about USB Security.

See you to the next post.. :)

http://go.microsoft.com/fwlink/?LinkId=83355


On USB Driver #3

November 1, 2007

Hi,

As you can see from the previous posts, Usb Architecture is more complex that other Serial Interfaces, due to layerization and complex packet architecture, so is necessary to have a well organized Coding Framework, to avoid and mitiagate the coding difficulties.
To implement a basical USB Device Driver, you need The latest version of the WDF DDK, Version 1.1 of the KMDF, and for professionally intersted subject in USB Coding OSR USB-FX2 learning kit.

Common WDM functions are great for USB Coding, but is any case coder needs to implements complex mechanisms, that can lead to heavy performances/working problems of the driver you coded, so Microsoft provided with KMDF an Encapsulation mechanism, that make more easy the code implementation.

The Device Descriptor

As previously said, the first operation to access an USB Device is to fill the Device Descriptor, that contains between the other settings the Number of Configurations, this field in our case is 1, because WDF supports only one onfiguration. Each Configuration has a Descriptor, that mantains trace of all Active Interfaces, these interfaces can have alternate settings, in other words various Endpoints, but at least you need for each interface Endpoint0. Pay attention to the USB Device Specifications, because many devices does not supports Multiple Alternate Settings, and your driver will deadly BSOD.

Now let’s see how to Configure our USB Device:

NTSTATUS ConfigOurUsb(WDFDEVICE Device, PDEVICE_CONTEXT DeviceContext)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT pDeviceContext;
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS usbConfig;

status = WdfUsbTargetDeviceCreate(Device,
WDF_NO_OBJECT_ATTRIBUTES,
&DeviceContext->UsbDevice);

WdfUsbTargetDeviceCreate, takes as input parameters a handle to the device object and a pointer to a WDF_OBJECT_ATTRIBUTES structure and returns a handle to a WDFUSBDEVICE object, if Configuration goes ok, we can Select the Choised Configuration, we will use:

WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE

WdfUsbTargetDeviceSelectConfig, creates WDF USB interface and pipe objects, and returns information about the specified configuration. Is importanto to say that the configuration process is done by filling WDF_USB_DEVICE_SELECT_CONFIG_PARAMS structs (which have In and Out params). As you have seen we uses INIT_SINGLE_INTERFACE, but KMDF have various INIT_XXX pre setted configs, so you’ve only to choise the one you need. There are also other functions to get informations about Configuration:

WdfUsbTargetDeviceRetrieveConfigDescriptor
WdfUsbTargetDeviceGetDeviceDescriptor
WdfUsbTargetDeviceGetInterface

After Usb Configuration, we can proceed with Pipe Configuration:

The framework creates a pipe object for each pipe in the setting, and gets access for each pipe with WdfUsbInterfaceGetConfiguredPipe.

As said for each alternate setting there are one or more associated Endpoints, so our driver needs an Enumeration Procedure to identify and use these pipes:

NTSTATUS ConfigUsbPipe(PDEVICE_CONTEXT DeviceContext)
{

NTSTATUS status = STATUS_SUCCESS;
WDF_USB_PIPE_INFORMATION pipeInfo;
WDFUSBPIPE pipe = NULL;
UCHAR index;

for(index=0; index < numberConfiguredPipes; index++) {
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
pipe = WdfUsbInterfaceGetConfiguredPipe( pDeviceContext->UsbInterface,
index, //PipeIndex,
&pipeInfo
);

if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) {
pDeviceContext->InterruptPipe = pipe;
}
if(WdfUsbPipeTypeBulk == pipeInfo.PipeType
&& WdfUsbTargetPipeIsInEndpoint(pipe)) {
pDeviceContext->BulkReadPipe = pipe;
}
if(WdfUsbPipeTypeBulk == pipeInfo.PipeType
&& WdfUsbTargetPipeIsOutEndpoint(pipe)) {
pDeviceContext->BulkWritePipe = pipe;
}
}

Obviously, the most important function in our case is, WdfUsbInterfaceGetConfiguredPipe(), the out Struct WDF_USB_PIPE_INFORMATION mantains trace of each Pipe Configuration.

Here finishes the third part of Usb Coding, in the next days I’ll complete the coding part (probably with a full working USB Driver, and finally a we will talk about Usb Forensics.

See you to the next post.. :)


Follow

Get every new post delivered to your Inbox.