How To Communicate With Its USB Devices Using HID Protocol
How To Communicate With Its USB Devices Using HID Protocol
display=Print
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.
(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.
Background
This article was possible with this WDK sample:
https://github.com/Microsoft/Windows-driver-samples/tree/master/hid/hclient
void Update()
{
while (true)
{
CheckHIDRead();
CheckHIDWrite();
Thread.Sleep(1);
}
}
This function returns the number of USB devices in order to scan them.
Int32 FindDeviceNumber()
{
var hidGuid = new Guid();
var deviceInfoData = new SP_DEVICE_INTERFACE_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));
var Index = 0;
while (SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, IntPtr.Zero,
ref hidGuid, Index, ref deviceInfoData))
{
Index++;
}
return (Index);
}
This function returns a data structure of each USB device needed
for Read() and Write().
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));
var iHIDD = 0;
while (SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, IntPtr.Zero,
ref hidGuid, iHIDD, ref deviceInfoData))
{
var 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 HID_Devices, iHIDD);
iHIDD++;
}
return iHIDD;
}
This function extend FindKnownHIDDevices().
//
// 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(HID_Device[iHIDD].Pointer);
HID_Device[iHIDD].Pointer = CreateFile(HID_Device[iHIDD].DevicePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ |
FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0,
IntPtr.Zero);
HID_Device[iHIDD].Caps = new HIDP_CAPS();
HID_Device[iHIDD].Attributes = new HIDD_ATTRIBUTES();
//
// If the device was not opened as overlapped, then fill in the rest of the
// HID_Device 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 HID_Device[iHIDD].Ppd);
HID_Device[iHIDD].Ppd = IntPtr.Zero;
//
// 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, wSendHID_PIDe will call FillDeviceInfo to look for
all
// of the usages in the device.
//
//FillDeviceInfo(ref HID_Device, iHIDD);
}
Then come the two important functions that will make you able to read or write USB
packets between a USB device and a PC.
//
// Read what the USB device has sent to the PC and store the result into
HID_Report[]
//
var HID_Report = new Byte[HID_Device.Caps.InputReportByteLength];
if (HID_Report.Length > 0)
{
var varA = 0U;
ReadFile(HID_Device.Pointer, HID_Report,
HID_Device.Caps.InputReportByteLength, ref varA, IntPtr.Zero);
Read_Output.Clear();
if (HID_Report.Length > 0)
{
HID_Report[0] = HIDWriteData.ReportID;
VendorID
ProductID
UsagePage
Usage
ReportID
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.
VendorID
ProductID
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 VendorID:ProductID but shrunk into several functions defined
by its UsagePage, Usage and ReportID.
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
Because this code is just a rewrite of an old C code from the 90s, it works on all Windows
Versions.