✨ Welcome to my USB 101 guide! ✨
Inspired by a Pluralsight course I completed on USB Forensics fundamentals, I decided to have a crack at explaining the basics myself. Being quite goal-oriented I knew that deciding on a blogpost will help me see through the learning and help with organising the new knowledge in my head – you know what they say, one doesn’t fully understand a topic until they are able to explain it to a five year old. While this might be a bit heavy for a five year old (although it probably would prove an efficient bedtime lullaby), it should convey what I learnt from only knowing of USB existence to understanding how it works under the hood.
Basics
First of all, let’s find out what the host machine sees about its USB without any USB devices being plugged in. For this we will need the lsusb
utility. Let’s try it out – make sure there’s nothing plugged into your USB ports.
lsusb
comes preinstalled on Linux. If you’re using macOS like myself, you need to install it first with brew install lsusb
. Run the lsusb -t
command to have the output presented as a tree. Other useful syntax can be lsusb | sort -k 2
which sorts the output by the bus number, or lsusb -v
, the verbose flag outputs more detailed information about the listed devices.
Kingas-MacBook-Pro:~ kingakieczkowska$ lsusb -t ... 0.Dev 000: iBridge Bus, /: Bus 000.Dev 001: USB 3.1 Bus, /: Bus 128.Dev 000: Apple T2 Controller, /: Bus 128.Dev 000: FaceTime HD Camera (Built-in), /: Bus 128.Dev 000: Ambient Light Sensor, /: Bus 128.Dev 000: Headset, |__ Bus 128.Dev 008: Apple Internal Keyboard / Trackpad, 480M /: Bus 128.Dev 000: Touch Bar Display, /: Bus 128.Dev 000: Touch Bar Backlight,
The output provides us with a bus number, device number, device name and the maximum signalling rate (speed) for the device – it’s the number at the end of a line, marked above in green. As this is probably way more devices than one can expect to see with literally nothing plugged into the machine, let’s look at them closely:
-
- iBridge is a USB device used internally as an interface to the TouchBar;
- USB 3.1 Bus is the bus to which all user devices will be connecting;
- all devices on Bus 128 are prebuilt into the machine.
I have a MacBook Pro so to actually be able to use my USB I need a freaking dongle (yes, I know, it’s ridiculous). Let’s see what changes when I plug in a dongle with another USB C port and two USB A ports (USB A is the ‘regular’ USB you most commonly see). The dongle also has an HDMI port which allows for USB C to HDMI connection.

