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.
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
&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:
Now is time to Add our Filter, so let’s study FilterAddDevice:
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.
ULONG deviceType = FILE_DEVICE_UNKNOWN;
deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
deviceType = deviceObject->DeviceType;
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
NULL, // No Name
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 |
Device properties can be retrieved by calling
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
(PVOID * )&( (deviceExtension->LowerDeviceObject)->DriverObject ),
Now we can grab all IRPs with FilterDispatchAny
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)
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.
if( irpStack->MinorFunction == IRP_MN_QUERY_INTERFACE )
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.. 🙂