Skip to content

Reverse Engineering USB Protocol

Luca Weiss edited this page Aug 6, 2023 · 14 revisions

Prerequisites

There are a few things you will need when reverse engineering a USB protocol device:

  • The device you want to reverse
  • If the device you're reversing is a mouse or a keyboard, you will need additional mouse/keyboard to avoid generating useless traffic and to be able to actually use the computer when your device is passed through.
  • Hypervisor capable of passing through a USB device.
    • You could use VirtualBox with VirtualBox Extension Pack
      • Make sure to add the user to the vboxuser group sudo usermod -aG vboxusers <USERNAME> and then reboot/relogin or else VirtualBox may not recognize any USB devices
    • You could use KVM/QEMU inside of virt-manager. Here's how you'd pass through a USB device in it.
  • OS for the VM on the hypervisor - Windows 11 VM should work fine, however if the device/driver you're reversing only works in older version of Windows, then that's what you'll have to use.
  • The driver you're reversing installed on the VM.
  • Wireshark installed on the host

NOTE: Please make sure to save the captured data as .pcapng and not as .pcap! Opening a .pcap and saving it as .pcapng is not sufficient.

Before attempting to do anything, you will need to pass the USB device of whatever you are reverse engineering through to the VM. Make sure the driver/software you are reversing can recognize the device.

I would also familarise yourself with the wireshark interface as it will be used extensively.

Phase 1 - Research

First, you want to learn as much about the device as you can, you need to understand its capabilities before trying to re-implement them. The main thing would be a list of features.

Razer BlackWidow Chroma will be used as an example throughout the rest of this page.

The Chroma has full RGB backlit keys which are each individually addressable. This tells us two things, firstly that as the keyboard can access a whole spectrum of colours, it wouldn't be a bad assumption to think that the keyboard is using simple RGB encoding (aka 1byte = RED, 1byte = GREEN, 1byte = BLUE, 3bytes total) to represent colours, similar to how you would represent colour in HTML, like #FF0000 (RED).

Secondly as each of the keys are individually addressable, this means each key is probably represented by an RGB backlit value. So how might they accomplish this feature? you could send a Key ID and an RGB value to the keyboard to set up a key, this is OK but would take ages for lots of keys. You could send all of the keys RGB values but that's a lot of data. What we have found is that you set the keys on the keyboard on a row by row basis.

The Chroma has brightness settings, so it stands to reason that you would send a brightness value to the keyboard. Etc...

For documentation, you could go through the Razer Synapse app and take screenshots of all the various features and buttons you can click on.

Insert snaps of razer synapse app options #TODO

Phase 2 - Setup

To even be able to properly start Wireshark without having to resort to launching it as root(do not do that), you will need to add your user account to the wireshark group and also use setcap to be able to read raw packets. Just execute the following commands while replacing c0rn3j with your own username:

$ sudo gpasswd -a c0rn3j wireshark
$ sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_DAC_OVERRIDE+eip' /usr/bin/dumpcap

You now have to relog or reboot for the group permissions to take effect.

To be able to monitor USB traffic, you need to load a kernel module(You will need to execute it again upon rebooting):

$ sudo modprobe usbmon

You are now ready to start Wireshark, do so. You should be able to see usbmon interfaces in the interfaces list. To know which one to pick, you need to know which bus it is on.

$ lsusb
Bus 005 Device 003: ID 1532:0203 Razer USA, Ltd

As you can see, the bus ID is 5 and the device ID is 3. Other useful things to note is the USB Vendor ID - 0x1532 and the USB Product ID - 0x0203.

Pick the usbmon interface with the same number as the bus ID of your device.

Now you should be able to see all traffic on the bus, which is likely a lot if you have more USB devices. You need to filter out the unnecessary devices, to do so, you need to create a filter in Wireshark. To create one, you will need the USB bus ID and the USB device ID from lsusb which you've ran before.

Using the filter below will filter out all irrelevant USB devices:

usb.bus_id == 5 && usb.device_address == 3

Pictures of greyed out areas like interface list, the checkboxes etc...

Open Synapse and set the device to its most inert state so in the case of the keyboard I set it to the None effect.

Phase 3 - Reverse Engineering

Once Wireshark is running, Synapse is open and controlling the device, we are good to start. Now, here is where the repetitive fun begins. When the Razer Synapse app sends commands to the keyboard, it uses a specific format. From that we can hopefully create a compatible driver.

Clear any data Wireshark has captured, (a green lopsided triangle with a restart circular arrow). Now we are going to find out how the static mode is done. Switch the keyboard between the None and Static mode a few times. Stop the recording, save the packets. Repeat that a second time, starting at the beginning of the paragraph then move onto another feature.

Having to click on every packet to see data is very annoying, you want to add relevant information as a column. Adding data as column

Now you're able to relatively quickly compare and see what's happening. However, you may notice those three ever-present three dots at the end of any longer string - This is a limitation of Wireshark (see Bug 14265).

To read the data in full, you can either read the whole packet itself in Wireshark, or you can use tcpdump(and some bash voodoo) on a saved capture to generate human-readable text.

You can then throw that in a colorizer(which is a very simple PHP script) to strip it of all data except for the data portion of the packet and colorize depending if the command goes from host to device or device to host.

Colorizer site

Phase 4 - Decoding the protocol

The Razer protocol is built up of various fields.

  • Start Byte, its 0x00 if its PC -> Device, its 0x02 when its Device -> PC.
  • ID Byte, this is normally 0xFF but it can vary.
  • Reserved Bytes, this is 3 bytes of 0, 0x000000
  • Number of parameters byte, this signifies how many bytes are after the command byte.
  • Reserved Byte, not sure what this byte is for.
  • Command Byte, this signifies what action is being done, like change effect for example.
  • Sub command Byte, this is part of the parameters but has been split out for some reason.
  • Parameters, this is a varying number of bytes.

On the wireshark screen, you should see a field called left over data should have a string that looks something like the below (First line is static effect, to illustrate what is being done I added in the wave effect line).

00ff00000004030a0600ff00000000000000000000000000...
00ff00000002030a01020000000000000000000000000000...

You can decode this so that it makes more sense.

Start | ID | Reserved | Num Params | Reserved | Command | Sub Cmd | Params | Effect
00      FF    000000       04           03        0A         06     00FF00 | Static (Green RGB Value)
00      FF    000000       02           03        0A         01     02     | Wave (Direction Down)

The Number of bytes in Sub Cmd + Params should equal the number (remember its hex) in "Num Params"

After gathering lots of data, like the static effect in various colours, or the wave effect in each direction you can see we are starting to understand how the Synapse App works.