On USB Coding #5

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

One Response to On USB Coding #5

  1. Alex says:

    Hi dude! I’m quite surprised somebody really decided to make such a review of a USB lower filter-driver. I’m doing the same just now (except it in russian) and have some particular questions about it.
    First of all i don’t understand, why you don’t use the FilterDispatchAny routine in your DriverEntry function, but still have it for some reasons. In the sources i have (it’s Benoit’s driver-filter, http://benoit.papillault.free.fr/index.php.en ) this routine is actually used (for every major function, except IRP_MJ_PNP and IRP_MJ_POWER).
    The second thing i didn’t get is about the DispatchPnP routine. IRP_MJ_PNP is actually set to be handled by the FilterDispatchPnP routine, but FilterDispatchAny has the branch to handle IRP_MJ_PNP too. Why???
    Next, I didn’t get it, where my driver actually is??? It’s said to be lower-filter and installed as lower-filter, but sources say FDO appears to be our LOWER device, so the truth is that my driver is UPPER-filter driver. How is it possible? May be my understanding of WMD is inaccurate?
    And finally, WHY IN THE WORLD we have to hook FDO dispatch tables just because of 2 IRP function codes??? Why can we not handle them by our FiDO?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: