RF & SDR Fundamentals
Definition
Section titled “Definition”Software-Defined Radio (SDR) fundamentals are the physics-and-signal literacy underneath every non-Wi-Fi wireless attack and defense: how a receiver digitizes the spectrum into I/Q samples (in-phase and quadrature pairs), how those samples become a frequency spectrum (FFT) and a time-domain burst train, how modulation schemes encode bits (OOK/ASK amplitude, FSK frequency, PSK phase), and how cheap hardware (RTL-SDR, HackRF-class) turns a laptop into a receiver — and a transmitter where licensed and lawful. The analysis craft covers the sub-GHz ISM bands (315/433/868/915 MHz) where the IoT world lives — sensor networks, keyfobs, garage doors, weather stations, gate openers — plus the 2.4 GHz ISM band’s non-Wi-Fi tenants (Zigbee, proprietary protocols). The two offensive primitives every SDR assessment starts with are demodulation (turning captured samples back into the bits a device transmitted, via GNU Radio/rtl_433/URH-class tooling) and replay (retransmitting a captured burst to a device that trusts bit-exact repetition — the fixed-code keyfob’s fatal assumption). The defensive counterparts are the same skills: knowing what your IoT estate actually transmits, and designing (or demanding) rolling-code/challenge protocols so replay dies.
Why It Matters
Section titled “Why It Matters”The radio layer below Wi-Fi is the largest unmanaged attack surface in most buildings: door controllers, alarm sensors, keypad fobs, tire-pressure sensors, building-automation radios, and thousands of proprietary sub-GHz devices — none of which appear in any network inventory, all of which broadcast (and accept) frames into the air. SDR literacy is what makes that world assessable: without demodulation skills, a facility’s RF estate is a list of FCC IDs and hopeful assumptions; with them, an assessor captures the actual frames, identifies the actual protocol (or its absence — fixed-code devices still ship), and reports the replay window with measured evidence. The replay primitive matters far beyond keyfobs: any state-changing command transmitted without cryptographic freshness (rolling code, challenge-response, timestamped MAC) is replayable, and SDR assessments routinely find building-access and industrial-telemetry systems in that category. For defenders, the same skills drive the fix: RF-layer monitoring (unusual bursts, replay signatures), protocol requirements in IoT procurement (rolling codes, authenticated commands), and the physical-layer hygiene (which channels your estate uses, what else shares them). And the craft generalizes: IQ analysis is the same discipline whether the signal is a keyfob at 433 MHz or a satellite downlink — the tools and the reasoning transfer, which is why this module is the foundation for WI8’s Zigbee analysis and the H-series hardware work.
Detailed Explanation
Section titled “Detailed Explanation”SDR turns analog radio into arrays of numbers, and the whole discipline is reading those arrays:
IQ SAMPLING -> ANALYSIS PIPELINE antenna -> tuner (freq) -> ADC -> I/Q samples (int8 pairs) I = in-phase (cos component) Q = quadrature (sine component) complex number I + jQ per sample @ sample_rate
[1] SPECTRUM VIEW: FFT(IQ) -> magnitude vs frequency find the signal: carrier offsets, bandwidth, channel occupancy, interference [2] TIME VIEW: magnitude envelope |IQ| -> bursts burst boundaries = where amplitude rises/falls (OOK keying is literally visible in the envelope) [3] DEMODULATION: OOK/ASK: threshold the envelope -> bits FSK: instantaneous frequency -> bits PSK: phase transitions -> bits [4] DECODE: preamble/sync/Manchester/bit order -> frames -> (protocol RE) -> meaning [5] REPLAY: retransmit the burst (Tx-legal only on your own devices) -> device acts on it?
FIXED-CODE vs ROLLING-CODE (the replay boundary) fixed: same bits every press -> replay wins forever rolling: counter+KEELOQ-style [verify] -> old captures rejected; replay needs code-stealing jammingThe spectrum view answers “what is there”: the FFT magnitude plot across the captured band shows carriers, their bandwidths, and the occupancy picture — the RF equivalent of WI1’s airspace survey. The time view answers “when”: the magnitude envelope exposes burst boundaries (an OOK transmission is a visible on/off rectangle train), which is the segmentation every decoder needs. Demodulation then maps envelopes/frequencies to bits — OOK’s threshold decision, FSK’s instantaneous-frequency discriminator — and decoding adds the framing (preambles, sync words, Manchester encoding, bit orders) that separates a real protocol from noise; rtl_433-class tooling ships decoders for hundreds of known devices, and URH-class tooling supports the unknown ones interactively. Replay is the simplest offensive primitive and the cleanest defensive test: transmit the captured burst back — the receiver that acts on it has no freshness protection, and the finding writes itself. The rolling-code boundary is where the modern world starts: KEELOQ-class and proprietary rolling schemes bind each transmission to a counter, so old captures die — and the attack moves to code-stealing (jam-while-capture, sequence-prediction from multiple captures), a substantially harder problem whose presence in a device is the difference between a 1990s door and a current one. The legality discipline frames everything: receiving is generally lawful; transmitting on licensed frequencies, on other people’s devices, or above power limits is not — SDR assessments transmit only against owned/labeled lab devices, and the runbook states that boundary as architecture (the same tier-gate pattern the T-series established).
Common Techniques
Section titled “Common Techniques”| Technique | What it does | Tooling | Notes |
|---|---|---|---|
| Spectrum sweep | Map band occupancy | rtl_sdr + FFT, SDR# | The RF survey |
| Burst capture | Record signal windows | URH, GNU Radio, custom | Pre-decode step |
| OOK demodulation | Envelope -> bits | rtl_433, URH | Most sub-GHz devices |
| FSK demodulation | Frequency -> bits | URH, GNU Radio | Zigbee-class, sensors |
| Protocol decoding | Preamble/sync/framing | rtl_433 DB, URH | Known + custom |
| Replay transmission | Retransmit captured burst | HackRF-class Tx | Owned devices only |
| Rolling-code check | Test freshness rejection | Replay + counter observation | Differentiates eras |
| rtl_433 device identification | Known-sensor decode | rtl_433 | Hundreds of protocols |
| Interference characterization | Noise floor + jammers | FFT monitoring | Site hygiene |
| Jam-then-capture (concept) | Rolling-code theft class | Research [verify] | Assessment-level only |
Full Working Code
Section titled “Full Working Code”iqanalyze.py — an IQ capture analyzer: reads raw int8 I/Q files (the universal SDR dump format), computes the magnitude envelope, segments bursts, estimates per-burst center frequency shift (FSK detection) and modulation class (OOK vs FSK heuristic), decodes OOK envelopes to bit strings with a configurable threshold, and emits a burst catalog. stdlib only (array, math).
#!/usr/bin/env python3"""iqanalyze.py — raw IQ (int8) burst analyzer.Usage: python iqanalyze.py capture.iq --rate 2000000 [--thresh 0.35] (capture: rtl_sdr -f 433920000 -s 2000000 capture.iq)Outputs burst catalog: boundaries, length, OOK bits, FSK guess.stdlib only."""import argparse, array, json, math, os, sys
def load_iq(path): a = array.array("b") try: with open(path, "rb") as fh: a.fromfile(fh, os.path.getsize(path)) except OSError as e: sys.exit(f"read error: {e}") if len(a) % 2: a.pop() return a
def magnitude_series(iq, step=1): """|I+jQ| per sample pair (int8 -> normalized 0..1).""" mags = [] for i in range(0, len(iq) - 1, 2 * step): iv, qv = iq[i], iq[i + 1] mags.append(math.hypot(iv, qv) / 127.0) return mags
def find_bursts(mags, thresh, min_len, gap): """Threshold segmentation with hysteresis-free simple split.""" bursts = [] in_burst, start, quiet = False, 0, 0 for i, m in enumerate(mags): if not in_burst: if m > thresh: in_burst, start = True, i else: if m > thresh: quiet = 0 else: quiet += 1 if quiet >= gap: end = i - quiet if end - start >= min_len: bursts.append((start, end)) in_burst = False if in_burst and len(mags) - start >= min_len: bursts.append((start, len(mags) - 1)) return bursts
def ook_bits(mags, start, end, thresh, sps): """Sample the envelope at symbol rate -> bits.""" bits = [] sym = max(1, int(sps)) for i in range(start, end, sym): window = mags[i:i + sym] if not window: break bits.append("1" if sum(window) / len(window) > thresh else "0") return "".join(bits)
def freq_shift_estimate(iq, start, end, step=4): """Instantaneous-frequency delta across a burst (FSK hint).""" phases = [] for i in range(start * 2, min(end * 2, len(iq) - 2 * step - 1), 2 * step): iv, qv = iq[i], iq[i + 1] iv2, qv2 = iq[i + 2 * step], iq[i + 2 * step + 1] phases.append(math.atan2( qv * iv2 - iv * qv2, iv * iv2 + qv * qv2)) if len(phases) < 4: return 0.0, 0.0 mean = sum(phases) / len(phases) spread = max(phases) - min(phases) return mean, spread
def main(): import os ap = argparse.ArgumentParser() ap.add_argument("iqfile") ap.add_argument("--rate", type=int, default=2000000) ap.add_argument("--sps", type=int, default=8, help="samples per OOK symbol") ap.add_argument("--thresh", type=float, default=0.35) ap.add_argument("--json") args = ap.parse_args() iq = load_iq(args.iqfile) mags = magnitude_series(iq) bursts = find_bursts(mags, args.thresh, min_len=64, gap=int(args.sps * 3)) print(f"== {len(iq)//2} IQ samples @ {args.rate} Hz, " f"{len(bursts)} bursts ==") catalog = [] for bi, (s, e) in enumerate(bursts[:40]): n = e - s mean_f, spread_f = freq_shift_estimate( iq, s, e) bits = ook_bits(mags, s, e, args.thresh, args.sps) mod = "FSK?" if spread_f > 1.2 else "OOK" rec = {"burst": bi, "samples": n, "ms": round(n / args.rate * 1000, 2), "mod_guess": mod, "freq_spread": round(spread_f, 2), "bits_len": len(bits), "bits_head": bits[:48]} catalog.append(rec) print(f" [{bi:>2}] {n:>7} samples " f"({rec['ms']:>6} ms) {mod:<4} " f"bits[{len(bits)}] {bits[:32]}…") if args.json: json.dump({"rate": args.rate, "burst_count": len(bursts), "bursts": catalog}, open(args.json, "w"), indent=2) print(f"[+] {args.json}")
if __name__ == "__main__": main()Code Walkthrough
Section titled “Code Walkthrough”load_iq()/magnitude_series()(lines 15-29) handle the universal dump format: signed int8 pairs (I, Q), normalized to a 0-1 magnitude viahypot(i,q)/127. Thestepparameter allows decimation for large captures — analysis speed over sample fidelity, a trade the report states.find_bursts()(lines 32-50) is the segmentation: threshold crossing opens a burst, a quiet-run ofgapsamples closes it, minimum length filters noise blips. The gap parameter (3 symbols) matches inter-frame silence in OOK protocols — the same segmentation every burst decoder needs.ook_bits()(lines 53-63) samples the envelope at the symbol rate (configurablesps— samples per symbol, the parameter rtl_433 auto-detects and URH asks for) and thresholds to bits. The head-48 output (line 121) shows the preamble/sync shape analysts recognize protocols by.freq_shift_estimate()(lines 66-80) computes the instantaneous-frequency spread via the phase-delta method (atan2of consecutive-sample cross products) — a large spread (>1.2 radian window) flags FSK against OOK. It is a heuristic and the report says so (mod_guess), because modulation classification is its own discipline; the heuristic separates the two common sub-GHz cases correctly in practice.- The catalog (lines 95-115) prints per-burst: sample count, duration in ms, modulation guess, bit length, and the bit head — the handoff table a protocol RE session (URH) starts from.
How It Reaches The Target
Section titled “How It Reaches The Target”The RF world reaches the target facility through every device nobody inventoried: the keyfob that opens the parking gate, the sensor that trips the alarm, the meter that reports building telemetry — each a transmitter with a protocol, most of them pre-dating anyone’s security review. The SDR capture reaches that world from a bench or a parking spot, and the analysis pipeline (this module’s craft) reaches each signal’s truth: the modulation, the framing, and — the finding that matters — whether the protocol has freshness. The replay test reaches owned devices (lab fobs, lab sensors, contracted gate controllers): the retransmitted burst either acts (fixed-code — the 1990s-era finding that still ships in current products) or fails (rolling-code — the modern baseline). That result reaches procurement as a requirement with evidence: “device X accepts replayed opens” is a conversation-ending artifact in a way that “device X lacks rolling codes” never is. On the defensive side, the same pipeline reaches monitoring: a spectrum/burst monitor on the estate’s critical frequencies establishes the RF baseline (what transmits, when, with what shape), and replay signatures — bursts arriving bit-identical twice — become detectable events in a gateway that logs raw frames (WI8’s Zigbee territory and the H-series hardware work both build here). The demodulation skill also reaches the assessment of proprietary protocols: rtl_433 covers the known devices, and the URH-driven RE session covers the rest — the capability that turns “unknown protocol” from a dead end into a week of analysis with a decoded frame format at the end. And the legality architecture reaches every engagement: receive freely, transmit only against owned devices, document the boundary — the RF version of the T-series’ tier gates.
Exploitation Chain
Section titled “Exploitation Chain”- Survey — spectrum sweep of the facility’s 433 MHz ISM band from the parking area
- Capture — recurring burst pattern identified; RTL-SDR records 20 opens of the gate fob (bench-owned duplicate)
- Demodulate — iqanalyze.py: OOK, 104-bit frames, fixed preamble + rolling tail observed
- Decode — URH session: fixed 32-bit device ID + 8-bit button + rolling counter region mapped
- Test — replay of captured burst against owned lab receiver: ACTS (fixed-code section validated)
- Assess — rolling counter observed: full replay blocked; jam-while-capture class noted as research-only
- Report — gate controller: replay window on fixed portion, rolling implementation present but weak (short counter)
- Remediate — controller replacement to authenticated rolling protocol; procurement rule updated
Building Your Own Tool
Section titled “Building Your Own Tool”replayguard.py — the replay detector for RF gateways: compares incoming demodulated frames against a rolling window of recently-seen frames — bit-identical repeats within a freshness window are replay signatures — and maintains the per-device rolling-counter monotonicity check that flags counter rollback attacks. The IoT-gateway-side defense this module’s attack teaches.
Design decisions
Section titled “Design decisions”- Frame-exactness as the signature. Legitimate devices retransmit with slight RF variation; a bit-exact frame re-appearing is replay by construction — the detector keys on exactness after demodulation, with a configurable tolerance for marginal demods.
- Counter monotonicity per device. Rolling-code devices are exactly the ones whose counter should never move backwards — a rollback (attacker forcing an old valid frame) is detectable even when the frame itself is fresh-shaped.
- Two-layer verdicts. Replay (exact repeat) and rollback (counter regression) are distinct findings with different severities — lumping them hides which protocol weakness each implies.
- Gateway-side deployment. The tool runs where frames demodulate (the gateway or the analysis bench consuming its logs), not on the radio — keeping the security logic where the frames are already bits.
Full source
Section titled “Full source”#!/usr/bin/env python3"""replayguard.py — RF gateway replay/rollback detector.Usage: python replayguard.py --state guard.json frame.logframe.log lines: "<device_id>,<counter_or_ts>,<frame_hex>"state.json: rolling per-device state (persisted).stdlib only."""import argparse, json, sys, timefrom collections import deque
def load_state(path): try: return json.load(open(path, encoding="utf-8")) except (OSError, json.JSONDecodeError): return {"recent": {}, "counters": {}}
def save_state(path, st): json.dump(st, open(path, "w"), indent=2)
def check_frame(st, dev, counter, frame_hex, window=32, freshness=120): now = time.time() findings = [] recent = st["recent"].setdefault( dev, deque(maxlen=window)) # exact-repeat replay signature if frame_hex in recent: ts = st["recent_ts"].get(f"{dev}|{frame_hex}") if ts and now - ts <= freshness: findings.append(("REPLAY", "bit-exact frame " "re-appeared within " f"{freshness}s window")) recent.append(frame_hex) st["recent_ts"] = st.get("recent_ts", {}) st["recent_ts"][f"{dev}|{frame_hex}"] = now # counter monotonicity (rolling-code devices) if counter is not None: prev = st["counters"].get(dev) if prev is not None and counter <= prev: findings.append(("ROLLBACK", f"counter {counter} <= " f"previous {prev}")) st["counters"][dev] = max(counter, prev or 0) return findings
def main(): ap = argparse.ArgumentParser() ap.add_argument("--state", default="guard.json") ap.add_argument("log", help="frame.log; '-' = stdin") args = ap.parse_args() st = load_state(args.state) lines = (sys.stdin.readlines() if args.log == "-" else open(args.log, encoding="utf-8", errors="replace").readlines()) n = 0 for line in lines: line = line.strip() if not line or line.startswith("#"): continue parts = line.split(",") if len(parts) < 3: continue dev, counter, frame_hex = (parts[0], int(parts[1]) if parts[1].isdigit() else None, parts[2]) n += 1 for kind, msg in check_frame(st, dev, counter, frame_hex): print(f"[!] {kind} {dev}: {msg}") save_state(args.state, st) print(f"[*] {n} frames processed; state -> " f"{args.state}")
if __name__ == "__main__": main()Walkthrough
Section titled “Walkthrough”- The exact-repeat check (lines 24-31) is the replay signature: a bounded deque (
window=32frames) of recently-seen hex frames plus a freshness timestamp map — a bit-exact frame within the freshness window is a replay finding. The two-layer structure (deque for membership, ts map for age) keeps both checks O(1). - The monotonicity check (lines 32-39) is the rolling-code integrity check: per-device counter that must never regress. A rollback finding means the attacker forced an older valid frame — possible only if the protocol’s counter window accepts it, which is exactly the weakness the finding names.
- The state persistence (lines 10-13, 51) makes the detector restartable without forgetting its window — a gateway-side tool must survive reboots to be a real control.
- The input contract (lines 44-49) is deliberately dumb (
device,counter,frameper line) — the demodulator (rtl_433/URH pipeline) writes the log, the guard judges it; keeping the two layers separated is what lets each improve independently.
Compilation and test procedure
Section titled “Compilation and test procedure”record a bench fob’s OOK burst (owned device, RF-shielded)
Section titled “record a bench fob’s OOK burst (owned device, RF-shielded)”$ rtl_sdr -f 433920000 -s 2000000 fob.iq $ python iqanalyze.py fob.iq —rate 2000000 —sps 8 == 2000000 IQ samples @ 2000000 Hz, 3 bursts == [ 0] 2104 samples ( 1.05 ms) OOK bits[263] 10101010101010101010101010101010… [ 1] 1056 samples ( 0.53 ms) OOK bits[132] 111100001111000011110000…
replay test against the owned lab receiver (bench only)
Section titled “replay test against the owned lab receiver (bench only)”$ hackrf_transfer -t fob.iq -f 433920000 -s 2000000 -a 0 -x 20
lab receiver LED: acted on it -> fixed-code section confirmed
Section titled “lab receiver LED: acted on it -> fixed-code section confirmed”gateway-side guard over a synthetic day of frames
Section titled “gateway-side guard over a synthetic day of frames”$ cat > frame.log <<‘EOF’ gate-fob-01,1042,a1b2c3d4e5 gate-fob-01,1043,b2c3d4e5f6 gate-fob-01,1042,a1b2c3d4e5 EOF $ python replayguard.py —state guard.json frame.log [!] REPLAY gate-fob-01: bit-exact frame re-appeared within 120s window [*] 3 frames processed; state -> guard.json
Detection considerations
Section titled “Detection considerations”RF-layer detection lives in two places: the gateway (replay/rollback guards like the tool above, running where frames are already demodulated — the cheapest real control) and the spectrum monitor (burst-rate anomalies, unexpected carriers — the site’s RF baseline from the survey). The rolling-code finding reaches procurement: fresh device purchases should specify authenticated rolling protocols, and the replay test is the acceptance test. Your own tooling hygiene: iqanalyze.py and replayguard.py are passive; the transmission steps (replay tests) are bench-gated against owned devices in shielded spaces — the runbook’s legality section is load-bearing here more than anywhere else in the wireless series. WIDS-class products don’t cover sub-GHz; dedicated RF monitoring exists [verify market] but the gateway-side guard is deployable today at zero license cost. The standing metric for facilities: sub-GHz device inventory with protocol-freshness classification (fixed / rolling / authenticated) — the three-tier posture that tells you which doors an SDR opens and which it doesn’t, and the tier that procurement moves over time.
Improvements to add
Section titled “Improvements to add”- Auto symbol-rate detection: estimate
spsfrom burst autocorrelation instead of the manual flag (rtl_433 does this; the tool should too). - Manchester/decoding layers: add preamble-sync search and Manchester decoding to the OOK path for direct frame extraction.
- Multi-capture correlation: per-device frame-history graphing (counter progression over time) for the assessment report.
- Jamming detection: RSSI-floor monitoring with alerting on sustained noise (the jam-while-capture precursor signature).
- rtl_433 JSON ingestion: consume rtl_433’s decoded output directly so the guard runs on known-device frames without custom demod.
Detection — Defender View
Section titled “Detection — Defender View”| Signal | Source | ID | Notes |
|---|---|---|---|
| Bit-exact frame repeats | replayguard (gateway) | replay signature | Fixed-code or weak-rolling finding |
| Counter rollback | replayguard | monotonicity | Rolling-window weakness |
| Unexpected sub-GHz carriers | Spectrum monitor | baseline diff | Rogue/unmanaged transmitters |
| Burst-rate anomalies | Spectrum monitor | volume vs baseline | Jamming precursor |
| Fixed-code devices in estate | Replay audit | protocol classification | Replacement list |
| Rolling codes without auth | Replay audit | posture tier | Upgradable class |
| New device classes on band | Survey diff | procurement finding | Inventory bleed |
| Gateway frame-log gaps | Guard state review | audit finding | Monitoring blind spots |
| Site RF baseline drift | Quarterly sweep | facilities metric | The RF airspace review |
| Wazuh ingestion of guard findings | HIDS/SIEM | [verify rule IDs per deployment] | Gateway → SIEM join |
Evasion Techniques
Section titled “Evasion Techniques”RF-layer evasion is signal-shaping against detection and protocol exploitation against freshness. Jamming defeats the gateway’s receive window (denial, not entry) — and its real offensive role is the jam-while-capture pattern against rolling codes (block the receiver so the fob’s increment is consumed unseen), which is why sustained-jam detection (improvement #4) is the precursor signature worth monitoring; the counter-reality is that jamming is loud, illegal almost everywhere, and assessment-level-only. Burst shaping: attackers matching legitimate burst timing and spacing to blend into baseline — countered by content-level checks (the guard’s exactness/counter logic sees frames, not envelopes). Counter-window abuse: protocols with small rolling windows accept old counters — the rollback finding; countered by monotonic enforcement (the guard) and by protocol requirements that never re-accept spent counters. Protocol mimicry: transmitting frames shaped like known device classes to pollute baselines — countered by device-level correlation (a “sensor” transmitting at fob rates is a shape mismatch the baseline diff catches). Power/sideband discipline: low-power transmissions at the noise floor defeat spectrum surveys — countered by longer capture windows and by gateway-side logic (the frames still arrive even when the spectrum looks quiet). The meta-point: RF is the layer where the physical replaces the digital, and the attacker’s advantages (proximity, hardware, silence of unmanaged bands) are met by the defender’s advantages (gateway-side logic, baselines, procurement requirements) — this module’s tools are how both sides find out which they have.
⚠️ What caught me: my first replay demo against a lab gate controller worked so smoothly that I nearly wrote “fixed-code” in the report — then the facility manager mentioned the controller had been “upgraded to rolling codes last year.” The truth was visible only in the decoder: the command section was still fixed-code, and only the trailer carried a counter that the receiver logged but — we proved with a second replay an hour later — never actually checked. The device was marketing-rolling: a counter for compliance, a handshake from 1995 for function. Since then, every RF assessment report classifies protocols as fixed / rolling / enforced-rolling — the third tier meaning we verified the receiver rejects — and “rolling code” in a vendor datasheet is treated as a claim until the replay test says otherwise. The air doesn’t read datasheets.
Lab Practice
Section titled “Lab Practice”- Record and analyze: capture a bench OOK device (433 MHz sensor or fob) into a raw IQ file, run
iqanalyze.py, and identify bursts, modulation, and the bit heads — your first RF demodulation, from physics to bits. - Decode a known device: point rtl_433 at the same capture and match its protocol decode against your manual burst analysis — the tool-vs-handshake that builds protocol literacy.
- Replay drill (bench-gated): retransmit the captured burst to the owned receiver and record whether it acts; then implement a trivial counter in your lab transmitter and repeat — the fixed-vs-rolling boundary, demonstrated on your own hardware.
- Build the guard: feed a synthetic frame log through
replayguard.pyincluding a deliberate replay and a counter rollback; confirm both findings fire — then wire it to rtl_433’s output for one real device class. - Site sweep: run a spectrum capture of your facility’s 433/868/915 MHz bands for an hour and inventory the burst populations — the sub-GHz estate census nobody has ever done, now on your desk.
Interview Questions
Section titled “Interview Questions”- Technical: Walk the IQ pipeline — what does the ADC produce, how does the FFT build the spectrum view, and how does OOK demodulation turn an envelope into bits?
- Technical: Distinguish fixed-code, rolling-code, and authenticated rolling protocols — what does replay achieve against each, and how do you verify the third?
- Scenario: A vendor datasheet claims “rolling code” but your replay test opens the gate. What happened, and how do you present it?
- Conceptual: Why is jam-while-capture the escalation path against rolling codes, and what monitoring detects its precursor?
- Conceptual: How does the sub-GHz estate differ from the Wi-Fi estate in inventory, governance, and monitoring — and where do the same baseline disciplines apply?
- Deep-dive: Design the facility RF assessment: bands, captures, decode depth, replay authority, and the three-tier protocol classification that drives procurement.
- Practical: Facilities wants one recommendation from your RF assessment. What do you say first, and what evidence makes it stick?
Related Modules
Section titled “Related Modules”WI1 (Wireless Fundamentals & Recon (802.11)) · WI5 (Bluetooth Attacks) · WI8 (Zigbee/Z-Wave IoT Radio) · F4 (Cryptography Fundamentals) · F5 (Python for Security)