HID Field Guide

Copyright 2015 by Assured Information Security, Inc. Created by Jean-Edouard Lejosne <lejosnej@ainfosec.com>. This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/.
Contributions by Ross Philipson <philipsonr@ainfosec.com>

The HID specs are quite long and difficult to apprehend. The main purpose of this page is to give some HID background to understand SuperHID<< TODO

The HID descriptor

Every HID device has a HID descriptor, that is read by the driver during its initialization. The HID descriptor defines the format of the data the device will send. It is mandatory, as there's no pre-defined data format in HID. Here is a HID descriptor (a stripped version of the one used in SuperHID) and its equivalent structure in C format. See next section for the explanations.

/* First a few constants */

#define REPORT_ID_MOUSE         0x02
#define REPORT_ID_MULTITOUCH    0x04
#define REPORT_ID_MT_MAX_COUNT  0x10

/* The HID descriptor */

char[] hid_descriptor = {
    0x05, 0x01,                   /* USAGE_PAGE (Generic Desktop)         */
    0x09, 0x02,                   /* USAGE (Mouse)                        */ 
    0xa1, 0x01,                   /* COLLECTION (Application)             */
    0x85, REPORT_ID_MOUSE,        /*   REPORT_ID (2)                      */    /* mouse_report.report_id */
    0x09, 0x01,                   /*   USAGE (Pointer)                    */
    0xa1, 0x00,                   /*   COLLECTION (Physical)              */
    0x05, 0x09,                   /*     USAGE_PAGE (Button)              */
    0x19, 0x01,                   /*     USAGE_MINIMUM (Button 1)         */
    0x29, 0x05,                   /*     USAGE_MAXIMUM (Button 5)         */
    0x15, 0x00,                   /*     LOGICAL_MINIMUM (0)              */
    0x25, 0x01,                   /*     LOGICAL_MAXIMUM (1)              */
    0x95, 0x05,                   /*     REPORT_COUNT (5)                 */
    0x75, 0x01,                   /*     REPORT_SIZE (1)                  */
    0x81, 0x02,                   /*     INPUT (Data,Var,Abs)             */    /* mouse_report.misc & 0x1F */
    0x95, 0x01,                   /*     REPORT_COUNT (1)                 */
    0x75, 0x03,                   /*     REPORT_SIZE (3)                  */
    0x81, 0x03,                   /*     INPUT (Cnst,Var,Abs)             */    /* mouse_report.misc & 0xE0 */
    0x05, 0x01,                   /*     USAGE_PAGE (Generic Desktop)     */
    0x09, 0x30,                   /*     USAGE (X)                        */
    0x09, 0x31,                   /*     USAGE (Y)                        */
    0x09, 0x38,                   /*     USAGE (Z)                        */
    0x15, 0x81,                   /*     LOGICAL_MINIMUM (-127)           */
    0x25, 0x7f,                   /*     LOGICAL_MAXIMUM (127)            */
    0x75, 0x08,                   /*     REPORT_SIZE (8)                  */
    0x95, 0x03,                   /*     REPORT_COUNT (3)                 */
    0x81, 0x06,                   /*     INPUT (Data,Var,Rel)             */    /* mouse_report.x mouse_report.y mouse_report.z */
    0xc0,                         /*   END_COLLECTION                     */
    0xc0,                         /* END_COLLECTION                       */
    0x05, 0x0D,                   /* USAGE PAGE (Digitizer),              */
    0x09, 0x04,                   /* USAGE (Touchscreen),                 */
    0xA1, 0x01,                   /* COLLECTION (Application),            */
    0x85, REPORT_ID_MULTITOUCH,   /*      Report ID (4),                  */    /* multitouch_report.report_id */
    0x09, 0x22,                   /*      Usage (Finger),                 */
    0xA1, 0x00,                   /*      Collection (Physical),          */
    0x09, 0x42,                   /*          Usage (Tip Switch),         */
    0x15, 0x00,                   /*          Logical Minimum (0),        */
    0x25, 0x01,                   /*          Logical Maximum (1),        */
    0x75, 0x01,                   /*          Report Size (1),            */
    0x95, 0x01,                   /*          Report Count (1),           */
    0x81, 0x02,                   /*          Input (Variable),           */    /* multitouch_report.misc & 0x01 */
    0x09, 0x32,                   /*          Usage (In Range),           */
    0x81, 0x02,                   /*          Input (Variable),           */    /* multitouch_report.misc & 0x02 */
    0x09, 0x37,                   /*          Usage (Data Valid),         */
    0x81, 0x02,                   /*          Input (Variable),           */    /* multitouch_report.misc & 0x04 */
    0x25, 0x1F,                   /*          Logical Maximum (31),       */
    0x75, 0x05,                   /*          Report Size (5),            */
    0x09, 0x51,                   /*          Usage (51h),                */
    0x81, 0x02,                   /*          Input (Variable),           */ 
    0x05, 0x01,                   /*                                      */

    0x65, 0x11,         

    0x75, 0x10,          
    0x46, 0x56, 0x0A,             /*          Physical Maximum (2646),    */
    0x26, 0xFF, 0x0F,             /*          Logical Maximum (4095),     */

    0x81, 0x02,                   /*          Input (Variable),           */
    0x46, 0xB2, 0x05,             /*          Physical Maximum (1458),    */
    0x26, 0xFF, 0x0F,          
    0x09, 0x31,                   /*          Usage (Y),                  */
    0x81, 0x02,                   /*          Input (Variable),           */
    0x05, 0x0D,                   /*          Usage Page (Digitizer),     */
    0x75, 0x08,                   /*          Report Size (8),            */
    0x85, REPORT_ID_MT_MAX_COUNT, /*          Report ID (10),             */
    0x09, 0x55,                   /*          Usage (55h),                */
    0x25, 0x10,                   /*          Logical Maximum (16),       */
    0xB1, 0x02,                   /*          Feature (Variable),         */
    0xC0,                         /*      End Collection,                 */
    0xC0                          /*  End Collection                      */
}