Let’s check what appears in lsusb
after plugging in the dongle.
Kingas-MacBook-Pro:~ kingakieczkowska$ lsusb -t ... 0.Dev 000: iBridge Bus, Bus 000.Dev 001: 3.0 root hub, 5000M /: Bus 000.Dev 001: USB 3.1 Bus, |__ Bus 000.Dev 001: USB3.0 Hub, 5G |__ Bus 020.Dev 001: USB2.0 Hub, 480M |__ Bus 020.Dev 002: USB 2.0 BILLBOARD, 12M /: Bus 128.Dev 000: Apple T2 Controller, /: Bus 128.Dev 000: FaceTime HD Camera (Built-in), /: Bus 128.Dev 000: Ambient Light Sensor, /: Bus 128.Dev 000: Headset, |__ Bus 128.Dev 008: Apple Internal Keyboard / Trackpad, 480M /: Bus 128.Dev 000: Touch Bar Display, /: Bus 128.Dev 000: Touch Bar Backlight,
The lilac lines represent the root hub, two new hubs – USB 3.0 and USB 2.0 – and a new BILLBOARD device. Let’s look at them one by one.
The root hub is the USB interface directly with our machine. Next, you might wonder why after inserting a single USB dongle two USB hubs are showing up? This is because actually every USB3.0 includes a USB2.0 within it for the sake of backwards compatibility. The USB3.0 architecture is so vastly different from USB2.0 it would not be possible to use non-USB3.0-compatible devices with it otherwise. You can use a USB3.0 with a USB2.0 compatible device, but the device will only be able to achieve the USB2.0 maximum speed of 480 megabits per second (Mbps) and not USB3.0’s maximum of 5 gigabits per second (Gbps).
Lastly, there’s a USB2.0 BILLBOARD device showing as connected to the USB2.0 hub. This is actually a built-in device within the dongle which facilitates the USB C <-> HDMI connection.
Let’s plug in a USB memory stick into one of ports available in the hub / dongle to see what happens.
Kingas-MacBook-Pro:~ kingakieczkowska$ lsusb -t ... 0.Dev 000: iBridge Bus, Bus 000.Dev 001: 3.0 root hub, 5000M /: Bus 000.Dev 001: USB 3.1 Bus, |__ Bus 000.Dev 001: USB3.0 Hub, 5G |__ Bus 000.Dev 004: USB DISK, 5G |__ Bus 020.Dev 001: USB2.0 Hub, 480M |__ Bus 020.Dev 002: USB 2.0 BILLBOARD, 12M /: Bus 128.Dev 000: Apple T2 Controller, /: Bus 128.Dev 000: FaceTime HD Camera (Built-in), /: Bus 128.Dev 000: Ambient Light Sensor, /: Bus 128.Dev 000: Headset, |__ Bus 128.Dev 008: Apple Internal Keyboard / Trackpad, 480M /: Bus 128.Dev 000: Touch Bar Display, /: Bus 128.Dev 000: Touch Bar Backlight,
The bold magenta line shows the USB disk being present. Because it’s USB3.0 compatible, it is connected to the USB3.0 hub, and we can see its maximum signalling rate is at 5 gbps.
Next, let’s check what’s going to happen when we connect an old keyboard.
Kingas-MacBook-Pro:~ kingakieczkowska$ lsusb -t ... 0.Dev 000: iBridge Bus, Bus 000.Dev 001: 3.0 root hub, 5000M /: Bus 000.Dev 001: USB 3.1 Bus, |__ Bus 000.Dev 001: USB3.0 Hub, 5G |__ Bus 000.Dev 003: USB DISK, 5G |__ Bus 020.Dev 001: USB2.0 Hub, 480M |__ Bus 020.Dev 005: Composite Device, 1.5M |__ Bus 020.Dev 003: USB 2.0 BILLBOARD, 12M /: Bus 128.Dev 000: Apple T2 Controller, /: Bus 128.Dev 000: FaceTime HD Camera (Built-in), /: Bus 128.Dev 000: Ambient Light Sensor, /: Bus 128.Dev 000: Headset, |__ Bus 128.Dev 008: Apple Internal Keyboard / Trackpad, 480M /: Bus 128.Dev 000: Touch Bar Display, /: Bus 128.Dev 000: Touch Bar Backlight,
Here we go. My old keyboard’s signalling rate is at 1.5M, clearly incompatible with USB3.0 (it really is old) – therefore connected to the USB2.0 hub. Also – what the hell is Composite Device? Sometimes the name of the plugged device will be uninformative so it’s wise to get yourself used to comparing the output of lsusb
before and after plugging something in is the way to go to identify the device you want to investigate.
All of the above connections are facilitated by the host controller. Every machine will have at least one to manage data on the USB buses on the hardware level.
USB <-> Host communications
Let’s dive into how USB devices actually communicate with the host machine.
Endpoints
A USB endpoint is a ‘virtual wire’, it’s both a data sink and source for the device. There are four types of USB endpoints. These types also determine the type of data transfer that occurs on them, so the below are simultaneously endpoint and transfer types:
-
- Interrupt
Used by mice (like the one we see above) and keyboards, low speed (turns out mice and keyboards don’t exactly send that much data that often). Fun fact: turns out the interrupt does not actually interrupt the host machine in any way. It must wait until it’s polled by the host to send any requests. - Control
Used by most USB devices; the device with that endpoint must be able to respond to standard USB device requests like GET ADDRESS, SET ADDRESS, GET DESCRIPTOR, SET DESCRIPTOR, GET STATUS, CLEAR FEATURE, SET FEATURE etc. GET_STATUS informs of the status of the endpoint. The latter get, set and clear and set boolean values for different features on a device. - Bulk
Used for good performance, no low-speed transfers are allowed; used extensively by USB mass storage (USBMS) devices. - Isochronous
Primarily used for time-critical applications such as streaming media; whenever guaranteed bandwidth is required.
- Interrupt
It’s important to note that each endpoint will have an assigned direction. The direction is always relative to the host, not the device: 1 for in to the host, 0 for out of the host. USB2.0 devices can have up to 32 endpoints (16 in, 16 out), while USB3.0 devices support more (up to 96 or 254 depending on the underlying architecture). It’s important to bear in mind that a single device might use more than one endpoint – more on this here.
You can clearly see what type of endpoint is used for communication in Wireshark. Select a packet and scroll down to the USB URB (URB stands for USB Request Block) section, then Endpoint to check the transfer type and direction used by this particular packet.
The control transfer is interesting as this is the one that will be used to send data describing the device, such as make, language, etc. which can be extremely helpful in your forensics investigation. This is conveyed through descriptors.
Descriptors
You will see quite a lot of requests sent by the host to the device including GET_DESCRIPTOR Request
and the corresponding replies from the device – GET_DESCRIPTORS Response
. We are mostly interested in the responses, as they are holding the information the device reports about itself back to the host. In Wireshark you can filter your capture to show only packets that have a bDescriptorType
field by simply putting usb.bDescriptorType
in the filter bar. That will show you both requests and responses. To filter for responses only, we will add && !usb.bmRequestType
to the filter bar. This is because only requests will have the bmRequestType
field, and adding the exclamation mark inverts the filter, leaving us with all the packets that do not have the bmRequestType
field.
A number of interesting information are carried by descriptors of a number of different types. They all share a common structure, when the first byte of the descriptor specifies the length of the descriptor, second byte specifies the type, and the remaining data form the actual descriptor. The descriptor types are the following:
-
- Device descriptors (0x01)
- Configuration descriptors (0x02)
- String descriptors (0x03)
- Interface descriptors (0x04)
- Endpoint descriptors (0x05)
We can observe this in action in any packet containing descriptors. Below is an example of a configuration descriptor; when the header of that section is selected in Wireshark, it will highlight the bytes in the raw data view below so you can investigate each and everyone for yourself. This is a configuration descriptor for a USB memory stick – in a forensic investigation situation we might not know that though, so learning details about the device such as it not being self-powered and not having the capacity of remote wake up (as a mouse or other HID would probably do).
The device descriptor offers more detailed information about the device, such as vendor and product identification information.
The string descriptor is an interesting one. It is optional and the kind of information you can expect to find in those depends on the device. Interestingly, it support multiple languages denoted by specific codes – eg. English (US) is 0x0409, German is 0x0407, which might be interesting for your forensic investigation.
The information in the string descriptor is in human-readable format, and can reveal for instance the name of the device, like shown below.


