Porting to new devices
When will we consider new devices?
Rayhunter is already officially supported on several devices, and people are often interested in adding support for hardware they already own. Here's a non-exhaustive list of situations where we'd consider adding a new Tier 2 device:
- The device is significantly cheaper or more available in a specific region than any device we already support.
- The device supports 5G and costs less than 100 USD.
- You're willing to commit to supporting this device and handling bug reports.
- The device has support for all cellular bands and can work in any country.
We want to avoid a situation where the list of supported devices keeps growing but the number of recurring contributors and maintainers stays the same.
That said, you can always maintain a fork, or install Rayhunter manually without writing an installer. You can promote this work in the GitHub discussions area, where most new hardware investigations happen.
Please don't open issues about supporting a new device, use GitHub discussions instead. Most hardware investigations end up being abandoned, and the amount of issues we'd have to triage would be too much.
Prerequisites: root shell, and /dev/diag
Rayhunter is a Linux binary that reads traffic from the Qualcomm diagnostic interface, which requires root. If either of those isn't available, Rayhunter can't work. Everything else (displays, buttons) is secondary, and we can deal with it later.
In the devices we currently support /dev/diag is the interface for Qualcomm diagnostics and devices with this will be easiest to support. Newer Qualcomm modems expose the diagnostic interface over a USB gadget which is something we are working on support for, but do not currently have. Thus devices with the former diagnostic interface will be easier to port Rayhunter to.
You can check ahead of purchase whether /dev/diag is available by ensuring the device has a Qualcomm MDM* chip. Other Qualcomm LTE chips might work but we haven't encountered one yet. Typically you will be able to get this information from fcc.report, where either the chip is written down in some PDF or at least plainly visible in one of the teardown photos. Sometimes this information can also be found through teardown videos on YouTube. If you find that chip, there's a good chance (but no guarantee) /dev/diag is available.
Any vendor other than Qualcomm (Mediatek, Rockchip, ...) is unlikely to work. Quectel sometimes repackages Qualcomm chips into larger systems and might work. Huawei devices won't work, as they use their own chips.
Getting a root shell varies from device to device. Check the GitHub discussions for prior art, and look through the installer source in installer/src/ for inspiration. These approaches are common:
- Connecting with
adb shell. - If
adb shelldoesn't work, sending a special USB serial command might enable it. - Sometimes there's an unpatched CVE that can be used to launch
telnetdas root (search "device name CVE", the website opencve.io is particularly easy to use).
Once you have a root shell, check that /dev/diag exists.
Installing Rayhunter manually
The Rayhunter installation consists of just two components: the rayhunter-daemon binary, and the config file (config.toml).
Typically the layout on the filesystem will look like this:
/data/rayhunter/rayhunter-daemon
/data/rayhunter/config.toml
/data/rayhunter/qmdl/
Then, ./rayhunter-daemon config.toml can be started manually.
You can refer to Installing from source for how to obtain the rayhunter-daemon binary.
We're assuming that your device is ARMv7, i.e. 32-bit ARM (armv7-unknown-linux-musleabihf). If that's not the case, you can still build the daemon but you'll need to figure out the correct target triple on your own.
You can copy the daemon and config files to the device using netcat or adb push. They don't have to be in /data/rayhunter/, this is just convention. If you use a different path, be sure to update the qmdl_store_path setting in config.toml.
The device setting in config.toml must match one of the lowercase variant names from the Device enum (e.g. "orbic", "tplink"). This controls which display driver is used.
Setting debug_mode = true in config.toml runs the daemon without /dev/diag, so you can test the display and web UI without the hardware.
Autostart
To make Rayhunter start on boot, you'll need an init script. The existing installers use the template at dist/scripts/rayhunter_daemon, which has a #RAYHUNTER-PRESTART placeholder that gets replaced with device-specific setup commands (e.g. killing a vendor UI process, mounting an SD card). Look at how the existing installers handle this in their install() functions.
Display support
The device setting mentioned above also controls which display driver is loaded (see Device enum in lib/src/lib.rs). Unless your device is a variant of an existing device, you'll want to add a new variant to the Device enum and write a corresponding display module in daemon/src/display/.
You can play around with the existing values of the device setting to see which one ends up rendering on your device's display. Most likely your device has a display similar enough to an existing one, and the display module for that device (e.g. daemon/src/display/orbic.rs, daemon/src/display/tplink.rs) can be used as a starting point.
If your device has LEDs instead of a display, take a look at daemon/src/display/uz801.rs which controls LEDs via sysfs.
Button support
Rayhunter can use the power button to restart recordings via a double-tap gesture. The implementation is in daemon/src/key_input.rs. It currently has no structure for device-specific implementations, as all devices we support expose the same input event interface.
The key_input_mode setting in config.toml controls this feature (0 = disabled, 1 = double-tap power button to start/stop recordings).
Writing the installer, and contributing official support
At this point you'll want to have figured out how to automate the entire installation in principle, and how to make it as repeatable as possible. A proof-of-concept of this in bash or another language is also a welcome contribution (to be posted on GitHub discussions, not as a PR).
Writing the installer means adding a new variant to the Command enum in installer/src/lib.rs and implementing the install logic in a new module under installer/src/. Each subcommand maps to a device-specific entry point function (e.g. tplink::main_tplink, orbic_network::install).
The installer gets the daemon binary path from env!("FILE_RAYHUNTER_DAEMON"), which is set at build time. Config installation is handled by the shared install_config() helper in the connection module, which writes the config file with the correct device name.
You must also add a shell utility subcommand under installer util (the UtilSubCommand enum in installer/src/lib.rs), e.g. installer util tplink-shell, installer util orbic-shell. This is required -- without it, users and developers have no way to interactively debug the device. Depending on connectivity, this might be a telnet session, an ADB shell, or a serial connection. Other utilities (file transfer helpers, etc.) are optional but encouraged. See the existing UtilSubCommand variants for examples.
Please reuse existing utilities wherever possible. Take a look at installer/src/tplink.rs and installer/src/orbic_network.rs for inspiration. But the structures there are still evolving, and we'll happily guide you during code review.