2

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?

user75619
  • 31
  • 1
  • 5
  • 1
    This might be useful https://teledynelecroy.com/protocolanalyzer/usb/mercury-t2 – IronMan Jul 31 '19 at 17:47
  • Not sure it's a good idea to use keyboard or mouse as a replacement. The real device can be completely different. Anyway, if what you want to achieve is quite simple you could write your own driver so no ones gets in the middle and steal your packets. USB drivers can be written as user-mode COM objects with Windows UMDF: https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/implement-driver-entry-for-a-usb-driver--umdf- in theory it could be written in C# also (not tested). – Simon Mourier Aug 04 '19 at 07:42
  • @SimonMourier I use keyboard and mouse only to teach myself how to intercept traffic and listen on USB port. When or rather if I'm ready, I'll then be able to arrange some time with the device to write code that works specifically for it. Thanks for the link, I'll give it a look, but writing own driver is out of the question. I didn't want to complicate my description with details, but in fact the device is rather complex, it just so happens that one particular mode of operation that we are interested in is very simple, traffic-wise. – user75619 Aug 04 '19 at 10:18

2 Answers2

2

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):

screenshot

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.

i336_
  • 1,813
  • 1
  • 20
  • 41
0

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!"

Community
  • 1
  • 1
computercarguy
  • 2,173
  • 1
  • 13
  • 27
  • As you note, QEMU is indeed a virtualization engine, for various CPU architectures. The most straightforward usage is to emulate the same architecture as the host system, where hardware virtualization acceleration will typically be available. You've listed setup instructions for AArch64; the OP is almost certainly running x86_64 hardware. Correct number of bits, yes (and *technically* both CPUs are a mix of CISC and RISC underneath) but QEMU cannot enable hardware acceleration for such a configuration. – i336_ Aug 10 '19 at 13:19
  • The time dependency issue you mention is indeed a real concern; emulation is *very* slow. QEMU boots Debian x86_64 with KVM in maybe 30 seconds, while Debian built for Cortex-A9 takes more like three and a half minutes. – i336_ Aug 10 '19 at 13:21
  • I also suspect the author's general reference to `.net` probably does not include .NET Core, which would be the only sane method to use if going the QEMU route. – i336_ Aug 10 '19 at 13:25
  • With all the above being said, the MITM idea is very close to (IMO) the right solution: just tracing/sniffing the USB data, which Windows allows you to do. USBPcap for Windows is one system that makes this a less-unwieldy process. – i336_ Aug 10 '19 at 13:32
  • @i336_, since this isn't emulating a whole OS, like Android or Linux, and just emulating some ports, it should run considerably faster. It looks like the whole project can emulate a whole OS, but this also looks like the OP can just pull the parts for emulating the USB and just emulate what they need and nothing else. – computercarguy Aug 12 '19 at 16:00
  • QEMU is a whole-system emulator, and IIUC its USB emulation is scoped to operate specifically within the usage scenario of emulation. QEMU emulates/virtualizes a CPU architecture (commonly the same as the host's) and some peripherals, said virtualization/emulation runs an OS, and you can tinker with data going into and out of the peripherals outside of whatever (sandboxed) environment you're running. – i336_ Aug 13 '19 at 05:44
  • I say the above simply because I've never seen bits of QEMU taken out of context and used on their own, or any documentation that (to me) would suggest this is possible. I would be very interested in being wrong here though, such compartmentalization would enable some useful use cases! – i336_ Aug 13 '19 at 05:47
  • (Oh - one other thing I neglected to clarify. The commandline you gave was a bit unusual - with both AArch64- and x86-specific options, and the `-hda fat:fatdir/` and references to OVMF give the appearance you copy-pasted generic instructions to test EFI shells (?). In any case, the particular command you pasted launches an entire virtual machine, very much yes.) – i336_ Aug 13 '19 at 05:50
  • @i336_, the first link I gave seemed to show USB emulation could be done without the full virtual machine. If I'm reading it wrong, I guess I'm wrong. All the commands in my answer were from the resources I linked, not my own. – computercarguy Aug 13 '19 at 15:48
  • (Learns to read, and actually clicks the links) Hmm. [This answer](https://stackoverflow.com/a/25456774) to the "how to emulate USB devices?" question, along with qemu-doc.html, do describe the emulation of USB devices somewhat hand-wavily, and depend pretty heavily on contextual interpretation. To add that clarification: QEMU works by running a VM and then dancing outside of the sandbox where the OS inside the VM can't reach (notwithstanding [celebrated exceptions that aren't supposed to happen](https://www.zdnet.com/article/microsoft-edge-used-to-escape-vmware-workstation-at-pwn2own-2017)). – i336_ Aug 14 '19 at 12:56