MeshCore LoRa Sniffer
August 23, 2026 ยท View on GitHub

FREE Reverse Engineering Self-Study Course HERE
MeshCore LoRa Sniffer
A lightweight, dedicated firmware for the ESP32-S3 and SX1262 LoRa module designed to passively sniff and parse raw MeshCore RF packets in real-time.
Overview
This project uses RadioLib to put the SX1262 into continuous receive mode (RX_CONTINUOUS) and intercepts raw MeshCore transmissions over the air. It intercepts the physical layer RF frames and breaks down the MeshCore packet headers into human-readable formats (e.g., V0/GRP_TXT/FLOOD), allowing you to monitor mesh network activity, signal strength (RSSI), and signal-to-noise ratio (SNR) directly from a serial monitor.
Note on Encryption: MeshCore natively encrypts text payloads and datagrams using AES128. As a passive sniffer, this firmware captures the raw encrypted ciphertexts but does not possess the Curve25519 private keys required to decrypt the payloads into plaintext.
๐ก Understanding MeshCore RF Cryptography & Handshakes
If you are new to RF or MeshCore, it's important to understand how devices talk securely over the air. Because radio waves are broadcast in every direction, anyone with an antenna (like this sniffer!) can intercept them.
To prevent eavesdropping, MeshCore encrypts the actual messages. However, to establish connections, route packets, and verify identities, the radios must exchange certain unencrypted "metadata" before the encrypted payload begins. This unencrypted metadata is known as a Handshake.
The cryptographic flow of MeshCore relies on three main components:
- Curve25519 (ECDH): An elliptic curve algorithm used to generate Public/Private key pairs and negotiate shared secrets.
- AES128: The symmetric cipher used to encrypt the actual payload (like your text messages).
- SHA256 HMAC: A hashing algorithm used to generate a Message Authentication Code (MAC) to prove the packet wasn't tampered with.
Even though we cannot read the encrypted AES payloads, the sniffer automatically dissects the unencrypted handshakes. This leaks a massive amount of operational intelligence (SIGINT) about the network.
Here is exactly how you can use the intercepted handshakes to analyze and map the network:
1. Advertisement Broadcasts (ADVERT)
When a node joins the mesh, it must prove its identity to others. It sends a PAYLOAD_TYPE_ADVERT packet entirely in the clear.
- What you see: The sniffer extracts the node's 32-byte Curve25519 Public Key, the time it was emitted, and a massive 64-byte Cryptographic Signature.
- How to use this data: This is the Holy Grail of tracking. A Public Key uniquely identifies a physical node. Even if the node changes its username or channels, its Public Key remains the same. By logging
ADVERThandshakes, you can build a database of every unique radio in your area.
2. Anonymous Key Exchange (ANON_REQ)
When Node A wants to talk to Node B but they haven't established a secure session, Node A initiates an anonymous request.
- What you see: The sniffer pulls out the Destination Hash (a 1-byte ID of Node B) and an Ephemeral Public Key (a temporary 32-byte Curve25519 key generated just for this one session), followed by a 2-byte Cipher MAC.
- How to use this data: Ephemeral keys provide Perfect Forward Secrecy. By capturing these handshakes, a security auditor can verify that the network is properly rotating keys. If you see the same Ephemeral Public Key used twice, the network's RNG (Random Number Generator) is broken and the encryption is compromised!
3. Direct Node-to-Node Messages (REQ, RESPONSE, TXT_MSG, ACK)
Once keys are exchanged, nodes send standard encrypted messages to each other.
- What you see: The sniffer extracts the Destination Hash (who it's going to), the Source Hash (who sent it), and a 2-byte Cipher MAC.
- How to use this data (Topology Mapping): By logging the Source and Destination hashes over time, you can map the entire communication graph (Topology) of the mesh network. You can see who is talking to who, who the most active nodes are, and who acts as the central router for the group, all without decrypting a single message.
4. Group Broadcasts (GRP_TXT, GRP_DATA)
These are messages meant for an entire channel (like a public chat room).
- What you see: Since there is no specific destination node, the sniffer extracts the Channel Hash (a 1-byte ID for the group) and the Cipher MAC.
- How to use this data (Traffic Analysis): Because MeshCore relies on "Flooding" (nodes rebroadcasting packets they hear), you will often intercept the exact same packet multiple times from different radios. You can prove it's the exact same packet by looking at the Cipher MAC. If the 2-byte MAC is identical, it's the same message hopping across the network. This allows you to trace packet propagation delays and routing paths.
๐ Real-World Sniffer Example
When a MeshCore packet is intercepted, the sniffer breaks down the layers exactly as described above. Here is a live interception of a Group Text message (GRP_TXT):
================= NEW PACKET =================
Time: 27543 ms
RSSI: -25.0 dBm
SNR: 12.0 dB
Length: 21 bytes
----------------------------------------------
HEADER PARSING:
Raw Header Byte: 0x15
Bits 0-1 (0x01): ROUTE_TYPE_FLOOD
Bits 2-5 (0x05): PAYLOAD_TYPE_GRP_TXT
Bits 6-7 (0x00): PAYLOAD_VER_1
----------------------------------------------
HANDSHAKE & CRYPTO:
Channel Hash: 00
Cipher MAC: 5D 6B
----------------------------------------------
CIPHERTEXT PAYLOAD (17 bytes):
2C 7C 3D FB DB 59 CC E2 50 2C 6F B1 B2 AD 6C 9B 7E
ASCII:
|,|=..Y..P,o...l.~|
==============================================
Breakdown of the Interception:
- Signal Stats: The packet arrived at
27543 mssince boot, with a decent signal strength (-25 dBm) and a very clear Signal-to-Noise Ratio (12.0 dB). - Header Parsing: The raw
0x15byte was decoded. It tells us this is aGRP_TXTpayload that is activelyFLOODing the mesh network. - Handshake & Crypto: We can see it's destined for Channel Hash
00(the default public channel). It has a cryptographic MAC of5D 6B. If we hear another packet with MAC5D 6Ba few milliseconds later, we know another node is rebroadcasting this exact message. - Ciphertext: The remaining 17 bytes are AES128 encrypted. Without the shared channel key, it just looks like random garbage (
|,|=..Y..P,o...l.~|), but we've already extracted all the routing intelligence we need!
๐ป CLI Usage
Interact with the sniffer in real-time via the serial monitor by pressing the following keys (carriage returns \r and \n are silently ignored):
u- Apply USA/Canada parameters (910.525 MHz, SF7, CR 4/5)e- Apply EU parameters (869.525 MHz, SF7, CR 4/5)x- Toggle continuous reception on or off (Standby mode)t- Send a dummy test packet (Useful for testing transmission hardware)?- Print current radio status and configurationh- Print the help menu
๐ Setup & Installation
For full hardware pinouts, power configurations (LDO vs DCDC), and step-by-step flashing instructions, please refer to the SETUP.md guide!
๐ Code Formatting
This project is configured with clang-format using LLVM style (4 spaces). If using VS Code with the xaver.clang-format extension, formatting will automatically apply on save based on the included .clang-format configuration.