/* And the corresponding C structures */

struct mouse_report
{
    uint8_t  report_id;    /* Will always be REPORT_ID_MOUSE */
    uint8_t  misc;
    uint8_t  x;
    uint8_t  y;
    uint8_t  z;
} __attri

struct multitouch_report
{
    uint8_t  report_id;    /* Will always be REPORT_ID_MULTITOUCH */
    uint8_t  
    uint16_t x;
    uint16_t y;
} __attribute__ ((__packed__));

Explanations

The single HID descriptor above defines two applications (or sub-devices): a mouse (USAGE PAGE 1, USAGE 2) (explained in the following) and a touchscreen (USAGE PAGE D, USAGE 4). There's a lot of possible applications (potentially 255 * 255), they're all listed in the HID specs. The device represented by this descriptor has to be considered as literally a single device that has 2 integrated sub-devices, a mouse and a touchscreen, which in real life probably doesn't exist. A more common multi-sub-devices device would be a touchscreen + stylus, as you can find such a thing in most tablets. But that's the beauty of HID, the device doesn't have to make sense. One HID device can potentially implement all the possible applications.

About the HID descriptor itself:

  • It describes the format of the data the device will send.
  • HID descriptors are very compressed. The amount of information per byte is quite impressive!
  • At first, it is easier to read the comments rather than the hex values in the the descriptors. But for more information about the values, read HID Generic Item Format
  • The parsing of HID descriptors has similarities the parsing of assembly code, in the sense that we define "functions" (opcodes in asm), which take a pre-defined number of "arguments" (operands in asm).

Here's the meaning of a few "functions":

  • COLLECTION(Application): Starts the definition of a "sub-device".
  • USAGE PAGE: A "global variable" telling about which "set" of USAGEs we're in. In a bible, that would be the chapter, and the USAGE would be the verse.
  • USAGE: This "global variable" defines the current "object" we're describing, relative to the current USAGE PAGE. As for USAGE PAGE, it takes one 1-byte-long argument.
  • REPORT ID: Sets the "sub-device" ID for the current "COLLECTION(Application)" (or FEATURE). This field is not needed if the device implements only one "COLLECTION(Application)" (and no FEATURE).
  • REPORT SIZE: Sets the size in bits to be expected for the next INPUT (or FEATURE). As everything else, this will be the size for every INPUT (and FEATURE) until we change it.
  • REPORT COUNT: Sets how many values of "REPORT SIZE" bits are expected in the next INPUT (or FEATURE).
  • INPUT: Defines a value that will actually be sent by the device. The data will be in the format defined by REPORT SIZE, REPORT COUNT, LOGICAL MINIMUM/MAXIMUM,... The usual INPUT is "INPUT (Variable)" or "INPUT (Data,Var,Rel)". "Input (Constant)" or "INPUT (Cnst,Var,Abs)" just represents random bits that have no meaning, usually used for padding.
  • FEATURE: Has a meaning close to INPUT, except that a FEATURE has its own REPORT ID, and will be requested by the driver instead of 'randomly' sent by the device.

Note: Under the "touchscreen" USAGE PAGE (4), there's a few USAGEs (0x51 and 0x55 here) that are part of a HID extension.

0x51 is the finger ID, and 0x55 is the maximum number of fingers supported by the touchscreen (mandatory for the Windows driver...)

The C structures are just a representation of all the INPUT defined in the descriptor.

As they're defined here, the device packets can be directly casted to one or the other struct, depending on the REPORT ID.

The field "misc" in the multitouch report struct is just lazyness, it should be a bit-field, as it represents three 1-bit values, and one 5 bits value.

Example of packets

Here are a couple of (valid) example of raw HID data (in hexadecimal) that the device described in the HID descriptor could send.

02 05 12 00 00

This packet has a REPORT ID of 0x02, so it is a mouse move.

The next byte, 0x05, corresponds to the "misc" field. The 5 lower bits each represent one button, 1 for pressed, 0 for not. 0x05 = 000 0 0 1 0 1, so the user is currently clicking on both the left click and the wheel click.

