Usb Reverse Engineering 2011 06 08 Usb
Usb Reverse Engineering 2011 06 08 Usb
Device Drivers
Jan Hauffa
([email protected])
Outline
1. What's this all about?
2. USB fundamentals
3. Sniffing and analyzing USB traffic
4. Case study
Why reverse engineering?
● hardware manufacturers that...
● only provide Windows drivers
● only provide Linux binary drivers for x86
● don't provide any documentation
→ http://linuxdriverproject.org/foswiki/bin/view/Main/DriversNeeded
● „repurposing“ of hardware
> What exactly are you trying to protect?
> The communication protocol, some image processing
> functions, the firmware?
http://permalink.gmane.org/gmane.comp.graphics.scanning.sane.devel/19722
Why USB devices?
● basic USB protocol is well documented and
easy to understand
● USB devices...
● can be programmed from user space
● can be reliably „passed through“ to an OS running
inside a VM
● sniffing USB traffic is easy:
● can be done in software
● FOSS tools available
Why not disassemble the driver?
● legal „gray area“
● http://en.wikipedia.org/wiki/Reverse_engineering#Legality
● some open source projects do not accept code
based on disassembling proprietary software
● http://kerneltrap.org/node/6692
● code obfuscation, anti-debugging techniques
● http://www.ossir.org/windows/supports/2005/2005-11-07/EADS-
CCR_Fabrice_Skype.pdf
● traffic analysis may actually be less work
USB fundamentals, part 1
● device provides 1 - 32 endpoints for
communication – also called pipes
● transfer modes:
● isochronous: guaranteed bandwidth, bounded
latency, possible data loss
● interrupt: bounded latency
● bulk: no guarantees on bandwidth or latency
● control: specific request/response message format
USB fundamentals, part 2
● endpoints for control transfers are bi-directional,
all others uni-directional
● every device supports control transfers on
endpoint 0
● host can request a configuration descriptor that
contains information on the other endpoints
USB fundamentals, part 3
● structure of a USB control transfer:
Offset Field Size Value Description
0 bmRequestType 1 Bit-Map D7 Data Phase
Transfer Direction
0 = Host to Device
1 = Device to Host
D6..5 Type
0 = Standard
1 = Class
2 = Vendor
3 = Reserved
D4..0 Recipient
0 = Device
1 = Interface
2 = Endpoint
3 = Other
4..31 = Reserved
1 bRequest 1 Value Request
2 wValue 2 Value Value
4 wIndex 2 Index or Offset Index
6 wLength 2 Count Number of bytes to
transfer if there is a
data phase
http://www.beyondlogic.org/usbnutshell/usb1.shtml
Sending USB requests
Windows:
UsbBuildVendorRequest(urb, prepare URB
Function, // bits 0..5 of bmRequestType
sizeof(struct URB_CONTROL_TRANSFER),
TransferFlags, // bit 7 of bmRequestType
0,
bRequest,
wValue,
wIndex,
buffer, NULL, wLength, NULL);
status = CallUSBDI(dev, urb); submit URB
libusb:
libusb_control_transfer (dev,
bmRequestType,
bRequest,
wValue,
wIndex,
buffer, wLength,
timeout);
Sniffing USB traffic
● SniffUSB 2.0
● http://www.pcausa.com/Utilities/UsbSnoop/
● installs a „filter“ between the device driver and
the USB stack, writes all USB requests to disk
[1570 ms] UsbSnoop - FilterDispatchAny(8c37bfd2) : IRP_MJ_INTERNAL_DEVICE_CONTROL
[1570 ms] UsbSnoop - FdoHookDispatchInternalIoctl(8c37c1ea) :
fdo=84432030, Irp=8458b4b8, IRQL=0
[1570 ms] >>> URB 36 going down >>>
-- URB_FUNCTION_VENDOR_DEVICE:
TransferFlags = 00000000 (USBD_TRANSFER_DIRECTION_OUT, ~USBD_SHORT_TRANSFER_OK)
TransferBufferLength = 00000001
TransferBuffer = 85c70df8
TransferBufferMDL = 00000000
00000000: c0
UrbLink = 00000000
RequestTypeReservedBits = 00000000
Request = 00000002
Value = 00000000
Index = 00000041
[1581 ms] UsbSnoop - MyInternalIOCTLCompletion(8c37c126) :
fido=00000000, Irp=8458b4b8, Context=8462bd60, IRQL=2
[1581 ms] <<< URB 36 coming back <<<
-- URB_FUNCTION_CONTROL_TRANSFER:
PipeHandle = 846451bc
TransferFlags = 0000000a (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK)
TransferBufferLength = 00000001
TransferBuffer = 85c70df8
TransferBufferMDL = 84649ab8
OmniVision OV511+
Event 6:
level = 0x01
req_type = URB_FUNCTION_VENDOR_DEVICE
req_TransferFlags = 0
req_TransferBufferLength = 0x01
req_data =
3d
req_Request = 0x02
req_Value = 0
req_Index = 0x50
res_type = URB_FUNCTION_CONTROL_TRANSFER
res_TransferFlags = 0x0a
res_TransferBufferLength = 0x01
Event 7:
level = 0x01
what you can't see:
req_type = URB_FUNCTION_VENDOR_DEVICE
req_TransferFlags = 0x01
req_TransferBufferLength = 0x01
● This is a log of plugging in the
req_Request = 0x03
req_Value = 0 device and capturing a single frame.
req_Index = 0x5f ● constant stream of control transfers
res_type = URB_FUNCTION_CONTROL_TRANSFER
res_TransferFlags = 0x0b as soon as the device is plugged in
res_TransferBufferLength all0x01
● = of them single byte read/write
res_data = requests as shown here
00
Event 6:
level = 0x02
OV511_command = write_register
regIdx = 0x50
value = 0x3d
Event 7:
level = 0x02
OV511_command = read_register
regIdx = 0x5f
value = 0
Event 8:
level = 0x02
OV511_command = write_register
regIdx = 0x41
value = 0xc0
Event 9:
level = 0x02
OV511_command = write_register
regIdx = 0x44
value = 0xc1
Event 10:
level = 0x02
OV511_command = write_register
regIdx = 0x42
value = 0x12
Event 348:
level = 0x01
req_type = URB_FUNCTION_ISOCH_TRANSFER
req_endpoint = 0x51
req_TransferFlags = 0x01
req_TransferBufferLength = 0x00006020
req_StartFrame = 0x00011b96
req_NumberOfPackets = 0x20
req_IsoPacket[0].Offset = 0
req_IsoPacket[0].Length = 0
req_IsoPacket[1].Offset = 769
req_IsoPacket[1].Length = 0
...
res_type = URB_FUNCTION_ISOCH_TRANSFER
res_endpoint = 0x51
res_TransferFlags = 0x01
res_TransferBufferLength = 0x00006020
res_StartFrame = 0x00011b76
res_NumberOfPackets = 0x20
res_ErrorCount = 0
res_IsoPacket[0].Offset = 0
res_IsoPacket[0].Length = 769
res_IsoPacket[0].Status = 0
res_IsoPacket[0].Status_data =
00 00 00 00 00 00 00 00 59 c8 f4 80 3e 80 01 02
01 01 02 42 1e c0 ff fe c7 f9 51 0d 01 fc 02 fe
memcpy(buf, "\x3d", 0x0000001);
ret = libusb_control_transfer(devh,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
0x0000002, 0x0000000, 0x0000050, buf, 0x0000001, 1000);
usleep(111*1000);
ret = libusb_control_transfer(devh,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_IN,
0x0000003, 0x0000000, 0x000005f, buf, 0x0000001, 1000);
printf("control msg returned %d, bytes: ", ret);
print_bytes(buf, ret);
printf("\n");
usleep(39*1000);
stream = fopen ("frame.ppm", "wb");
snprintf (header, sizeof (header), "P5\n%d %d\n255\n", 768, 128);
fwrite (header, 1, strlen (header), stream);
libusb_free_transfer(iso_trans);
}
fclose (stream);
result:
Ubuntu 10.10:
In this case, we can „cheat“ by reading the manual
(prepare for the worst):
http://mxhaard.free.fr/spca50x/Doc/Omnivision/ds_511P.pdf