# RfCat: scripting a subGHz dongle for hardware reverse engineering

> RfCat is a Python client and firmware package for talking to CC1111 based radio dongles, aimed at security researchers who need to probe hardware without writing their own stack. Its constraints are as real as its features: the client still documents Python 2.7, the dongle list is small, and the licence is a custom file rather than a standard one.

**atlas0fd00m/rfcat** — RfCat - swiss-army knife of ISM band radio

- Repository: https://github.com/atlas0fd00m/rfcat
- Stars: 632 · Forks: 132
- Language: C
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/atlas0fd00m-rfcat

## A CC1111 dongle becomes a scriptable radio

RfCat exists to shorten the path from "I have a radio device and no idea what it is talking" to "I have a tool that talks to it". The stated goals are to reduce the time for security researchers to create needed tools for analyzing unknown targets, to aid in reverse engineering of hardware, and, in the project's own words, to satisfy the author's appetite for radio work. The project describes itself as a swiss-army knife of ISM band radio.

The hardware side is narrow and deliberate. The supported dongles are the YARD Stick One, the cc1111emk sold as DONSDONGLES, the Chronos watch dongle, and the imme, which is listed as having limited support for both the IMME and IMMEDONGLE with the blunt note that the imme dongle is not really usable as of 1/31/2012. Everything else runs on the same CC1111 silicon, and the repository carries firmware source plus a `CC-Bootloader` directory, so the workflow is a stock dongle, custom firmware, and a host-side client.

The documentation also includes pinout diagrams for GoodFET, GreatFET, the EMK dongle and the YARD Stick One, mapping their debug pins to the CC1111 so you can attach a logic analyzer or a second radio. That is the reverse engineering part of the tool: not just transmit, but watch and correlate.

## The client still documents Python 2.7

This is the constraint that decides whether RfCat is usable in your environment, and it is stated without softening. The documentation says RfCat currently requires Python 2.7, and that the only suspected incompatibilities with Python 3.x are minimal, mostly `print("stuff")` versus `print "stuff"` and other str/bytes issues.

"Suspected" is doing real work in that sentence. The project has not published a compatibility matrix, so the claim is an expectation about syntax and string handling rather than a tested statement. For a security researcher on an air-gapped box with an old virtual machine, Python 2.7 is not a problem. For anyone who has to put this in a container image, a CI job or a reproducible environment, a Python 2 runtime is a dependency you will have to justify to your own organisation.

The rest of the runtime requirements are ordinary: python usb, libusb in either the 1.x or 0.1 line, pyreadline on Windows, and PySide2 for the Spectrum Analyzer GUI. The GUI dependency is deliberately not installed automatically because of support concerns for Raspberry Pi platforms, and the suggested route is `pip`. Note the packaging says something slightly different, since the extras group named `specan` in `setup.py` pins a newer Qt binding:

```python
        extras_require={
            'specan': [
                'PySide6==6.9.1',
            ]
        },
```

So the dependency story is split across two documents that disagree on the Qt binding version. Plan for that before you pin an environment.

## Granting dongle access through udev

RfCat talks to the dongle over USB, which means the host must be allowed to open the device. The documentation offers two routes: run the Python client in root mode, where the note says sudo works well, or configure udev so a normal user gets full access to the dongle.

The udev route ships a rule file in the `etc` directory of the repository. Copy it into place and reload the rules:

```sh
sudo cp etc/udev/rules.d/20-rfcat.rules /etc/udev/rules.d
sudo udevadm control --reload-rules
```

After reloading, the accounts you want to use must be members of the `dialout` group. That step is stated in prose rather than as a command, so whether you apply it with `usermod` or through your distribution's user management tool is up to you.

The tool is created, maintained and used primarily on Linux, and the build tools make and sdcc must be installed for creating new firmware and for some of the helper functions provided through make. On a fresh machine the order matters: install the rule before the client, otherwise the first run either fails on permissions or pushes you towards running as root, which is exactly the shortcut the udev rule exists to remove.

## Four entry points ship in the package, not one

RfCat is not a single command. `setup.py` installs a set of scripts and packages that tell you how the pieces divide up:

```python
scripts = ['rfcat',
           'rfcat_server',
           'rfcat_msfrelay',
           'CC-Bootloader/rfcat_bootloader',
           ]
```

The client itself is `rfcat`. `rfcat_server` and `rfcat_msfrelay` are separate entry points, and the repository also contains `rfcat_msfrelay` alongside dedicated documentation files such as `README.msfrelay`, `README.immesniff` and `README.nonroot`, which suggests the relay and sniffing modes are documented separately from the main client.

Underneath sits `rflib`, packaged with `vstruct` and `vstruct.defs`, so the wire format handling is a library you could use on its own. Versioning is unusually transparent: the version string is read from the `VERSION` file at build time, and the git revision is captured by running `revision.sh` and written into `rflib/rflib_version.py` as `RFLIB_VERSION`. The install requirements are read straight out of `requirements.txt`, whose contents are `pyusb`, `ipython`, `pyserial` and `numpy`. An interactive shell comes along with `ipython` in that list, which tells you the intended interaction style is a live Python session against the dongle rather than a scripted batch tool.

## Custom firmware starts from a copy of application.c

