Click here to Skip to main content
14,978,303 members
Articles / Programming Languages / XML
Article
Posted 23 Jul 2020

Stats

93K views
7.4K downloads
83 bookmarked

How to Communicate with its USB Devices using HID Protocol

Rate me:
Please Sign up or sign in to vote.
4.78/5 (25 votes)
23 Jul 2020CPOL2 min read
This article will help you to understand how to communicate with the USB devices using WinAPI in C#.
This article shows you how to use the USB/HID protocol under Windows to be able to send/receive USB packets from any USB devices connected to the PC. And without using DLL, just an application is needed.

(You need Visual Studio.)

Warning

This USB sniffer, because of its user mode method access to hardware, cannot read HID packets with RID at 0, it's due to Windows protection level to prevent keyloggers/spying software.

Image 1

(Do not add 0x, else the application will crash, I haven't added 0x prefix support)

Introduction

This article shows you how to use the USB/HID protocol under Windows to be able to send/receive USB packets from any USB devices connected to the PC.

And without using DLL, just an application is needed.

Background

This article was possible with this WDK sample:

Basically, it's just a rewrite of this sample, but in a simple form.

Using the Code

Declaration of variables, WinAPI functions and using: https://pastebin.com/MSRAYJ5b

All that you need is those functions:

C#
HidD_GetHidGuid
SetupDiGetClassDevs
SetupDiEnumDeviceInterfaces
SetupDiGetDeviceInterfaceDetail
SetupDiGetDeviceInterfaceDetail
CreateFile
HidD_GetPreparsedData
HidD_GetAttributes
HidP_GetCaps
WriteFile

Assembled, it will look like this, the core of your program:

C#
int FindNumberDevices()
{
    Guid                     hidGuid        = new Guid();
    SP_DEVICE_INTERFACE_DATA deviceInfoData = new SP_DEVICE_INTERFACE_DATA();
    int index = 0;

    HidD_GetHidGuid(ref hidGuid);

    //
    // Open a handle to the plug and play dev node.
    //
    SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
    hardwareDeviceInfo    = SetupDiGetClassDevs
    (ref hidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    deviceInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));

    index = 0;
    while (SetupDiEnumDeviceInterfaces
          (hardwareDeviceInfo, IntPtr.Zero, ref hidGuid, index, ref deviceInfoData))
    {
        index++;
    }

    return (index);
}

This function returns all the data structures for each USB device needed for ReadFile() and WriteFile().

C#
int FindKnownHidDevices(ref HID_DEVICE[] HidDevices)
{
    int                             iHIDD;
    int                             RequiredLength;

    Guid                            hidGuid            = new Guid();
    SP_DEVICE_INTERFACE_DATA        deviceInfoData     = new SP_DEVICE_INTERFACE_DATA();
    SP_DEVICE_INTERFACE_DETAIL_DATA functionClassDeviceData = 
                                             new SP_DEVICE_INTERFACE_DETAIL_DATA();

    HidD_GetHidGuid(ref hidGuid);

    //
    // Open a handle to the plug and play dev node.
    //
    SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
    hardwareDeviceInfo    = SetupDiGetClassDevs
    (ref hidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    deviceInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));

    iHIDD = 0;
    while (SetupDiEnumDeviceInterfaces
    (hardwareDeviceInfo, IntPtr.Zero, ref hidGuid, iHIDD, ref deviceInfoData))
    {
        RequiredLength = 0;

        //
        // allocate a function class device data structure to receive the
        // goods about this particular device.
        //
        SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, 
        ref deviceInfoData, IntPtr.Zero, 0, ref RequiredLength, IntPtr.Zero);

        if (IntPtr.Size == 8)
            functionClassDeviceData.cbSize = 8;
        else if (IntPtr.Size == 4)
            functionClassDeviceData.cbSize = 5;

        //
        // Retrieve the information from Plug and Play.
        //
        SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, ref deviceInfoData, 
        ref functionClassDeviceData, RequiredLength, ref RequiredLength, IntPtr.Zero);

        //
        // Open device with just generic query abilities to begin with
        //
        OpenHidDevice(functionClassDeviceData.DevicePath, ref HidDevices, iHIDD);

        iHIDD++;
    }

    return iHIDD;
}

This function depends on FindKnownHidDevices().

