Skip to content

RF & SDR Fundamentals

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.

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.

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 jamming

The 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).

TechniqueWhat it doesToolingNotes
Spectrum sweepMap band occupancyrtl_sdr + FFT, SDR#The RF survey
Burst captureRecord signal windowsURH, GNU Radio, customPre-decode step
OOK demodulationEnvelope -> bitsrtl_433, URHMost sub-GHz devices
FSK demodulationFrequency -> bitsURH, GNU RadioZigbee-class, sensors
Protocol decodingPreamble/sync/framingrtl_433 DB, URHKnown + custom
Replay transmissionRetransmit captured burstHackRF-class TxOwned devices only
Rolling-code checkTest freshness rejectionReplay + counter observationDifferentiates eras
rtl_433 device identificationKnown-sensor decodertl_433Hundreds of protocols
Interference characterizationNoise floor + jammersFFT monitoringSite hygiene
Jam-then-capture (concept)Rolling-code theft classResearch [verify]Assessment-level only

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()
  • load_iq() / magnitude_series() (lines 15-29) handle the universal dump format: signed int8 pairs (I, Q), normalized to a 0-1 magnitude via hypot(i,q)/127. The step parameter 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 of gap samples 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 (configurable sps — 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 (atan2 of 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.

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.

  1. Survey — spectrum sweep of the facility’s 433 MHz ISM band from the parking area
  2. Capture — recurring burst pattern identified; RTL-SDR records 20 opens of the gate fob (bench-owned duplicate)
  3. Demodulate — iqanalyze.py: OOK, 104-bit frames, fixed preamble + rolling tail observed
  4. Decode — URH session: fixed 32-bit device ID + 8-bit button + rolling counter region mapped
  5. Test — replay of captured burst against owned lab receiver: ACTS (fixed-code section validated)
  6. Assess — rolling counter observed: full replay blocked; jam-while-capture class noted as research-only
  7. Report — gate controller: replay window on fixed portion, rolling implementation present but weak (short counter)
  8. Remediate — controller replacement to authenticated rolling protocol; procurement rule updated

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.

  • 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.
#!/usr/bin/env python3
"""replayguard.py — RF gateway replay/rollback detector.
Usage:
python replayguard.py --state guard.json frame.log
frame.log lines: "<device_id>,<counter_or_ts>,<frame_hex>"
state.json: rolling per-device state (persisted).
stdlib only.
"""
import argparse, json, sys, time
from 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()
  • The exact-repeat check (lines 24-31) is the replay signature: a bounded deque (window=32 frames) 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,frame per 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.

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

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.

  1. Auto symbol-rate detection: estimate sps from burst autocorrelation instead of the manual flag (rtl_433 does this; the tool should too).
  2. Manchester/decoding layers: add preamble-sync search and Manchester decoding to the OOK path for direct frame extraction.
  3. Multi-capture correlation: per-device frame-history graphing (counter progression over time) for the assessment report.
  4. Jamming detection: RSSI-floor monitoring with alerting on sustained noise (the jam-while-capture precursor signature).
  5. rtl_433 JSON ingestion: consume rtl_433’s decoded output directly so the guard runs on known-device frames without custom demod.
SignalSourceIDNotes
Bit-exact frame repeatsreplayguard (gateway)replay signatureFixed-code or weak-rolling finding
Counter rollbackreplayguardmonotonicityRolling-window weakness
Unexpected sub-GHz carriersSpectrum monitorbaseline diffRogue/unmanaged transmitters
Burst-rate anomaliesSpectrum monitorvolume vs baselineJamming precursor
Fixed-code devices in estateReplay auditprotocol classificationReplacement list
Rolling codes without authReplay auditposture tierUpgradable class
New device classes on bandSurvey diffprocurement findingInventory bleed
Gateway frame-log gapsGuard state reviewaudit findingMonitoring blind spots
Site RF baseline driftQuarterly sweepfacilities metricThe RF airspace review
Wazuh ingestion of guard findingsHIDS/SIEM[verify rule IDs per deployment]Gateway → SIEM join

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.

  1. 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.
  2. 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.
  3. 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.
  4. Build the guard: feed a synthetic frame log through replayguard.py including a deliberate replay and a counter rollback; confirm both findings fire — then wire it to rtl_433’s output for one real device class.
  5. 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.
  • 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?

WI1 (Wireless Fundamentals & Recon (802.11)) · WI5 (Bluetooth Attacks) · WI8 (Zigbee/Z-Wave IoT Radio) · F4 (Cryptography Fundamentals) · F5 (Python for Security)