Great Scott Gadgets

open source tools for innovative people


Free Stuff - June 2024

The belated June 2024 recipient for the Great Scott Gadgets Free Stuff Program is Evan Metzinger. Evan is the president of the Cybersecurity club at Mt. San Antonio college in Walnut, California in the US. We will be sending Evan a HackRF One so he and his club can get some hands on experience with signals processing and participate in wireless capture the flag competitions.


Free Stuff - May 2024

The belated May 2024 Free Stuff recipient for the Great Scott Gadgets Free Stuff Program is Anik Mahanta from India! Anik is a student who is part of the CyRaksha Cybersecurity Club of Kolkata. CyRaksha is a free-to-join club that hosts their meetups both in-person and online. They will be using the HackRF One we are sending them to create CTF challenges, host informal meetups about RF technologies, and to create open access materials on how to use HackRF One safely while respecting Indian law.


Support Our Work at Great Scott Gadgets

Like every open source company, Great Scott Gadgets thrives with support from you, our community. The most direct way to support us is to buy our hardware, but for folks who already have our hardware, are more interested in our software, or just want to see us grow, we have a few other options. For example, we appreciate contributions to our GitHub repositories, documentation edits, and hearing your use cases and feedback for our products and projects. If you want to know other ways you can get involved, check out our “Support our Work” page.


Free Stuff - April 2024

The belated April 2024 Free Stuff recipient for the Great Scott Gadgets Free Stuff Program is Necati Sari! Necati is a radio amateur who is looking to build a single board computer (SBC) with HackRF One. Necati is developing this SBC to help support his local amateur radio community, TRAC Kadikoy in Turkey, in bringing in new members and to make software-defined radio more accessible to folks that are just starting in this discipline.

This application for the Free Stuff program stood out to the team here at Great Scott Gadgets because it focuses on community support and it proposes a project that we would like to see become reality. Free Stuff applications that describe specific projects in detail really stand out. We look forward to getting updates from Necati soon!


Sniffing PS5 Controller Packets with Cynthion

In our latest training video Martin uses Cynthion to sniff PS5 controller packets. The video guides you through how to set up Cynthion, how to use Packetry, and what PS5 controller packets look like. If you are just getting started with Cynthion or learning about USB, we suggest checking out this video.


Free Stuff - March 2024

The belated March 2024 Free Stuff recipient for the Great Scott Gadgets Free Stuff Program is Benjamin Pieres! Benjamin is a newly licensed amateur radio operator who is excited to contribute to the amateur radio community and share his passion for radio communication through workshops and innovative experiments.

One of the workshops Benjamin envisions hosting is focused on introducing basic radio communication principles to beginners. Participants will learn about radio wave propagation, antenna design, and the fundamentals of operating amateur radio equipment. Additionally, Benjamin plans to organize workshops on emergency communication preparedness, where attendees will learn how to establish communication networks during disasters or emergencies. The workshops will be held at Radio Club Bariloche (LU1VZ), located in the city of Bariloche, in the heart of the Argentinean Andes mountains. Benjamin says Bariloche is renowned for its vibrant amateur radio community and that it is home to one of the largest concentrations of ham radio repeaters in Argentina.

Benjamin will also be using the HackRF One we are sending him to explore the potential of low-power, long-distance communication using digital modes such as FT8. By experimenting with different antenna configurations and propagation techniques, Benjamin aims to demonstrate how amateur radio operators can establish reliable communication links over significant distances using minimal power. The experiment will involve setting up portable radio stations in remote locations and attempting to make contacts with stations located hundreds or thousands of miles away. He will document the experiment thoroughly, including equipment setup, operating procedures, and results analysis. The findings will be shared through articles, videos, and presentations to inspire other radio enthusiasts to engage in similar experiments.