You can use Wireshark filters to narrow down your search for specific descriptors – just pop in usb.bDescriptorType == 0x01
to filter for device descriptors only, or substitute a hex value for a different type of descriptor to filter for those.
USB Device classes
The last characteristic of USB protocol we will look at are device classes. All USB devices will have a device class attribute. The full list is available here for reference. In some cases the device class will be unspecified (the value will be 0x00). We then need to look at the specific interface class to determine what the device is.
Having applied a filter to only show packets including an interface descriptor – usb.bDescriptorType == 0x04
– the interface descriptor can quickly be found, revealing more characteristics of the device – eg that is is a HID and a mouse…
… or a USB mass storage device.
Bonus: Practical tips on sniffing USB traffic
macOS: you must run sudo ifconfig XHC20
before you will be able to see the interface in Wireshark and sniff the traffic on it. Also, make sure you actually disconnect all devices from your USB ports prior to starting the Wireshark capture, as you want to be able to capture their full communication – hence you’ll plug them in once the capture is started.
Linux: you need to run sudo modprobe usbmon
and then run Wireshark as root by running sudo wireshark &
to be able to sniff traffic on usbmon interfaces.
If you’re on macOS, the interface you’re going to want to sniff on will will be XHC (Extended Host Controller). The number of the interface corresponds to the bus number – XHC20 is Bus 020, XHC0 is Bus 000, etc. Unfortunately, I was not able to get the data to display correctly when sniffing directly on my Mac, so I moved onto a Kali VM for the sniffing part.
🔮🔮🔮
Well, that was quite a lot to take in, so let’s wrap it up here for now. I hope the above shed some light on how USB communications look like and what they consist of. Next up, I will want to look into the actual forensics of USB devices, especially mass storage devices. so keep your eyes peeled for more content coming.
Thanks for tuning in!
4 thoughts on “USB 101”