C#
void OpenHidDevice(string DevicePath, ref HID_DEVICE[] HidDevice, int iHIDD)
{
    /*++
    RoutineDescription:
    Given the HardwareDeviceInfo, representing a handle to the plug and
    play information, and deviceInfoData, representing a specific hid device,
    open that device and fill in all the relevant information in the given
    HID_DEVICE structure.
    --*/

    HidDevice[iHIDD].DevicePath = DevicePath;

    //
    //  The hid.dll APIs do not pass the overlapped structure into deviceiocontrol
    //  so to use them we must have a non overlapped device.  If the request is for
    //  an overlapped device, we will close the device below and get a handle to an
    //  overlapped device
    //
    CloseHandle(HidDevice[iHIDD].HidDevice);
    HidDevice[iHIDD].HidDevice  = CreateFile(HidDevice[iHIDD].DevicePath, 
    GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | 
    FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, IntPtr.Zero);
    HidDevice[iHIDD].Caps       = new HIDP_CAPS();
    HidDevice[iHIDD].Attributes = new HIDD_ATTRIBUTES();

    //
    //  If the device was not opened as overlapped, then fill in the rest of the
    //  HidDevice structure.  However, if opened as overlapped, this handle cannot
    //  be used in the calls to the HidD_ exported functions since each of these
    //  functions does synchronous I/O.
    //
    HidD_FreePreparsedData(ref HidDevice[iHIDD].Ppd);
    HidDevice[iHIDD].Ppd = IntPtr.Zero;
    HidD_GetPreparsedData(HidDevice[iHIDD].HidDevice, ref HidDevice[iHIDD].Ppd);
    HidD_GetAttributes(HidDevice[iHIDD].HidDevice, ref HidDevice[iHIDD].Attributes);
    HidP_GetCaps(HidDevice[iHIDD].Ppd, ref HidDevice[iHIDD].Caps);

    //MessageBox.Show(GetLastError().ToString());

    //
    // At this point, the client has a choice. It may choose to look at the
    // Usage and Page of the top level collection found in the HIDP_CAPS
    // structure. In this way --------*it could just use the usages it knows about.
    // If either HidP_GetUsages or HidP_GetUsageValue return an error, then
    // that particular usage does not exist in the report.
    // This is most likely the preferred method as the application can only
    // use usages of which it already knows.
    // In this case, the app need not even call GetButtonCaps or GetValueCaps.
    //
    // In this example, however, we will call FillDeviceInfo to look for all
    //    of the usages in the device.
    //
    //FillDeviceInfo(ref HidDevice);
}

Then come the two important functions that will make you able to read or write USB packets between a USB device and a PC.

C#
void    Read(HID_DEVICE HidDevice)  // Read the Report[] and send it to the USB device targeted by HidDevice.HidDevice
{
    Byte[] Report = new Byte[HidDevice.Caps.InputReportByteLength];
    UInt32 tmp    = 0;

    try
    {
        Report[0] = REPORT_ID;
    } catch{ }

    ReadFile(HidDevice.HidDevice, Report, HidDevice.Caps.InputReportByteLength, ref tmp, IntPtr.Zero);

    try
    {
        textBox_Read.Clear();
    } catch { }

    var Index = 0;
    while (Index < HidDevice.Caps.InputReportByteLength)
    {
        try
        {
            textBox_Read.Text += Report[Index++].ToString("X2");
            textBox_Read.Text += " - ";
        } catch { }
    }
}

void    Write(HID_DEVICE HidDevice) // Read what the USB device has sent to the PC and store the result into Report[]
{
    Byte[] Report = new Byte[HidDevice.Caps.OutputReportByteLength];
    UInt32 tmp    = 0;

    try
    {
        Report[0] = REPORT_ID;

        for (UInt32 Index = 0; Index < HidDevice.Caps.OutputReportByteLength; Index++)
        {
            Report[Index + 1] = WriteData[Index];
        }

    } catch { }

    WriteFile(HidDevice.HidDevice, Report, HidDevice.Caps.OutputReportByteLength, ref tmp, IntPtr.Zero);
}

To be able to do that, you'd need to set following data before:

  • DEVICE_VID
  • DEVICE_PID
  • USAGE_PAGE
  • USAGE
  • REPORT_ID

But be careful, you'd need to set the correct values for all those parameters, if one is false, you will not be able to send HID packets.

To read HID packets, you only need:

  • DEVICE_VID
  • DEVICE_PID

Also, you cannot read if the device cannot send data and you cannot write if the device cannot read data (defined by the HID Report Descriptor).

A device is defined by its DEVICE_VID/DEVICE_PID but shrunk into several functions defined by its USAGE_PAGE, USAGE and REPORT_ID.

As an example, the first function of a mouse is to send coordinate data, so you can read data from PC and the second function is to receive mouse button customization data, so you can send data from PC.