This application for the Free Stuff program stood out to the team here at Great Scott Gadgets due to its breadth, description of topics covered in proposed workshops, focus on community support and involvement, and clear description of what the HackRF One we are sending him will be used for. We are excited to see what Benjamin achieves!


Reverse-engineering a VNA ECal Interface With Cynthion

Recently I’ve been working on a little reverse-engineering project, hoping to make some of my electronics test equipment more convenient to use.

Often when doing reverse-engineering, a general strategy that I follow is to make (informed) guesses about how something might work and then I go looking for ways to prove that right or wrong. In this project, Cynthion was really useful for that process as I could use it to emulate part of the target system, so that I could quickly and easily test out theories about the protocol.

This write-up goes over some of the progress I’ve made so far and hopefully it will provide some helpful techniques you can use in your own projects!

VNA with a Cynthion plugged in

Background

In my work and hobby projects, I’m often using a Vector Network Analyzer (VNA) to measure RF components. Ideally, on each power-up and whenever the measurement parameters are changed, the VNA should be calibrated by connecting and measuring a set of four standards in turn on each of the two measurement ports. This can get a little tedious if you need to re-calibrate often, so an alternative option is to use an Electronic Calibration module (ECal) which only requires one connection per port and then has internal switches to select between the different standards automatically. ECal modules are available for my VNA (an Agilent E8803A), but they’re rare and expensive, so I’d like to figure out how the VNA communicates with them so that I can implement it myself in an open-source ECal.

Reverse-engineering options

Of course, the easiest way to do this would be to connect an ECal module and capture the USB traffic (with Packetry!), but an actual ECal is too elusive.

The next option is to disassemble and analyze the software running on the VNA, which I spent a bit of time doing, but it was slow going as I’m not too familiar with the APIs on Windows and how it uses them for USB. However, there are some quick things to learn by looking at the software. It’s split into many DLLs, so it’s easy to see the imports and exports of each and get an idea of the functionality we might expect from the USB ECal:

$ winedump -j export ecalusb.dll 
Contents of ecalusb.dll: 33280 bytes

[...]

  Entry Pt  Ordn  Name
  00001F50     1 ReadModule
  00002160     2 SetState
  00001F90     3 ReadModule1K
  000016D0     4 Reset
  00001FD0     5 ReadModuleData
  00002010     6 WriteModuleData
  00002210     7 EraseSector

Done dumping ecalusb.dll

So, it should have ways to read and write data on the module, which makes sense as it needs to store S-parameter data describing the characteristics of the different standards to be used for calibration. It also has SetState, which probably sets the switch positions to select different standards on each port.

Device Emulation

I decided to go down the route of emulating the ECal device, then seeing what the VNA tried to request of it and iterate from there. Using Cynthion with the Facedancer library you can easily emulate a USB device by writing a Python script, and quickly make changes to try out different things.

I started with the template.py example from the Facedancer project. Usually, a USB host will identify a particular device by looking at the vendor ID & product ID and/or the manufacturer/product/serial strings. From searching on forums and other test equipment groups, I found the expected values for the target device and I modified the template with these:

#!/usr/bin/env python3

from facedancer         import main
from facedancer         import *

@use_inner_classes_automatically
class ECalDevice(USBDevice):

    vendor_id                : int  = 0x0957
    product_id               : int  = 0x0001

    manufacturer_string      : str  = "Agilent Technologies"
    product_string           : str  = "USB ECal Module"
    serial_number_string     : str  = "S/N 12346"
    device_speed             : DeviceSpeed = DeviceSpeed.FULL

    class ECalConfiguration(USBConfiguration):

        class ECalInterface(USBInterface):

            class ECalInEndpoint(USBEndpoint):
                number               : int                    = 1
                direction            : USBDirection           = USBDirection.IN
                transfer_type        : USBTransferType        = USBTransferType.BULK
                max_packet_size      : int = 64
                
                def handle_data_requested(self):
                    self.send(b"Hello!")

            class ECalOutEndpoint(USBEndpoint):
                number               : int                    = 1
                direction            : USBDirection           = USBDirection.OUT

                def handle_data_received(self, data):
                    print(f"Received data: {data}")

