Installing Python & PyUSB
Now we need to start sending commands to this USB device! The fastest and easiest way we know to do this is to use LibUSB with a scripting language such as Python. There are LibUSB bindings for C and C++ and Perl but I happen to like Python so follow along!
If you don't have python installed, do that now.
Next up, install PyUSB by downloading it and running python setup.py install in the expanded directory
Attaching to the device
Lets go with the first simple example program which will just attach itself to the device in question
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
dev.set_configuration()
print "all done"
Note that we're looking for that same VID/PID we found originally. We're not sending any data around, yet. Verify that when the device is not plugged in you get an errorand if it is plugged in, there is no error
What messages to send?
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 the RequestType 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
dev.set_configuration()
# 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):
try:
ret = dev.ctrl_transfer(0xC0, bRequest, 0, 0, 1)
print "bRequest ",bRequest
print ret
except:
# failed to get data for this request
pass
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!
No comments:
Post a Comment