Now we can use Python + LibUSB to send Control Endpoint packets with the command

ctrl_transfer( bmRequestType, bmRequest, wValue, wIndex, nBytes)

This command can do both sending and receiving depending on what bmRequestType says (input or output). Still, there is a lot of options here. To send the right command you need to know theRequestType and the right Request and ther right Value as well as the Index and how many bytes to read or write.

If we were totally on our own, we would start by trying to read data from the device. This means we have to set the RequestType first

Direction Type Recipient
D7 D6 D5 D4 D3 D2 D1 D0

For bmRequestType the value passed is very structured so that's not as hard to guess. (See lvr.com for more information )

  • Bits 2, 3 and 4 are reserves so set them to 0.
  • The direction is set by bit #7, 0 is a 'write' out to the device, 1 is a 'read' from the device
  • The 'type' of message is two bits, 0 = Standard, 1 = Class, 2 = Vendor, 3 = Reserved. For many devices that are non-standard, you'll probably want 2 for vendor type. If its a more standard type of device, like a camera or mic, try 0 or 1. 3 Is unused
  • The last two bits are usd to determine the recipient for the message 0 = Device, 1 = Interface, 2 = Endpoint, 3 = Other. Go with 0 to start, you can try 2 if there are other endpoints

The safest thing to do is read data (no way to overwrite anything or configure) you can do that by sending packets with 0b11000000 (Read Vendor data from Device) = 0xC0.

If I were to write a fuzzer, I'd start by setting Index to 0 and iterating through all the byte values (255 different values) of bmRequest and the first few hundred wValues. Its pretty safe to just read random data to a USB device. Start by reading one byte to see if anything shows up, then increase the value

import usb.core
import usb.util
import sys
# find our device
dev = usb.core.find(idVendor=0x045e, idProduct=0x02B0)
# was it found?
if dev is None:
    raise ValueError('Device not found')
# set the active configuration. With no arguments, the first
# configuration will be the active one
# Let's fuzz around! 
# Lets start by Reading 1 byte from the Device using different Requests
# bRequest is a byte so there are 255 different values
for bRequest in range(255):
        ret = dev.ctrl_transfer(0xC0, bRequest, 0, 0, 1)
        print "bRequest ",bRequest
        print ret
        # failed to get data for this request

Looks like Request values 0, 5, 16, 50, 54, 64, 80 and 112 all return some sort of data. The rest had nothing to read

Next we'll try to read more data by changing the last argument to 100 bytes

OK lots of data, but what does it mean? This is where some guessing based on the device itself would come in handy. I'm terribly lazy though and if given an option to avoid a lot of guesswork, I'll take it!
Last updated on 2015-11-19 at 06.18.40 PM Published on 2012-07-29 at 11.58.38 AM