RF Protocol Reverse Engineering Explained: When the Airwaves Leak the Keys

🇺🇸EN🇸🇦AR

Imagine a building that trusts radio waves the same way most people trust a locked door. Sensors talk to controllers over the air, alarms stay quiet until something moves, and the documentation claims the protocol is “efficient and flexible.” That claim is usually true until the moment someone starts reading the actual packets.

This article walks through the practical process of reverse-engineering a proprietary RF security protocol: identifying structure, recovering the CRC, understanding addressing, and finally crafting packets that the system actually accepts. No magic, just methodical observation and a willingness to test the parameters the documentation never mentioned.

What Is Protocol Reverse Engineering, Anyway?

Protocol reverse engineering is the art of reconstructing the rules of a conversation when the only thing you have is the conversation itself. In the RF world that means capturing the radio signal, turning the analog waveform into bits, grouping those bits into fields, and then figuring out which fields control which behavior.

Unlike a web API that politely returns JSON error messages, a wireless security system simply discards packets that fail validation. Silence is the only feedback you get. That forces a different style of investigation: capture known-good traffic, compare packets that perform different actions, and gradually map every byte.

The protocol in this scenario was designed for low-power sensors operating around 433.92 MHz. Devices included perimeter lasers and alarm units that needed to stay coordinated without constant high-bandwidth chatter. The documentation listed command codes (suppress, alert, broadcast, turn-on, turn-off, move-left, move-right) and mentioned a CRC with a specific initial value, but left the packet layout and addressing rules intentionally vague.

Why Should You Care?

  • Physical security systems increasingly rely on proprietary RF links. A broken CRC or weak addressing model can let an attacker silence alarms or disable sensors without ever touching a wire.
  • The same techniques apply to garage-door openers, industrial remote controls, and many IoT devices that still treat “security by obscurity” as a feature.
  • Transmission parameters (modulation, samples-per-symbol, bits-per-symbol) are often as critical as the payload itself. A perfect packet sent with the wrong bit length is still garbage to the receiver.
  • Documentation is almost always incomplete. The real rules live in the silicon and in the traffic that silicon generates.

Worked Example: From Capture to Control

Capturing the conversation

The first step is obtaining real traffic. Signal files in complex format (interleaved I/Q samples) were imported into a tool that could demodulate FSK and display the recovered bits. Auto-detection usually produced usable parameters: FSK modulation, roughly 100 samples per symbol, 1 bit per symbol.

Once the bits were visible, the hex view revealed clear patterns across multiple packets:

  • A repeating preamble of aa bytes for one device class and bb bytes for another.
  • A fixed three-byte sequence that never changed.
  • A single byte that correlated with device type (alarm versus laser).
  • Variable address and command fields.
  • Two final bytes that changed with every different payload — the CRC.

Side-by-side comparison of packets that performed different actions made the field boundaries obvious. Broadcast packets used a special receiver value; movement and on/off commands used different command identifiers listed in the documentation.

Recovering the CRC

The documentation mentioned an initial CRC value of 0x1D0F and referenced a Microchip/STM32 implementation. That pointed strongly at CRC-16/CCITT. Feeding the payload (excluding the CRC itself) into a standard calculator with that initial value produced matching results for every captured packet. The CRC was stored high-byte first.

With a working CRC function, any new packet could be made valid. That removed the “silently discarded” failure mode and left only logical errors to debug.

The addressing surprise

Early attempts used the command codes from the documentation and a broadcast-style receiver address. The packets were accepted by the transmitter endpoint (HTTP 200) but produced no visible effect on the sensors.

Cycling the mysterious byte that had stayed constant in all captures eventually revealed the rule: the system expected the packet to be self-addressed. The sender field and the receiver field had to contain the same device identifier. Once that pattern was applied, suppress commands silenced individual alarms and turn-off commands disabled individual lasers.

The documentation never stated this requirement. It only became visible by treating the constant byte as a variable and watching the system react.

The parameter that almost ruined everything

Even with correct structure, correct CRC, and self-addressed commands, the sensors refused to change state when the transmission used 100 samples/symbol. Changing the bits-per-symbol parameter to 1 produced an immediate reaction: the first alarm turned red. Sending the full sequence with that single parameter fixed silenced every alarm and every laser.

The backend was clearly sensitive to the exact timing and symbol parameters used by the original hardware. A packet that looked perfect in hex still failed if the radio parameters did not match what the receivers expected.

Vulnerable Design Patterns (and How They Look in Code)

Proprietary RF stacks often implement validation in a way that looks like this:

// Vulnerable-style validation (simplified)
uint16_t received_crc = (packet[12] << 8) | packet[13];
uint16_t calculated = crc16_ccitt(packet, 12, 0x1D0F);

if (received_crc != calculated) {
    discard();          // silent drop
    return;
}

if (packet[9] != packet[10]) {   // undocumented self-address check
    discard();
    return;
}

dispatch_command(packet[8], packet[11]);

A more defensive approach would:

  • Reject packets that arrive with unexpected timing or modulation parameters.
  • Require cryptographic authentication in addition to CRC.
  • Log (or rate-limit) repeated invalid attempts instead of failing silently.
  • Document the addressing model so operators are not forced to reverse-engineer it.

Defense / How to Fix

  1. Authenticate, don’t just checksum. CRC detects corruption; it does not prove origin. Add a keyed MAC or a challenge-response layer.
  2. Make the addressing model explicit. If self-addressed commands are required, say so in the documentation and enforce it consistently.
  3. Validate radio parameters. Accept only the modulation, frequency, and symbol timing the hardware was designed for.
  4. Fail observably during development. Silent drops are convenient in production but make debugging (and attack surface analysis) far harder.
  5. Assume the air is hostile. Any protocol that can be recorded can be replayed or modified. Design as if an attacker already owns a compatible transmitter.

Final Thoughts

The most interesting failures in wireless security are rarely the exotic cryptographic breaks. They are the quiet mismatches between what the documentation claims and what the silicon actually accepts. A CRC that can be recomputed, an addressing rule that can be discovered by brute force, and a single transmission parameter that must match the original hardware are often enough to turn a “secure” perimeter into an open invitation.

The airwaves do not keep secrets. They only keep the people who never bother to listen.