The extension path for the tool is firmware, and the project has a firm convention for it. New development efforts should copy the `application.c` file to a name of your own, `appWhateverMyToolIs.c`, and attempt to avoid making changes to other files in the repository if at all possible.

The reasoning given is practical rather than stylistic: future bug fixes in other libraries and headers will go much more smoothly if you have not forked those files. That advice is reinforced by the fact that the repository keeps the shared code in `rflib`, in `firmware`, and in the `CC-Bootloader` directory, so a one-off tool is a new file in the tree rather than a patched copy of everything.

Building those custom applications needs make and sdcc, which is the SDCC toolchain for the 8051 family that the CC1111 belongs to. The repository also has a `tests` directory, a `FAQ` file and a `TODO` file, and the packaging pulls its long description from `README.rst` rather than `README.md`, which is the file most readers will land on. For a tool aimed at hardware work, the split between the Markdown front door and the reStructuredText used for package metadata is a small but real sign of an older layout.

## The CC1111 gotchas are the real documentation

The most useful part of the documentation for anyone writing firmware is the gotchas list, and it is worth reading line by line because each item maps to a class of silent failure.

First, the memory model includes both "RAM" and "XDATA" concepts, and standard RAM variables and XDATA variables use different assembly instructions to access them, so a function written for XDATA can behave oddly when applied to a standard RAM variable and the other way round. Second, variables should be defined in a single `.c` file with externs declared in a `.h` file that other modules include, which is standard C practice but caused the author real difficulty. Third, RAM memory is not cheap, use it sparingly.

The two radio-level rules are the ones that bite hardest. You need to set the radio into IDLE mode before reconfiguring it. And you need to set the radio into TX mode before writing to the RFD register, because it is a one-byte FIFO. Writing several bytes to a one-byte FIFO without the right mode is the kind of mistake that produces a radio that appears to work while dropping bytes, and the documentation does not spell out the failure symptom any further than the rule itself.

## A custom licence and an uneven release history

Two things are visible in the repository metadata that you should not skip. The first is licensing: the licence is recorded as Other, a custom licence that GitHub cannot classify, and the repository carries a plain `LICENSE` file rather than a named standard licence. There is a `LICENSE` file and a separate `LICENSE.CC-Bootloader`, which means the bootloader code is under separate terms from the rest of the tree, and anyone shipping the bootloader should read both files rather than assuming one grant covers everything. This is not a legal opinion, it is a pointer to the two files that carry the terms.

The second is release cadence. The recent tags are v3.0.0 named `foobarrier` on 2026-08-11, v2.0.1 named `heatwave` on 2023-08-29, and v1.9.7 named `boringbungalow` on 2022-12-20. The last push was on 2026-08-16, so the current work sits right after the v3.0.0 tag, and the tag names are playful rather than semantic, which means you cannot infer compatibility from the version number alone. The gap between the 2022 and 2023 tags and the 2026 one suggests the project moved through a long quiet stretch before this release, and the default branch is `master` rather than `main`.

The packaging also hints at unfinished business. The classifier list in `setup.py` opens with the comment about how mature the project is and the value for Alpha, but the file is cut off there. The distribution describes itself as the swiss army knife of subGHz, and the honest summary is that the hardware support and the packaging maturity are at different stages.

## Conclusion

RfCat fits a researcher who already owns a YARD Stick One or a cc1111emk and wants a repeatable script for reading and writing subGHz frames, or who wants to fork `application.c` into a one-off tool for a device under test. It does not fit a team that needs a supported, packaged radio stack, because the client targets Python 2.7 and the package metadata carries no mature classification. Before committing, check the `LICENSE` file yourself because the repository licence is a custom one, confirm the dongle you own is on the supported list, and decide whether the Python 2.7 dependency rules the tool out for you.

## FAQ

### What hardware does RfCat support?

The supported dongles are the YARD Stick One, the cc1111emk known as DONSDONGLES, the Chronos watch dongle, and the imme, which has limited support and is noted as not really usable since 1/31/2012. The dongle must be flashed with the necessary application firmware.

### Does RfCat work with Python 3?

The documentation says RfCat currently requires Python 2.7, and that the only suspected incompatibilities with Python 3.x are minimal, mostly print statement differences and other str/bytes issues. No tested compatibility statement is published.

### How do I let a non-root user use the RfCat dongle?

Copy the shipped udev rule into place and reload the rules, using the two commands in the installation section. Then make sure the accounts that need access are members of the `dialout` group.

### What extra tools do I need to build custom RfCat firmware?

Make and SDCC must be installed locally for creating new firmware and for some of the helper functions provided through make. New tools should copy the `application.c` file to a name of your own and avoid changing other files in the repository.

### What licence does RfCat use?

It is a custom licence that GitHub cannot classify, so the terms are in the `LICENSE` file at the root of the repository. The `CC-Bootloader` directory carries its own separate `LICENSE.CC-Bootloader` file, so the bootloader terms are not necessarily the same as the rest of the tree.

## Sources

- [atlas0fd00m/rfcat on GitHub](https://github.com/atlas0fd00m/rfcat)
- [Issues](https://github.com/atlas0fd00m/rfcat/issues)
- [README](https://github.com/atlas0fd00m/rfcat/blob/master/README.md)
- [Releases](https://github.com/atlas0fd00m/rfcat/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/atlas0fd00m-rfcat
