Reverse Engineering Serial Devices on Windows

It's an all too common phenomena - there's a cool piece of hardware which requires vendor software but that proprietary software only runs on Windows.

Currently, this is an issue with Radioddity's GA-510 10W radio. Radioddity updated the device's chipset in mid-2023 without changing the product ID and the new chipset does not have support in CHIRP. Radioddity only provides a Windows executable for programming the radio.

On Linux there are a few options for intercepting serial data that will not work directly on Windows like slsnif and socat in addition to the methods described below. None of the methods described here are dependent on Windows and should work on most platforms.

So how do we free the bits from a Windows binary blob?

Option 1: Wireshark + USBPcap

Wireshark is primarily a network protocol analyzer but with USBPcap installed it also supports tracking and analyzing USB streams.

Once USBPcap is installed it will show up as a capture device in Wireshark. Clicking the gear icon allows selecting a specific USB device to capture.

USBPcap Settings in Wireshark

Once the capture has started the USB frames are displayed in the GUI.

Sniffed Serial Data

It is completely possible to click through line by line and copy the relevant data out of each packet. Fortunately, there are many tools to work with pcap files that can simplify the process.  usbrply is a small utility to convert a USB capture into python or C code that replays the USB frames in the pcap file. But for reversing the GA-510 it is more useful to inspect the data than replay it.

tshark is a command line based analyzer that is installed alongside Wireshark. tshark allows display filters to be applied to previously captured data, so the USB capture data can be stripped from the Wireshark pcap.

> tshark.exe -r capture.pcapng -Y usb.capdata -T fields -e usb.src -e usb.capdata
host    50534541524348
2.1.2   06444d5231373032
host    50415353535441
2.1.2   500000
host    535953494e464f
2.1.2   06
host    5600000a0d
2.1.2   560d0a0a0d6900000000000000
Serial data extracted from the pcap with tshark

From the output we can see that the host sends \x50\x53\45\41\52\43\48 (PSEARCH) and the device replies back with \x06\x44\x4d\x52\x31\x37\x30\x32 ("\x06DMR1702").

Option 2: Logic Analyzer

If it is not possible to install Wireshark on the Windows machine an external logic analyzer (e.g. Saleae Logic, Digilent Analog Discovery) may work for sniffing the serial bus. If GND/TX/RX can be accessed on the device via pads or test points then the logic analyzer can be attached directly to the PCB. If the device has a non-USB connector at the end of the programming cable (e.g. a Baofeng programming cable uses a 2.5mm and 3.5mm jack) then an interception harness can be used to read the serial data directly off the wire. However, if the harness or device does not expose GND/TX/RX then a logic analyzer is not an option.

Saleae Logic Analyzer Sniff of the Windows Program

Sniffing the serial bus with a logic analyzer is a time consuming option but provides definitive data on what is being sent to the device.

Option 3: Windows VM + qemu + socat

Sometimes a Windows VM is available and a physical machine is not (or the physical machine is across the room). In that case, qemu can be used to virtualize Windows and socat can be used to MITM the serial device.

In the notional setup, the physical device is connected to a Linux host and enumerates as /dev/ttyUSB0. A Windows VM is running within qemu on the Linux host. A virtual Serial Device is attached to the VM (either via qemu XML or virt-manager) which enumerates as a COM device in Windows. On the Linux host the virtual Serial Device appears as a pts.

Notional Block Diagram

virt-manager will generate a new source path for the serial device on every boot:

Virt Manager Serial Settings

socat can be used to link the physical /dev/ttyUSB0 to the virtual pts device:

# socat -d -d -x 2> log.txt /dev/ttyUSB0,b57600,raw,echo=0 /dev/pts/9,raw,echo=0

Now, the pts in qemu is linking /dev/ttyUSB0 to COM1 in Windows. The data that is flowing is recorded in log.txt:

< 2024/01/24 19:16:44.000948516  length=1 from=14 to=14
< 2024/01/24 19:16:44.000948586  length=2 from=15 to=16
 53 45
< 2024/01/24 19:16:44.000948614  length=1 from=17 to=17
< 2024/01/24 19:16:44.000948645  length=1 from=18 to=18
< 2024/01/24 19:16:44.000948708  length=1 from=19 to=19
< 2024/01/24 19:16:44.000948805  length=1 from=20 to=20
> 2024/01/24 19:16:44.000954053  length=8 from=7 to=14
 06 44 4d 52 31 37 30 32

Once again, the hex bytes of "PSEARCH" are present in the logs, albeit in a painful format. A little python can convert this into a reasonable format:

def read_log(log):
    with open(log, 'r') as radio:
        transactions = []
        direction = ''
        transaction = ''
        for line in radio.readlines():
            if (line.startswith('<') and direction != '<') or (line.startswith('>') and direction != '>'):
                # We've changed direction
                #print(f"Direction change!\n {direction}\t{transaction}")
                direction = line[0]
                transaction = direction
            elif line.startswith(' '):
                transaction += line.strip('\n')
    return transactions
for t in read_log('log.txt'):
< 50 53 45 41 52 43 48
> 06 44 4d 52 31 37 30 32

Linux Bonus: If sniffing a serial device on Linux, socat can create the virtual device directly. In this case passing /dev/pts/5 to a program using a serial device instead of /dev/ttyUSB0 will allow the data to be streamed.

# socat -d -d -x /dev/ttyUSB0,b57600,raw,echo=0 pty,raw,echo=0
socat[22063] N opening character device "/dev/ttyUSB0" for reading and writing
socat[22063] N PTY is /dev/pts/5
socat[22063] N starting data transfer loop with FDs [5,5] and [6,6]
> 2024/02/20 20:06:48.000165060  length=5 from=0 to=4

Option 4: Reverse Engineering the Binary

Sniffing the serial bus, either physically or virtually, is by far the easiest path to understanding what is being sent to the device. The "nuclear option" is to reverse engineer the Windows executable to determine what it is writing to the device. Determining what the binary is doing is a tedious process but can be done; below is the "PSEARCH" string in the binary using Ghidra.

PSEARCH in the Disassembly of the Windows Binary

Despite the challenges with reverse engineering, on occasion there are functions with hidden functionality that aren't exposed in the GUI or are behind a password. For example, the GA-510 program allows users to access a secret menu to set the font and boot logo if they enter BAOFENGH5:

Was this an interesting read? Say thanks and help keep this site ad & analytic free by using my Amazon Affilliate URL. I'll receive a small portion of any purchases made within 24 hours.