Writing a small utility tool that listens on USB ports, need advice

会有一股神秘感。 提交于 2019-12-12 20:16:39

问题


I have a piece of hardware that works in cycles. It comes with a proprietary software tool that lets user control it from PC via USB. User defines how long each cycle is. At the beginning of each cycle the software tool rapidly issues a series of commands to the hardware via USB and then goes into the idle mode, awaiting next cycle.

There's a second piece of hardware that needs to be synched with the first one. Basically, when the aforementioned series of commands is issued to hardware #1, hardware #2 should do its thing too.

Hardware #2 comes with API and SDK and whatnot. Hardware #1 does not - there's only this tool where you can define how long your cycle will be.

In view of all this, it's been decided that the easiest way to achieve this will be by listening on USB port that hardware #1 uses so that each time traffic on it is detected hardware #2 is issued its directives too. I've used a monitoring app on hardware #1's USB port and its traffic looks pretty simple: it's a rapid and short succession of packets at the very beginning of a cycle and then nothing until the next cycle.

The idea is to write a small Windows-based utility app that will listen on hardware #1's USB port and issue an API call to hardware #2 every time it detects a cycle. Ideally, it should be a .NET app, cause I'm familiar it with the most, as far as writing Windows-code goes.

The difficult part is writing the code for listening on hardware #1's USB port. Also, I don't have access to hardware #1 at all times, so I use plain USB keyboard and mouse as its substitutes. At this point the goal is to simply detect traffic on keyboard or mouse USB ports.

So far I have attempted two libraries I found on the web - LibUsbDotNet and Usb.Net. I haven't been able to achieve the goal with either of the two.

I can't get Usb.Net to even read the packets...

And LibUsbDotNet seems to hijack the traffic - so if I type on a keyboard in a notepad or Word, letters don't appear there, while my app successfully reads the packets. Writing intercepted packets back to the port doesn't help.

Here's the code that I use with LibUsbDotNet. It's basically library's example, slightly modified to work with my keyboard.

// 1118 = Vendor Id, 1872 = Product Id
UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(1118, 1872);

ErrorCode ec = ErrorCode.None;

try
{
    // Find and open the usb device.
    MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);

    // If the device is open and ready
    if (MyUsbDevice == null) throw new Exception("Device Not Found.");

    // If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
    // it exposes an IUsbDevice interface. If not (WinUSB) the 
    // 'wholeUsbDevice' variable will be null indicating this is 
    // an interface of a device; it does not require or support 
    // configuration and interface selection.
    IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
    if (!ReferenceEquals(wholeUsbDevice, null))
    {
        // This is a "whole" USB device. Before it can be used, 
        // the desired configuration and interface must be selected.

        // Select config #1
        wholeUsbDevice.SetConfiguration(1);

        // Claim interface #0.
        wholeUsbDevice.ClaimInterface(0);
    }

    // open read endpoint 1.
    UsbEndpointReader reader = MyUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);

    // keyboard communicates in packets of size 8
    byte[] readBuffer = new byte[8];
    while (ec == ErrorCode.None)
    {
        int bytesRead;

        // If the device hasn't sent data in the last 5 seconds,
        // a timeout error (ec = IoTimedOut) will occur. 
        ec = reader.Read(readBuffer, 5000, out bytesRead);

        if (bytesRead == 0) throw new Exception(string.Format("{0}:No more bytes!", ec));
        Console.WriteLine("{0} bytes read", bytesRead);

        // Write that output to the console.
        foreach (byte b in readBuffer) Console.Write("{0} ", b);
        Console.WriteLine();
    }

    Console.WriteLine("\r\nDone!\r\n");
}
catch (Exception ex)
{
    Console.WriteLine();
    Console.WriteLine((ec != ErrorCode.None? ec + ":" : String.Empty) + ex.Message);
}
finally
{
    if (MyUsbDevice != null)
    {
        if (MyUsbDevice.IsOpen)
        {
            // If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
            // it exposes an IUsbDevice interface. If not (WinUSB) the 
            // 'wholeUsbDevice' variable will be null indicating this is 
            // an interface of a device; it does not require or support 
            // configuration and interface selection.
            IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
            if (!ReferenceEquals(wholeUsbDevice, null))
            {
                // Release interface #0.
                wholeUsbDevice.ReleaseInterface(0);
            }

            MyUsbDevice.Close();
        }
        MyUsbDevice = null;

        // Free usb resources
        UsbDevice.Exit();

    }

    // Wait for user input..
    Console.ReadKey();
}

How do I modify this so that it doesn't intercept the packets but simply detects them while letting them pass through to their intended destination?

Any advice is welcome with either Usb.Net or LibUsbDotNet. Or, perhaps, a different library that better suits my needs?


回答1:


USB traffic sniffing is less common than actual end-to-end USB device control, so the quantity of libraries available to help with this sort of thing is a bit thin on the ground. You're probably going to have to do a bit of adapting to what options are available.

One straightforward-looking approach is https://desowin.org/usbpcap/: the commandline version can output in real time to stdout, as shown in the screenshot below (from the linked page):

The significant catch with this approach would be the requirement to implement the Pcap data format, which I strongly suspect you will end up having to do yourself as all the Pcap parsing implementations that do exist (including for .NET) are all for the (again) more-popular case scenario of using Pcap to capture network data.