And to set those variables, you need to read the HID Descriptor of the USB devices that you target, it can be retrieved with a USB sniffer as https://github.com/djpnewton/busdog or http://www.usblyzer.com/usb-analysis-features.htm

The HID Descriptor usually start with 0x05, 0x01.

And to learn to read HID Descriptor, use this tool: http://www.usb.org/developers/hidpage#HID Descriptor Tool

Because this code is just a rewrite of an old C code from the 90s, it works on all Windows Versions.

History

  • 2nd October, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

wqaxs36
France France
No Biography provided

Comments and Discussions

 
Questionwhat is Report ID (RID) Pin
AliAhmadSabir6-Jun-21 3:54
MemberAliAhmadSabir6-Jun-21 3:54 
AnswerRe: what is Report ID (RID) Pin
wqaxs366-Jun-21 5:35
Memberwqaxs366-Jun-21 5:35 
QuestionIt does not work with HID barcode reader Pin
Member 146350394-Oct-20 18:33
MemberMember 146350394-Oct-20 18:33 
AnswerRe: It does not work with HID barcode reader Pin
wqaxs365-Oct-20 5:25
Memberwqaxs365-Oct-20 5:25 
QuestionGreat tutorial about USB HID Pin
Potter6830-Aug-20 7:24
MemberPotter6830-Aug-20 7:24 
QuestionControlling serial and keyboard emulated devices Pin
uzayim27-Jul-20 12:24
Memberuzayim27-Jul-20 12:24 
AnswerRe: Controlling serial and keyboard emulated devices Pin
wqaxs3627-Jul-20 22:48
Memberwqaxs3627-Jul-20 22:48 
PraiseThank you for this +5 Pin
honey the codewitch23-Jul-20 21:12
mvahoney the codewitch23-Jul-20 21:12 
QuestionI think that I'm running into "cannot read HID packets with RID at 0"... can you please elaborate...? Pin
crn1147-Jun-20 7:34
Membercrn1147-Jun-20 7:34 
AnswerRe: I think that I'm running into "cannot read HID packets with RID at 0"... can you please elaborate...? Pin
wqaxs3614-Jun-20 22:34
Memberwqaxs3614-Jun-20 22:34 
AnswerRe: I think that I'm running into "cannot read HID packets with RID at 0"... can you please elaborate...? Pin
wqaxs3614-Jun-20 22:52
Memberwqaxs3614-Jun-20 22:52 
QuestionI have a VID and a PID, I enter it and hit submit to read... only see zeros in window... what am I doing wrong? Pin
crn1146-Jun-20 3:00
Membercrn1146-Jun-20 3:00 
AnswerRe: I have a VID and a PID, I enter it and hit submit to read... only see zeros in window... what am I doing wrong? Pin
wqaxs3614-Jun-20 22:29
Memberwqaxs3614-Jun-20 22:29 
QuestionComminucate with KVM Pin
vjaggi12-May-20 5:33
Membervjaggi12-May-20 5:33 
AnswerRe: Comminucate with KVM Pin
wqaxs3614-May-20 17:36
Memberwqaxs3614-May-20 17:36 
QuestionLooks Good! Pin
glennPattonContracting210-May-20 23:10
professionalglennPattonContracting210-May-20 23:10 
Questionneeds to run as Administrator Pin
Member 144722843-Mar-20 7:34
MemberMember 144722843-Mar-20 7:34 
AnswerRe: needs to run as Administrator Pin
wqaxs369-May-20 8:08
Memberwqaxs369-May-20 8:08 
QuestionKeyboard communication Pin
Paul Effect[the real one]12-Nov-19 2:53
MemberPaul Effect[the real one]12-Nov-19 2:53 
AnswerRe: Keyboard communication Pin
wqaxs3624-Nov-19 13:05
Memberwqaxs3624-Nov-19 13:05 
GeneralRe: Keyboard communication Pin
Paul Effect[the real one]25-Nov-19 11:24
MemberPaul Effect[the real one]25-Nov-19 11:24 
GeneralRe: Keyboard communication Pin
wqaxs3625-Nov-19 11:35
Memberwqaxs3625-Nov-19 11:35 
GeneralRe: Keyboard communication Pin
wqaxs3625-Nov-19 11:53
Memberwqaxs3625-Nov-19 11:53 
GeneralRe: Keyboard communication Pin
Paul Effect[the real one]10-Dec-19 12:22
MemberPaul Effect[the real one]10-Dec-19 12:22 
QuestionAnd without using DLL, just an application is needed. Pin
Member 137679925-Oct-19 1:27
MemberMember 137679925-Oct-19 1:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.