RF reconnaissance, sub-GHz protocol decoding and BLE enumeration, built for authorised lab testing and vendor approved research.
Transmit is disabled by default and stays disabled until you explicitly enable it twice: once on disk, once per command.
| Area | Capability |
|---|---|
| Recon | Wideband spectrum sweep, noise floor estimation, peak detection |
| Capture | IQ recording in cs8, cu8, cs16 and cf32 |
| Analysis | Burst detection, OOK and FSK pulse extraction, symbol time recovery |
| Sub-GHz | EV1527 and HS1527, PT2262 tri-state, KeeLoq framing, Manchester, PPM |
| Key fobs | Car/gate remote analysis: modulation, protocol, fixed vs rolling code |
| Aviation | ADS-B on 1090 MHz: callsign, altitude, velocity, CPR position |
| Cellular | Passive GSM/LTE downlink survey by ARFCN/EARFCN, GSM FCCH presence |
| BLE | Advertisement scanning and GATT enumeration through the host adapter |
| Transmit | Raw IQ replay and synthesised PWM codes, both authorisation gated |
Capabilities are modules in a registry rather than hardcoded subcommands, so adding a protocol means adding one file:
rfsploit modules # list everything, grouped by category
rfsploit search keeloq # find a module by protocol, band or category
rfsploit info decode/adsb # options, types, defaults and references
rfsploit run decode/adsb duration=30Options are OPTION=VALUE and are validated before any hardware is touched, so
a typo or an out of range value fails immediately rather than halfway through a
capture:
$ rfsploit run decode/adsb durartion=10
[!] decode/adsb: unknown option(s) durartion. Known options: input, duration,
backend, lna_gain, vga_gain, amp, format, device
A module name can be abbreviated when it is unambiguous, so run subghz and
run decode/subghz are the same thing.
Aircraft broadcast identity, position and velocity in the clear on 1090 MHz. Reception is entirely passive.
rfsploit run decode/adsb duration=30ICAO Callsign Alt ft Speed kt Track Latitude Longitude Msgs
40621D 38000 52.25720 3.91937 12
4840D6 KLM1023 3
Frames failing the 24 bit CRC are discarded rather than reported: a corrupted position is worse than no position. Positions require a matched pair of even and odd CPR frames, so an aircraft can appear with no position until both arrive. This needs a 1090 MHz antenna with a view of the sky; a sub-GHz whip will hear almost nothing.
Passive reconnaissance of what cellular networks broadcast: which downlink channels are on air and what channel number they use. It transmits nothing, and it does not collect any subscriber data. This is the opposite of an IMSI catcher (see "Not included, by design").
rfsploit run recon/cellular band=b3 # LTE band 3, list active carriers
rfsploit run recon/cellular band=gsm900 confirm=true # GSM, confirm the top hits3 LTE carrier(s):
EARFCN MHz BW MHz dB SNR
1612 1846.1784 14.76 -19.0 43.9
1311 1816.1193 18.10 -37.1 25.8
GSM channels are 200 kHz wide, so each active ARFCN is one peak. LTE carriers are 1.4 to 20 MHz wide, so they are detected as blocks of elevated power and the carrier centre is mapped to an EARFCN; the reported bandwidth is the width of that block. The band-plan arithmetic (3GPP TS 45.005 for GSM, TS 36.101 for LTE) is pinned in tests to published anchor frequencies.
confirm=true on a GSM band, or the standalone decode/gsm-fcch module, captures
a channel and looks for the frequency-correction burst (FCCH), a pure tone at
67708 Hz above the carrier that only a live base station transmits. Finding it
proves a real GSM downlink and measures the receiver's frequency error:
rfsploit run decode/gsm-fcch arfcn=79 band=gsm900FCCH tone found: live GSM downlink
tone offset : 68320 Hz (ideal 67708)
frequency err : 612 Hz
tone duration : 584 us
The FCCH recurs only a few times per 235 ms multiframe and only on a network's BCCH carrier, so a single short capture can miss it; a miss does not prove the channel is dead. This is presence and timing only. It does not decode the network identity (MCC, MNC, LAC, Cell ID); that needs full GSM demodulation with a tool such as gr-gsm.
Identify a car or gate remote and classify its security. It reports the carrier, the modulation (OOK vs FSK), the decoded protocol, and whether the code is fixed or rolling. This is analysis of a remote you own.
# capture the same button twice, then compare
rfsploit run capture/iq freq=433.92M output=press1.cs8 duration=4
rfsploit run capture/iq freq=433.92M output=press2.cs8 duration=4
rfsploit run decode/keyfob input=press1.cs8 input2=press2.cs8 modulation : OOK (AM depth 0.98, FM spread 19960 Hz, high confidence)
decoded :
ev1527 24 bits hex=B385AD serial_hex=B385A button_4bit=13
security : FIXED
two presses of the button decoded to the same payload, so the code does not
change; a fixed code is replayable
The fixed-vs-rolling test is passive and needs no key: press the same button
twice and diff the frames. Identical every time means a fixed code (EV1527,
PT2262), which is replayable. Different every time means a rolling code (KeeLoq
and similar), which is not. Band presets are us (315 MHz, North America and
Japan), eu (433.92 MHz, most of the world) and eu868 (868.3 MHz, Europe).
A fixed code, once confirmed on a remote you own, can be resent with the gated
tx/replay module. rfsploit does not implement attacks against vehicles:
no RollJam or jam-and-replay, no rolling-code prediction or key recovery, no
jamming, no relay/PKE range extension, no cloning. See "Not included, by design".
| Backend | Receive | Transmit | Requires |
|---|---|---|---|
| hackrf | yes | yes | hackrf_transfer, hackrf_sweep |
| rtlsdr | yes | no | rtl_sdr, rtl_test |
| soapy | yes | yes | SoapySDR Python bindings |
| file | yes | writes to disk | nothing |
Every module runs a capability preflight before it touches hardware. Missing tooling, an absent device, an out of range frequency or sample rate, a receive only radio asked to transmit, or a radio already claimed by another process all fail immediately with the command that fixes them.
$ rfsploit capture -f 8G -s 2M -d 1 -w out.cs8
[!] capture cannot run on this host:
- frequency: 8000.0000 MHz is outside the hackrf range 1-6000 MHz
fix: retune within the device range, or use a different radio
Add -v to print every check, including the ones that passed.
rfsploit.py runs straight from a checkout. It puts src/ at the front of the
import path itself, so only numpy and scipy are needed:
git clone https://github.com/V33RU/rfsploit
cd rfsploit
python3 rfsploit.py # start the interactive console
python3 rfsploit.py devices # or run a single command
python3 rfsploit.py sweep --start 433M --stop 435MIt is a thin entry point that hands every argument to the same CLI as the
installed rfsploit command and as python3 -m rfsploit; the three are
interchangeable. With no arguments it starts the console; with arguments it runs
that one command and exits.
The console is a command shell with module context, in the style of msfconsole.
It reuses the CLI for execution, so run is exactly a rfsploit run under the
hood.
$ rfsploit
rfsploit > search adsb
decode/adsb Decode ADS-B aircraft position, callsign and velocity on 1090 MHz
rfsploit > use decode/adsb
rfsploit(decode/adsb) > show options
rfsploit(decode/adsb) > set duration 30
rfsploit(decode/adsb) > run
| Command | Effect |
|---|---|
search <term> |
find modules by name, band or protocol |
use <module> |
select a module (short names work: use adsb) |
show options |
list the module's options and current values |
set <opt> <value> |
set an option (validated immediately) |
unset <opt> |
clear an option back to its default |
run (or exploit) |
run the module with the options set |
info [module], back, help, exit |
as expected |
Required options that are still unset are shown in red, advanced tuning options
are hidden behind show advanced, and any shell command (sweep ...,
tx-enable) also works at the prompt. Tab completion covers commands, module
names and option names. Transmit modules refuse to run until tx-enable has
been run and authorized is set true.
git clone https://github.com/V33RU/rfsploit
cd rfsploit
pip install -e ".[ble,dev]"Host tools for a HackRF on Debian or Ubuntu:
sudo apt install hackrfCheck what the host can do:
rfsploit devicesSurvey a band and list the active carriers:
rfsploit sweep --start 433M --stop 435M --sweeps 8 --threshold 12Record two seconds of IQ:
rfsploit capture -f 433.92M -s 2M -d 2 -w remote.cs8Decode a captured remote:
rfsploit decode -i remote.cs8 -s 2M -f 433.92M --threshold 20Decode straight off the radio:
rfsploit decode -f 433.92M -s 2M -d 10 --threshold 20Inspect timing when nothing decodes:
rfsploit bursts -i remote.cs8 -s 2M --threshold 20 --extract-dir bursts/
rfsploit decode -i bursts/burst_000.cs8 -s 2M --show-pulsesScan for BLE devices and enumerate one you own:
rfsploit ble-scan -d 10
rfsploit ble-enum AA:BB:CC:DD:EE:FFMachine readable output for any command:
rfsploit sweep --start 433M --stop 435M -o sweep.json --jsonTransmit requires two independent steps, so no single command can key the radio:
rfsploit tx-enable # writes the authorisation file, once
rfsploit replay -i remote.cs8 -f 433.92M -s 2M --i-am-authorized
rfsploit transmit-code 101100111000010110101101 -f 433.92M --te 350 --i-am-authorized
rfsploit tx-disable # revoketx-status reports the current state. Transmitting outside the common ISM
bands is not blocked, but it is called out in the output.
Sending a synthesised code rather than replaying raw IQ is the stronger proof:
if the receiver acts on transmit-code, the recovered bits and symbol time were
correct, which a raw replay does not establish.
decode reports the protocol, bit count, hex, recovered symbol time and how
many times the frame repeated. A frame seen once carries a note saying so:
remotes normally repeat, and a single frame is more likely to be a decoding
artefact than a real code.
Burst detection estimates its noise floor from the capture itself, which fails on a fully occupied band. When the result looks unreliable the tool says so rather than presenting it as clean:
[!] The threshold did not cleanly separate signal from noise. Adjust --threshold
and re-run. A dynamic range under 3 dB means the capture is either dead air
or one unbroken carrier.
Truncated symbols at a capture boundary are dropped, never guessed. A decoder returns the bits it observed or nothing at all.
- KeeLoq frames are parsed, not decrypted. The hopping code needs the manufacturer key, and the rolling counter means a captured frame the receiver has already seen will not replay.
- BLE goes through the host Bluetooth adapter, not the SDR. A HackRF cannot follow a BLE connection: it hops 1600 times a second across 40 channels, which needs an Ubertooth One, nRF52840 or similar. Scanning and GATT enumeration work because the host adapter connects as an ordinary central.
ble-enumreports characteristics that advertise a write property over an unpaired connection. It does not write to them. Confirm the effect against your own device before treating that as a finding.- The RTL-SDR and SoapySDR backends are implemented but were not verified against hardware, since neither was available on the development host. The HackRF and BLE paths were verified end to end.
- ADS-B decoding is verified against published Mode S frames and against synthesised RF, but no live aircraft were received on the development host: a sweep of 1085 to 1095 MHz showed flat noise, meaning nothing was reaching the antenna. The decode path is proven, the receive path is not.
- The GSM FCCH detector was verified two ways: against synthesised tones (clean, frequency-shifted, low-SNR, noise and random-traffic cases) and against live base stations on the development host, where it confirmed three GSM downlinks and measured their frequency error. Over-air detection is per-capture and intermittent, so a miss is not proof of absence.
- No IMSI catcher. That is an active fake base station: it forces phones belonging to uninvolved people to attach to it. It targets third parties rather than equipment under test, and is illegal in most jurisdictions.
- No GPS spoofing or jamming, which breaks navigation for aircraft, shipping and road traffic well beyond any test bench.
- No vehicle attacks. The key fob module classifies fixed vs rolling codes but implements none of the techniques that steal cars: no RollJam or jam-and-replay, no rolling-code prediction or counter desync, no cryptographic key recovery wired to a transmitter, no relay / passive-entry range extension, no fixed-code brute force (De Bruijn / OpenSesame), and no fob cloning. Replaying a fixed code on a remote you own, through the gated tx/replay module, is the one transmit path, and it is for your own equipment.
Passive receive and decode of broadcast navigation, aviation, cellular and satellite signals is in scope. The cellular survey reads only what a network beacons to everyone (which channels are active, what channel number they use). Subscriber identifiers are not collected.
- Aviation and maritime: ADS-B is done, AIS on 162 MHz is next
- Satellite downlinks: NOAA APT and Meteor LRPT weather imagery, Inmarsat STD-C
- Cellular: survey is done; GSM system-information decode (MCC/MNC/LAC/CID) via a gr-gsm passthrough when that tooling is present
- Utility and telemetry: rtl_433 style sensors, TPMS, POCSAG and FLEX paging
pytestThe decoder tests need no radio. They synthesise a known signal, push it through the full receive path with added noise, and assert the exact bits come back. EV1527 recovery is verified down to 5 dB SNR.
For equipment you own or have written authorisation to test. Transmitting on licensed spectrum without authorisation is illegal in most jurisdictions. Keep transmit work in a shielded or attenuated setup.
MIT. See LICENSE.