main(ECalDevice)

Running this script and then clicking “Detect Connected ECals” on the VNA gives the following output:

$ python ecal-emulate.py --suggest
INFO    | __init__       | Starting emulation, press 'Control-C' to disconnect and exit.
INFO    | moondancer     | Using the Moondancer backend.
INFO    | moondancer     | Connected FULL speed device '__main__.ECalDevice' to target host.
INFO    | device         | Host issued a bus reset; resetting our connection.
INFO    | moondancer     | Target host configuration complete.
WARNING | device         | Stalling unhandled OUT VENDOR request 0x04 to DEVICE [value=0x0000, index=0x0000, length=0].

This shows that the script got a vendor-specific request from the VNA, but doesn’t yet have the code to handle it so it returns a STALL response (which is the terminology for how USB devices respond to unhandled requests). Since I ran it with the --suggest argument, stopping the script gives me a suggestion for code that I can add to handle the request!

^CINFO    | moondancer     | Disconnecting from target host.

Automatic Suggestions
These suggestions are based on simple observed behavior;
not all of these suggestions may be useful / desirable.


Request handler code:

  @vendor_request_handler(number=4, direction=USBDirection.OUT)
  @to_device
  def handle_control_request_4(self, request):
      # Most recent request data: bytearray(b'').
      # Replace me with your handler.
      request.stall()

I add the suggested handler to my script, but change the response from stall to ack and also print out the request:

--- a/ecal-emulate.py
+++ b/ecal-emulate.py
@@ -34,4 +34,11 @@ class ECalDevice(USBDevice):
                def handle_data_received(self, data):
                    print(f"Received data: {data}")

+    @vendor_request_handler(number=4, direction=USBDirection.OUT)
+    @to_device
+    def handle_control_request_4(self, request):
+        print(request)
+        request.ack()
+
+
main(ECalDevice)

I ran the script and got a new message saying that there was also a vendor request number 2 to be handled, so I went through the same process to handle that too. After doing that, I got a lot more output - now it sent many #2 vendor requests with values counting down from 0x400 (1024) by 6 each time:

OUT VENDOR request 0x04 to DEVICE [value=0x0000, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x0400, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x03fa, index=0x0000, length=0]
...
OUT VENDOR request 0x02 to DEVICE [value=0x0010, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x000a, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x0004, index=0x0000, length=0]

This looked very promising, as I was expecting it to read out 1 kB of memory from the hints in some of the DLL exports mentioned earlier. However, I was a bit confused at this point because, while it seemed to be addressing the memory, I couldn’t see any way that the device could actually return the data. Due to the way USB control transfers work, there isn’t a way for an OUT request to return any data - it can only ACK or NAK.

I wanted to see if there might be something else happening on the bus that I might be missing. Fortunately, I have a few Cynthions about so I hooked up a second one in-line to do a packet capture and see if I could learn more:

Screenshot of Packetry doing a packet capture. After each OUT vendor request, there’s a BULK IN transfer of “Hello!”

Doh! Of course, the template example I was working from was also setting up a BULK IN endpoint to return “Hello!”. After each address vendor request, the VNA would request data on the bulk endpoint and receive those 6 bytes, then adjust the address accordingly and repeat. If I had realised, I could have just added a print to the handler to see that happening rather than setting up the packet capture.

Now that I had a pretty good idea of how the data was being read out I could go ahead and implement it properly, but I needed some appropriate data to return. Fortunately I was able to find some memory dumps that another user had shared online for the 8506x series ECal modules. They had shared them as ASCII hex dumps, so I converted them to binaries with xxd:

$ head HP85062-60006.txt 
=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2021.09.29 21:47:04 =~=~=~=~=~=~=~=~=~=~=~=
dump 0 040000

