Rayhunter · Volume 8
Rayhunter Volume 8 — Reading the Recordings
Pulling QMDL/PCAP off the device, reviewing the recorded control plane in Wireshark, cross-referencing serving cells against public tower databases, and a host-side summary workflow
8.1 About this Volume
Everything here is post-hoc review of the detector’s own exported logs. Rayhunter records the modem’s control-plane diagnostic stream while it runs; this volume is about getting those files onto a host and reading them to understand what the detector saw. It is review and sanity-checking, not attack methodology — the only cellular traffic in view is the control plane the operator’s own device already exchanged with the network, saved by the operator’s own detector.
The two things worth being clear about up front. First, Rayhunter records the control plane — the signalling between the device and the tower (identity requests, security mode, system-information broadcasts, connection setup/release) — not user traffic (calls, texts, data payloads are not its subject). Second, reading a recording is how a warning gets turned from a coloured line into something an operator, or an expert they hand it to, can reason about. Volume 5 explains the analyzers; this volume explains reading their raw material.
8.2 What Rayhunter records: QMDL and PCAP
Qualcomm modems expose a diagnostic (“DIAG”) interface that streams the modem’s internal control-plane events. Rayhunter records that stream to a QMDL file (Qualcomm MoDem Log) — the native, lossless capture of the DIAG messages. For analysis in standard tooling, the same control plane is also made available as a PCAP, the universal packet-capture format that Wireshark and the tshark/pcap ecosystem read directly.
Table 1 — 2. What Rayhunter records: QMDL and PCAP
| Artifact | What it is | Read it with |
|---|---|---|
| QMDL | The modem’s native DIAG log — the raw, complete record | Rayhunter’s own analyzers; Qualcomm-aware tooling |
| PCAP | The control-plane messages in standard packet-capture form | Wireshark / tshark, and any pcap library |
The web UI’s Download control exports a recording for off-device review. The PCAP is the one most users open first, because Wireshark dissects LTE RRC and NAS messages natively; the QMDL is the artifact to keep as the lossless original and to feed back through Rayhunter’s analyzers (§8).
Why the control plane is enough to detect behaviour. The analyzers (Vol 5) key off control-plane events — an identity request without authentication, a redirect to 2G, a null-cipher suggestion, a stripped-down system-information broadcast. None of that requires user-plane content, which is why Rayhunter can be useful while recording nothing a user would consider private traffic.
8.3 Pulling the files off the device
Two routes, matching the two web-UI access paths from Volume 6.
Over the web UI (simplest). Browse to http://<device>:8080, pick the recording, and use Download. Because Rayhunter serves plain HTTP, the browser may show an “insecure download blocked” prompt — expected on the local link, and safe to accept for this device. This gives the QMDL and/or PCAP straight to the host’s download folder.
Over USB/ADB (Orbic). With ADB set up (Vol 6 §6), the port-forward that exposes the UI (adb forward tcp:8080 tcp:8080) also lets a host reach the download endpoint at http://localhost:8080. Recordings can be retrieved the same way without joining the device’s Wi-Fi.
# Host side, Orbic over USB: forward the UI/download port, then pull via the web UI
adb forward tcp:8080 tcp:8080
# → browse http://localhost:8080 and use Download, or script the download endpoint
# Keep an organized, per-trip archive on the host (untrusted data in its own dir)
mkdir -p ~/rayhunter/2026-10-17-trip && cd ~/rayhunter/2026-10-17-trip
# place the exported .qmdl and .pcap here
ls -la
# rayhunter_2026-10-17_143022.qmdl
# rayhunter_2026-10-17_143022.pcap
Handle recordings as sensitive data. A recording is a log of where the device was and the control plane it saw. Store exports in a dedicated per-trip directory, keep them off shared machines, and delete the on-device original only after the export is safely archived (Vol 7 §8, Vol 10 OPSEC).
8.4 Opening the PCAP in Wireshark
Opening the exported PCAP in Wireshark shows the control plane Rayhunter recorded as a packet list. The dissectors to lean on are the LTE ones:
- LTE RRC (Radio Resource Control) — the layer carrying system-information broadcasts (SIBs), connection setup, reconfiguration, and release/redirect. This is where a redirect toward 2G, or a system-information block that ranks 2G/3G above LTE, shows up.
- NAS-EPS (Non-Access Stratum) — the layer carrying identity requests, authentication, and the security-mode command. This is where an identity request without authentication, or a null-cipher security-mode command, appears.
A practical reading pass, mapping to the analyzers rather than hunting blindly:
Table 2 — A practical reading pass, mapping to the analyzers rather than hunting blindly
| In Wireshark, look at… | …to see the pattern behind |
|---|---|
nas-eps identity-request messages, and whether authentication follows | IMSI Requested analyzer |
lte-rrc connection-release with a redirect to a 2G carrier | 2G Downgrade analyzer |
lte-rrc SIB6 / SIB7 contents and the priority they assign 2G/3G vs LTE | LTE SIB6/7 Downgrade analyzer |
| The RRC security-mode / cipher suggested (EEA0 = null) | Null Cipher analyzer |
| The NAS security-mode command’s cipher after authentication | NAS Null Cipher analyzer |
| Whether SIB1 schedules at least two further SIBs (e.g. SIB3/4/5) | Incomplete SIB analyzer |
| Connect/disconnect events framing a session | Diagnostic Information (context, not a warning) |
Display filters keep the view tractable:
nas-eps.emm.type_mm == 0x55 # EMM Identity Request (NAS identity ask)
lte-rrc.sib6_element # presence of SIB6 content
lte-rrc.sib7_element # presence of SIB7 content
lte-rrc.c1 == 4 # RRC Connection Release (redirect lives here)
(Exact filter names track the Wireshark version’s LTE dissectors; use the detail pane to confirm field names, then right-click → Apply as Filter.)
8.5 Cross-referencing the serving cell against tower databases
A recording carries the identifiers of the cells the device attached to — the operator’s MCC/MNC (country + network), the TAC (tracking area), and the Cell ID / eNB ID. Those identifiers can be checked against a public, crowdsourced tower database to sanity-check whether a cell the device saw is a known, legitimately-mapped cell at that location, or one with no public record.
OpenCelliD (opencellid.org) is the common reference — a free, community-maintained database of cell towers keyed by MCC/MNC/LAC-or-TAC/CellID with approximate coordinates. The sanity-check reasoning:
- A serving cell whose identifiers resolve to a known tower at roughly the location the device was in is unremarkable.
- A serving cell with identifiers that don’t appear in the database, or that map to a wildly different location, is worth a second look — not proof of anything (crowdsourced databases are incomplete, and new/temporary legitimate cells exist), but a data point that lines up with, or against, an analyzer warning.
Table 3 — 5. Cross-referencing the serving cell against tower databases
| Identifier | What it is | Use in the check |
|---|---|---|
| MCC / MNC | Mobile country code / network code | Confirms the carrier is the expected one |
| TAC / LAC | Tracking / location area code | Narrows the area; a mismatch with location is notable |
| Cell ID / eNB | The specific cell | The key for a tower-database lookup |
A database miss is not a detection. OpenCelliD does not list every legitimate cell, and carriers add, move, and retire cells constantly. “Not in the database” is a weak signal that gains weight only alongside an analyzer warning and the surrounding context. Treat tower lookups as corroboration, never as an independent verdict.
8.6 Analyzer → meaning → what to check
The reference table for turning a fired analyzer into a review action. (Volume 5 is the authority on how each analyzer decides; this maps each to a plain-language meaning and what to look at in the recording.)
Table 4 — 6. Analyzer → meaning → what to check
| Analyzer (Vol 5) | Category-level meaning | What to check in the recording |
|---|---|---|
| IMSI Requested | The network asked for the device’s permanent identity (IMSI/IMEI) in a suspicious pattern — e.g. new tower → identity asked → no authentication → disconnect | NAS identity-request messages; whether authentication followed; whether a connection even preceded the request |
| Connection Release / 2G Downgrade | A base station released the connection and redirected the device to a 2G cell, where interception is easier | RRC connection-release with a redirect to a 2G carrier; what cell it pointed at |
| LTE SIB6/7 Downgrade | An LTE tower broadcast SIB6/SIB7 giving 2G/3G frequencies higher priority than LTE (a well-behaved tower ranks 4G neighbours higher) | SIB6/SIB7 contents and the reselection priorities they set; malformed SIB7 is the more common of the two |
| Null Cipher | The cell suggested the null cipher (EEA0) at the RRC layer — radio encryption off, which should almost never happen outside test/regulated cases | The RRC security-mode / cipher negotiated |
| NAS Null Cipher | A NAS-layer security-mode command suggested EEA0 after successful core-network authentication — possible attack (e.g. via SS7-obtained key material) or a rare misconfiguration | The NAS security-mode command’s cipher choice and its position after authentication |
| Incomplete SIB | SIB1 failed to schedule at least two further SIBs (commonly SIB3/4/5); fake base stations often omit them | SIB1’s scheduling list; weak alone, strong combined with IMSI Requested |
| Diagnostic Information | Context: logs tower connect/disconnect to aid PCAP review — informational, not a warning | Use as the timeline that frames the other analyzers’ events |
| No NAS Messages (experimental, default off) | ≥5 min of diagnostic traffic with no NAS message — may mean a missing/inactive/improperly-seated SIM or no reachable network | SIM state and whether the device had service; prone to false positives |
| Test Analyzer (default off) | Fires on every SIB1 broadcast — a noisy self-test to confirm Rayhunter is alive | Only ever used to confirm the detector works; expect constant firing |
Read combinations, not single hits. The design intent is that several lower-confidence signals together are stronger than any one alone — an Incomplete SIB on its own might be a misconfigured tower, but an Incomplete SIB alongside an IMSI Requested is a much stronger indicator. The severity ladder (Vol 7 §5) already reflects some of this; the recording lets the reviewer see the co-occurrence directly.
8.7 A host-side summary workflow
A realistic review session: pull the files, get a quick packet-level summary with tshark (Wireshark’s CLI), then a small script to turn that into a per-trip, human-readable digest. All of it reads the detector’s own output.
#!/usr/bin/env bash
# review-trip.sh — summarize one Rayhunter trip's exported PCAP.
# Reads ONLY the detector's own recording. No network, no attack surface.
set -euo pipefail
PCAP="$1" # e.g. ~/rayhunter/2026-10-17-trip/rayhunter_2026-10-17_143022.pcap
echo "== Capture overview =="
capinfos -aceu "$PCAP" # packet count, duration, start/end times
echo; echo "== NAS identity requests (IMSI Requested signal) =="
tshark -r "$PCAP" -Y 'nas-eps.emm.type_mm == 0x55' \
-T fields -e frame.number -e frame.time -e nas_eps.emm.id_type2
echo; echo "== RRC connection releases / redirects (2G-downgrade signal) =="
tshark -r "$PCAP" -Y 'lte-rrc.rrcConnectionRelease_element' \
-T fields -e frame.number -e frame.time
echo; echo "== SIB6 / SIB7 presence (SIB downgrade signal) =="
tshark -r "$PCAP" -Y 'lte-rrc.sib6_element or lte-rrc.sib7_element' \
-T fields -e frame.number -e frame.time
echo; echo "== Serving-cell identifiers seen (for OpenCelliD cross-check) =="
tshark -r "$PCAP" -T fields -e lte-rrc.cellIdentity -e lte-rrc.trackingAreaCode \
| sort -u | sed '/^\t*$/d'
#!/usr/bin/env python3 -I
"""trip_digest.py — one-line-per-event digest from tshark JSON of a Rayhunter PCAP.
Run tshark with -T json -x over the detector's own export and pipe it here.
Reads untrusted capture data only as data; no exec, no network."""
import json, sys
events = json.load(sys.stdin) # tshark -T json output
counts = {}
for pkt in events:
layers = pkt.get("_source", {}).get("layers", {})
if "nas-eps" in layers:
counts["nas_identity_or_security"] = counts.get("nas_identity_or_security", 0) + 1
if "lte-rrc" in layers:
counts["rrc_control"] = counts.get("rrc_control", 0) + 1
print("Rayhunter trip digest")
for k, v in sorted(counts.items()):
print(f" {k:28s} {v}")
print("Review any NAS identity request without a following authentication (Vol 8 §6).")
The point of the script layer is not automation of judgement — it is to surface the handful of control-plane events worth a human’s eyes, so a reviewer opens Wireshark already knowing which frames to inspect.
8.8 Re-running the analyzers offline
Rayhunter’s analyzers (Vol 5) run on-device in real time, but the same heuristics can be applied to a saved QMDL after the fact on a host. Rayhunter’s codebase is a Rust workspace, and alongside the on-device daemon it builds tooling that ingests a recorded QMDL and reports which analyzers would fire — the offline counterpart to the on-device web-UI analysis view. This is useful for two things: reviewing a recording on a full keyboard-and-screen host rather than the hotspot’s tiny UI, and re-checking an old recording after an analyzer has been improved in a newer release.
The workflow shape (consult the repo’s current README and tool help for exact invocation, as the tooling tracks the release):
1. Download the .qmdl (the lossless original) from the device (§3)
2. Build or obtain Rayhunter's host-side analysis tool from the repo (Rust / cargo)
3. Point it at the .qmdl → it reports the analyzer findings, same heuristics as on-device
4. Cross-read against the Wireshark PCAP view (§4) and the tower lookups (§5)
Keep the QMDL, not just the PCAP. The QMDL is the lossless native log; the PCAP is a derived, Wireshark-friendly view. Re-running improved analyzers, or handing a recording to an expert, both want the QMDL. Archive it as the original (Vol 7 §8).
8.9 What a log can and cannot tell you
The honest boundary of recording review:
A recording can:
- Show the specific control-plane events behind a warning — the actual identity request, release/redirect, SIB, or cipher choice.
- Let an analyzer’s finding be confirmed, contextualized, or discounted by a human or an expert.
- Preserve the evidence of a suspicious session for later analysis, reporting, or comparison across trips.
- Supply serving-cell identifiers for a tower-database sanity check (§5).
A recording cannot:
- Identify who operated a suspicious cell, with what device, or for what purpose. The control plane does not carry that.
- Turn a heuristic warning into a certainty. The analyzers flag patterns associated with cell-site simulators; legitimate networks sometimes produce similar patterns (Vol 7 §6).
- Reconstruct user traffic — calls, texts, data payloads are not recorded and are not the subject.
- Prove absence. A quiet recording is consistent with “nothing happened” and with “a stealthy technique the heuristics don’t catch” (Vol 9 covers Rayhunter’s limits).
When it matters, hand it to an expert. EFF’s own guidance frames the PCAP as something a user can pass to someone with cellular-security expertise for deeper review. The operator’s job is to capture, preserve, and do the first-order read; a definitive call on an ambiguous recording is expert territory.
Volume 9 places Rayhunter among the other detection projects — SnoopSnitch, CellGuard, SeaGlass, Crocodile Hunter — and situates where a general SDR like the HackRF One fits, with an honest account of what this class of tool can and cannot do.
Comments (0)