目录
警告
此USB嗅探器由于其用户模式方法访问硬件,无法读取RID为0的HID数据包,这是由于Windows保护级别可防止键盘记录器/间谍软件。
(不要添加0x,否则应用程序会崩溃,因为我没有添加0x前缀支持)
背景
此WDK示例可以写这篇文章:
基本上,它只是对这个示例的重写,但形式很简单。
使用代码
主要代码:
void CheckHIDRead()
{
HIDReadInfo.Device = new HID_DEVICE[DeviceDiscovery.FindDeviceNumber()];
DeviceDiscovery.FindKnownHIDDevices(ref HIDReadInfo.Device);
for (var Index = 0; Index < HIDReadInfo.Device.Length; Index++)
{
if (HIDReadInfo.VendorID != 0)
{
var Count = 0;
if (HIDReadInfo.Device[Index].Attributes.VendorID == HIDReadInfo.VendorID)
{
Count++;
}
if (HIDReadInfo.Device[Index].Attributes.ProductID == HIDReadInfo.ProductID)
{
Count++;
}
if (Count == 2)
{
HIDReadInfo.iDevice = Index;
HIDReadInfo.Active = true;
return;
}
}
}
}
void CheckHIDWrite()
{
HIDWriteInfo.Device = new HID_DEVICE[DeviceDiscovery.FindDeviceNumber()];
DeviceDiscovery.FindKnownHIDDevices(ref HIDWriteInfo.Device);
for (var Index = 0; Index < HIDWriteInfo.Device.Length; Index++)
{
if (HIDWriteInfo.VendorID != 0)
{
var Count = 0;
if (HIDWriteInfo.Device[Index].Attributes.VendorID == HIDWriteInfo.VendorID)
{
Count++;
}
if (HIDWriteInfo.Device[Index].Attributes.ProductID == HIDWriteInfo.ProductID)
{
Count++;
}
if (HIDWriteInfo.Device[Index].Caps.UsagePage == HIDWriteInfo.UsagePage)
{
Count++;
}
if (HIDWriteInfo.Device[Index].Caps.Usage == HIDWriteInfo.Usage)
{
Count++;
}
if (Count == 4)
{
HIDWriteInfo.iDevice = Index;
HIDWriteInfo.Active = true;
return;
}
}
}
}
HIDReadUpdate()和HIDWriteUpdate()用于检查我们是否按了“读取”或“发送”按钮,以及输入的数据(VID-PID-Usa...)是否与连接的USB设备相对应。
此函数返回USB设备的数量,以便对其进行扫描。
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);
}
此函数返回HIDRead()和HIDWrite()所需的每个USB设备的数据结构。
static public void FindKnownHIDDevices(ref HID_DEVICE[] HID_Devices)
{
var hidGuid = new Guid();
var deviceInfoData = new SP_DEVICE_INTERFACE_DATA();
var functionClassDeviceData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
Hid.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));
for (var iHIDD = 0; iHIDD < HID_Devices.Length; iHIDD++)
{
SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, IntPtr.Zero, ref hidGuid, iHIDD, ref deviceInfoData);
//
// Allocate a function class device data structure to receive the
// goods about this particular device.
//
var RequiredLength = 0;
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);
}
}
此函数扩展FindKnownHIDDevices()。
static void OpenHIDDevice(String DevicePath, ref HID_DEVICE[] HID_Device, Int32 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 relivant information in the given
// HID_DEVICE structure.
//
HID_Device[iHIDD].DevicePath = DevicePath;
//
// The hid.dll api's 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].Handle);
HID_Device[iHIDD].Handle = CreateFile(HID_Device[iHIDD].DevicePath,
FileAccess.ReadWrite, FileShare.ReadWrite,
0, FileMode.Open, FileOptions.None,
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.
//
Hid.HidD_FreePreparsedData(ref HID_Device[iHIDD].Ppd);
HID_Device[iHIDD].Ppd = IntPtr.Zero;
Hid.HidD_GetPreparsedData(HID_Device[iHIDD].Handle, ref HID_Device[iHIDD].Ppd);
Hid.HidD_GetAttributes (HID_Device[iHIDD].Handle, ref HID_Device[iHIDD].Attributes);
Hid.HidP_GetCaps (HID_Device[iHIDD].Ppd , ref HID_Device[iHIDD].Caps);
var Buffer = Marshal.AllocHGlobal(126);
{
Hid.HidD_GetManufacturerString(HID_Device[iHIDD].Handle, Buffer, 126);
HID_Device[iHIDD].Manufacturer = Marshal.PtrToStringAuto(Buffer);
Hid.HidD_GetProductString(HID_Device[iHIDD].Handle, Buffer, 126);
HID_Device[iHIDD].Product = Marshal.PtrToStringAuto(Buffer);
Hid.HidD_GetSerialNumberString(HID_Device[iHIDD].Handle, Buffer, 126);
HID_Device[iHIDD].SerialNumber = Marshal.PtrToStructure<Int32>(Buffer);
}
Marshal.FreeHGlobal(Buffer);
}
然后是两个重要功能,使您能够在USB设备和PC之间读取或写入HID数据包。
static async public void BeginAsyncRead(object? state)
{
//
// Read what the USB device has sent to the PC and store the result inside HID_Report[]
//
if (HIDReadInfo.Active == true)
{
var Device = HIDReadInfo.Device[HIDReadInfo.iDevice];
var ReportBuffer = new Byte[Device.Caps.InputReportByteLength];
if (ReportBuffer.Length > 0)
{
await Task.Run(() =>
{
var NumberOfBytesRead = 0U;
Kernel32.ReadFile(Device.Handle, ReportBuffer, Device.Caps.InputReportByteLength, ref NumberOfBytesRead, IntPtr.Zero);
});
HIDReadInfo.ReportData = new List<Byte>(ReportBuffer);
}
}
}
static public void BeginSyncSend(object? state)
{
//
// Sent to the USB device what is stored inside WriteData[]
//
if (HIDWriteInfo.Done is false && HIDWriteInfo.Active is true)
{
var Device = HIDWriteInfo.Device[HIDWriteInfo.iDevice];
var ReportBuffer = new Byte[Device.Caps.OutputReportByteLength];
if (ReportBuffer.Length > 0)
{
//
// Add ReportID to the first byte of HID_ReportContent
//
ReportBuffer[0] = HIDWriteInfo.ReportID;
//
// Copy ReportData into HID_ReportContent starting from index 1
//
Array.Copy(HIDWriteInfo.ReportData, 0, ReportBuffer, 1, ReportBuffer.Length - 1);
var varA = 0U;
Kernel32.WriteFile(Device.Handle, ReportBuffer, Device.Caps.OutputReportByteLength, ref varA, IntPtr.Zero);
}
HIDWriteInfo.Done = true;
}
}
为此,您需要在之前设置以下数据:
- VendorID
- ProductID
- UsagePage
- Usage
- ReportID
但请注意,您需要为所有这些参数设置正确的值,如果是false,您将无法发送HID数据包。
要读取HID数据包,您只需要:
- VendorID
- ProductID
此外,如果USB设备无法发送数据,则无法读取,如果USB设备无法读取数据(在其HID报告描述符中定义),则无法写入。
设备由其定义VendorID:ProductID,但缩小为由UsagePage、Usage和ReportID定义的多个函数。
例如,鼠标的第一个功能是发送坐标数据,因此您可以从PC读取数据,第二个功能是接收鼠标按钮自定义数据,因此您可以从PC发送数据。
要设置这些变量,您需要读取目标USB设备的HID描述符,可以使用USB嗅探器将其检索为 GitHub - djpnewton/busdog: busdog is a filter driver for MS Windows (XP and above) to sniff USB traffic.或http://www.usblyzer.com/usb-analysis-features.htm
HID描述符通常以0x05开头,0x01。
要学习阅读HID描述符,请使用此工具:Human Interface Devices (HID) Specifications and Tools | USB-IF
因为此代码只是对90年代旧C代码的重写,所以它适用于所有Windows版本。
https://www.codeproject.com/Articles/1244702/Communication-with-USB-Devices-using-HID-Protocol