Then it has an X value of 0x12 and Y and Z are 0x00.

So this packet is a 10 pixels mouse move on the X axis, with left click and wheel click pressed.

04 0F 12 34 56 78

This packet has a REPORT ID of 0x04, so it is a touchscreen event.

The next byte, 0x0F, corresponds to the "misc" field. So it needs to be cut in the 4 values it represents:

0x0F = 00001 1 1 1

From low bit to high:

  • 1 is the "TIP SWITCH", meaning that the finger represented here is touching the screen
  • 1 is "DATA VALID", meaning that the device determined that it was a "proper" event, not a user "accident".
  • 1 is "IN RANGE", meaning that the coordinates are in-range...
  • 00001 is the finger ID: 1
  • The next two bytes are the X coordinate of the finger: 0x1234. Now sometimes there's some endian crap to consider here, the value could actually be 0x3412.
  • The last two are the Y coordinate.

Tools

The DIGImend project includes a bunch of awesome HID tools, like

  • usbhid-dump That dumps USB HID descriptors
  • hidrd That converts raw HID descriptor to human-readable code

References

The HID Specs

The Usage Guide

The multitouch specs addition

Microsoft's rules about multitouch

Annex: The HID Generic Item Format

Now that you have seen a HID descriptor we have to have an idea what all those crazy numbers mean. Basically each of those sets of 1, 2, 3 etc bytes is a fixed size item. You can see the format of these items in chapters 5 and 6 of the HID specification. These fixed sized ones are called short items; there are long items of variable size but I have never seen one in the wild so we can skip them here. The first byte is the prefix and it can be followed by 0, 1, 2, or 4 bytes of data. The format of the prefix is as follows:

 7 6 5 4    3 2      1 0       <-- bits
|  tag   | type  |  size  |
  • size specifies the number of data bytes: 0 = 0 bytes 1 = 1 byte 2 = 2 bytes 3 = 4 bytes
  • the type is: 0 = Main 1 = Global 2 = Local 3 = Reserved
  • the tag futher specifies the function

With that in hand, you would then decode those bytes using the tables in chapter six. For example, lets take one that is intuitive from Jed's HID descriptor: "0x81, 0x02, /* INPUT (Data,Var,Abs) */". This is an input value, it is data, it is variable not constant, and it comes from an absolute source. So that is 10000001. First note the least significant bit is 1, indicating one byte of data which is exactly what we have. Now the rest indicates a lookup in table 6.2.2.4 Main Items, specifically: "Input 1000 00 nn". Note they just use "nn" for the size bits since they can vary. On the right of Input is what data byte 1 means broken down by bits. Note that many defaults are omitted but 00000010 is consistant with Data(0)Variable(1)Absolute(0)...rest(0). That was a simple one where the data byte specified flags about the item. Note that some of them like Logical/Physical Maximum are followed by data bytes that specify sizes not flags.

Each item that is present sets up some state. The HID parser loads all this state information in a stack like structure. Collections form scopes with the overall state. So within a given scope or at global scope, everything that has been specified applies and the last thing that was specified supercedes previous settings. That is why you will see something like say Report Size set only once even though there are multiple reports. If you don't need to change it, you don't need to keep setting it. The obvious exception is when you come out of a scope level or do need to change settings.

Usages

So that brings us to usages. Usages are one of the harder concepts to get one's noggin around. Usages defines everything in HID from something as simple as a Button to an X coordinate to a physical Finger to a Digitizing Multifunction Stylus to a piece of Analog Medical equipment to a Banana Moan Framus. The HID Usage Table guide cited below has base list but there are many more addenda and unofficial lists floating around. One of the things that is confusing is that it defines such seemingly unrelated things, things that contain other things that contain other things. For example a Touch Screen is a Usage and so is a Finger. You use a Finger on a Touch Screen but Fingers and Touch Screens do not really belong to any common category of things. Or Usage X - that is just horizontal movement by convention. You just have to deal with it.

The basic idea is that there is a usage page that collects related things together. Whether or not you think they are related is besides the point. It is what it is. The most expansive and generic one is called Generic Desktop. All the basic stuff is on that one including the most common devices like keyboards and mice and all the common controls like buttons, keys, cursors, X's, Y's, Z's etc. So first you select the page then the Usage on the page:

0x05, 0x01,                   /* USAGE_PAGE (Generic Desktop)         */
0x09, 0x02,                   /* USAGE (Mouse)                        */

Meaning Generic Desktop is Usage Page 1 and on that page, Mouse is Usage 2. And if I look at my handy HUT guide I see this is the case. Let's see, section 4...yup Usage 2 is a Mouse. Remember that the HID parser is like a stack so once you "load" Usage Page 1 it stays in effect. That is why a bunch of other Usages are used before the page is changed to USAGE PAGE (Digitizer) later.

There is always a bit of confusion about this (mainly the why of it) but HID is a specification that comes from the USB Community, this is the main page:

http://www.usb.org/developers/hidpage/