However, before you freak out, I am suggesting this as a potentially-viable solution because the entire format documentation seems to fit onto two-and-a-tiny-bit A4 pages (I checked with print preview :D): https://desowin.org/usbpcap/captureformat.html - and furthermore, the official Pcap format reference at https://wiki.wireshark.org/Development/LibpcapFileFormat (linked to by the USBPcap site) is also distinctly not especially dense.

You could use USBPcap to output test data from a file, then just spend time iterating on parsing the file correctly.

In the same vein you could also get access to the device and save a few traffic dumps for later parsing at your own pace, which I suspect may be very practical.

I found a couple of (slightly-bitrotted :) ) C# links that may serve to help get oriented with writing your own Pcap parser: https://github.com/ryrychj/PcapngUtils and https://formats.kaitai.io/pcap/csharp.html.


While searching I also stumbled on a couple other leads.

The first was https://sourceforge.net/projects/usbsnoop/, which I found had moved over to https://github.com/SnoopWare/usbsnoop/. This is actually really interesting, as it implements its own kernel driver to do capture. I am kind of glad I found this - Windows before Server 2003 doesn't have USB sniffing support but this works as far back as Windows 2000 (and as recently as Windows 10, of course), which is fun to know even if just as trivia. (I once wanted to do some USB sniffing of my own between a device and some old software, and thought a Win2K VM would be the most lightweight way to do it until I learned about the limitation.) Maybe not so practical for your purposes; the frontend UI doesn't appear to support logging to files.

The other amusing thing that happened was finding https://www.codeproject.com/Questions/204396/USB-Sniffer-protocol - Post 3 generated the complaint "why are you answering this, this post is two years old", included a link to a domain that no longer resolves. The Web Archive has only one copy of the linked page over at http://web.archive.org/web/20140127164835/www.tellmeword.com/dh9ic/usb_sniffer_(source_code) - and the downloads (the Mirror ones) got archived too! Ha! I have no idea if they are useful or if the techniques they use are deprecated etc.

Both the above are in C though I think.




回答2:


Have you considered acting as a "man in the middle" (MitM) for the USB device?

From what you're saying, you can already consume the USB data, but that's it, the "signal" "dies" with your program. You might want to act as a virtual USB device for your device's software to connect to and pass along any data you get to that virtual USB device you set up. Saying this another way, your software acts as a bridge to pass all the data between the device and the device's driver/software, and you also use the data however you need.

The following answer asks how to do that. The accepted answer seems to not be actionable anymore, as the software is deprecated, but the Answer by Yaron Shani seems to still be updated as of 2 months ago.

How to emulate USB devices?

The full project is designed to be a virtual PC, but the link in the Answer goes directly to the USB emulation section.

https://github.com/qemu/qemu

https://github.com/qemu/qemu/tree/master/hw/usb

Relevant documentation for the USB emulation.

QEMU can emulate a PCI UHCI, OHCI, EHCI or XHCI USB controller. You can plug virtual USB devices or real host USB devices (only works with certain host operating systems). QEMU will automatically create and connect virtual USB hubs as necessary to connect multiple USB devices.

https://qemu.weilnetz.de/doc/qemu-doc.html#pcsys_005fusb

It seems like this is a command line utility, but I know there are ways to run command lines from C#. The below is an example of how to run the USB emulation from a command line.

Launching QEMU

Unlike the x86 default "pc" machine, QEMU has no default machine type for ARM. The "generic virtualized platform" type virt has no USB attached by default, so we need to manually add one. ich-usb-uhci1 supports high-speed, and so is needed for some devices to work under emulation. Using the "virtual FAT" support in QEMU is the easiest way to get a driver into UEFI without recompiling. The example below uses a subdirectory called fatdir which QEMU generates an emulated FAT filesystem from.

USB_HOST_BUS=8
USB_HOST_PORT=2
AARCH64_OPTS="-pflash QEMU_EFI.fd -cpu cortex-a57 -M virt -usb -device ich9-usb-uhci1"
X86_OPTS="-pflash OVMF.fd -usb"
COMMON_OPTS="-device usb-host,bus=usb-bus.0,hostbus=$USB_HOST_BUS,hostport=$USB_HOST_PORT -nographic -net none -hda fat:fatdir/"

https://station.eciton.net/qemu-usb-host-device-pass-through.html

And here's a SO Question that explains how to use command line commands with C#.

string strCmdText;
strCmdText= "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
System.Diagnostics.Process.Start("CMD.exe",strCmdText);

Run Command Prompt Commands

There could be a downside to this, or several in fact. If there's a time dependency (as in it needs millisecond or less response times) or there's encryption on the communication, you might be out of luck. The time delay might not matter with a keyboard, but if it's critical to the device or how the software works, you could be out of luck or see odd errors that are difficult to track down. Also, some software is designed to prevent virtual devices from being a MitM so data can't be stolen from it. You may also have issues with trying to get the device software to discover your emulated USB device instead of the real device.

This probably isn't what you'd hoped for in an answer, but since you don't have any other suggestions, I don't know what to tell you besides "Good luck!"



来源:https://stackoverflow.com/questions/57295661/writing-a-small-utility-tool-that-listens-on-usb-ports-need-advice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!