000000: 48 50 38 35 30 36 30 43 20 45 43 41 4C 00 E8 D1  | HP85060C ECAL... | 
000010: 31 83 C4 02 64 00 4E 6F 76 20 32 38 20 31 39 39  | 1...d.Nov 28 199 | 
000020: 34 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF  | 4............... | 
000030: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  | ................ | 
000040: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  | ................ | 
000050: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  | ................ | 
000060: FF FF FF FF 30 30 33 36 37 00 31 C0 50 E8 88 08  | ....00367.1.P... | 

$ xxd -r HP85062-60006.txt HP85062-60006.bin

$ hexdump -C HP85062-60006.bin | head
00000000  48 50 38 35 30 36 30 43  20 45 43 41 4c 00 e8 d1  |HP85060C ECAL...|
00000010  31 83 c4 02 64 00 4e 6f  76 20 32 38 20 31 39 39  |1...d.Nov 28 199|
00000020  34 00 ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |4...............|
00000030  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000060  ff ff ff ff 30 30 33 36  37 00 31 c0 50 e8 88 08  |....00367.1.P...|
00000070  33 35 46 33 35 46 20 4d  57 31 00 c4 04 56 e8 fb  |35F35F MW1...V..|
00000080  08 83 c4 02 38 20 41 75  67 20 32 30 30 31 20 00  |....8 Aug 2001 .|
00000090  41 47 49 4c 45 4e 54 2f  4d 54 41 00 b8 f8 6a eb  |AGILENT/MTA...j.|
000000a0  93 83 3e 94 06 00 74 1e  a1 82 06 83 c0 41 a2 98  |..>...t......A..|

Then I modified my Python script to load that file and return the data from the bulk endpoint, and tried again to detect the device from the VNA:

--- a/ecal-emulate.py
+++ b/ecal-emulate.py
@@ -14,6 +14,9 @@ class ECalDevice(USBDevice):
     serial_number_string     : str  = "S/N 12346"
     device_speed             : DeviceSpeed = DeviceSpeed.FULL
 
+    address = 0
+    data = open('EEPROM/HP85062-60006.bin', 'rb').read()
+
     class ECalConfiguration(USBConfiguration):
 
         class ECalInterface(USBInterface):
@@ -25,7 +28,10 @@ class ECalDevice(USBDevice):
                 max_packet_size      : int = 64
                 
                 def handle_data_requested(self):
-                    self.send(b"Hello!")
+                    # Respond with 32 bytes of EEPROM data
+                    dev = self.get_device()
+                    addr = dev.address
+                    self.send(dev.data[addr:addr+32])
 
             class ECalOutEndpoint(USBEndpoint):
                 number               : int                    = 1

Screenshot of the VNA software detecting an ECal, but showing garbled data

Success! …sort of. It detected something, which is great progress, but the output is a mess. After staring at it for a while, I realised my silly mistake - I’d forgotten to update the address when receiving vendor request 2:

--- a/ecal-emulate.py
+++ b/ecal-emulate.py
@@ -44,6 +44,7 @@ class ECalDevice(USBDevice):
     @to_device
     def handle_control_request_2(self, request):
         print(request)
+        self.address = request.value
         request.ack()

I added that, re-ran everything, and… nothing! It didn’t detect anything anymore. Something about that mistake actually made it work better.

Something that’s very common in file formats is to start with a header that includes some magic value and the parser will check that value before doing anything else. With the mistake in place, the script was returning the first 32 bytes of data to every request, so that was probably enough to pass the header check and show a detected device. However, that suggests that upon implementing the addressing, now the script wasn’t returning the header whenever the VNA was expecting it.

I had a look back at the pattern of #2 vendor requests:

OUT VENDOR request 0x02 to DEVICE [value=0x0400, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x03e0, index=0x0000, length=0]
...
OUT VENDOR request 0x02 to DEVICE [value=0x0040, index=0x0000, length=0]
OUT VENDOR request 0x02 to DEVICE [value=0x0020, index=0x0000, length=0]
^CINFO    | moondancer     | Disconnecting from target host.

Two things stood out to me about those:

  1. The VNA starts by requesting with value=0x400 and then counts down - that’s a bit odd, I’d expect it to start at address 0 and count up.
  2. The VNA never actually sends a request with value=0x0, so the script never sends the header at all with the addressing in place!

This got me thinking that maybe there’s some quirk in the addressing and it should actually be reversed (so that a request with value=0x400 goes to address 0x0). I made that change and re-ran the test:

--- a/ecal-emulate.py
+++ b/ecal-emulate.py
@@ -44,7 +44,7 @@ class ECalDevice(USBDevice):
     @to_device
     def handle_control_request_2(self, request):
         print(request)
-        self.address = request.value
+        self.address = 0x400 - request.value
         request.ack()

Screenshot of the VNA software detecting an ECal showing correct data

Bingo! The device is detected and all the information about it looks correct now.

While there are some other details still to figure out, I’ll wrap it up there as I think it’s demonstrated the process pretty well. Hopefully it provides some inspiration, please let us know if you use these techniques and tools in your own projects!

For anyone interested, the full code and any further research is available here: https://github.com/miek/ecal-reversing


Free Stuff - February 2024

The belated February 2024 Free Stuff recipient for the Great Scott Gadgets Free Stuff Program is Adam Drake! Adam, a teacher in Canada, sponsors three clubs at his high school - a competitive robotics club, a model railway club, and a D&D club. All of these clubs are fully funded from either internal school funds, the school PAC (Parental Advisory Council), or the NSHSS. This summer, Adam ran an RF Comms summer school where 18 students gained their amateur radio certification!

Following the success of the RF Comms summer school, Adam is now starting another after-school club: “RF Communications.” This club will teach students all about wireless communications, such as Bluetooth, Wi-Fi, cellular, and radio (HF, VHF, UHF, etc). Students will learn the theory of RF (radio frequency) communications, but the focus will be on practical uses of the technology. Students who do not yet have their radio licenses will have more chances to study and gain Canadian Amateur Radio licenses through this club.

We will be sending Adam a HackRF One to support these clubs and the students they impact. Thank you, Adam, for all you do in your community!


Cynthion is Here!

We’re pleased to announce that Cynthion is now available for purchase from our authorized resellers! This FPGA-based hardware platform from Great Scott Gadgets powered by LUNA gateware is your new go-to tool for discovering and exploring the world of USB at a fraction of the cost of existing High Speed USB analyzers. Whether you’re experienced with USB or you’re new and learning about it, Cynthion is a great multipurpose addition to your hardware experimentation toolbox! We’ve also developed custom open-source software tools that work with Cynthion:

  • Packetry is a fast and intuitive open-source software that allows you to capture and analyze traffic between a host and Low-, Full-, or High-Speed USB devices.

  • Moondancer is a Facedancer backend that enables you to reverse engineer USB devices and even create your own!

Cynthion comes in a beautiful and protective milled aluminum enclosure so that despite its small size (it fits in the palm of your hand) it has a solid feel and a nice weight. It is also currently available for sale without an enclosure at a lower cost. We’ve offered this option to make Cynthion as financially accessible as possible for hobbyists, students, and small teams.

You can learn more about Cynthion, including where to purchase it, by visiting our Cynthion webpage.


Free Stuff - January 2024

The January 2024 recipient for the Great Scott Gadgets Free Stuff Program is Marc, the author of PySDR.org. Marc is interested in learning more about HackRF One and potentially adding a chapter on HackRF One to the PySDR.org documentation. We look forward to working with Marc on this update to PySDR.org and helping even more folks with finding new ways to interact with their HackRF One.


subscribe to GSG feed