|
#include <Arduino.h> |
|
#include <WiFi.h> |
|
#include <esp_wifi.h> |
|
#include <esp_wifi_types.h> |
|
#include <string.h> |
|
|
|
// ============================================================================ |
|
// GLOBAL VARIABLES |
|
// ============================================================================ |
|
|
|
// A global offset counter for the hex dump, reset inside the handler |
|
uint16_t offset = 0; |
|
static uint8_t current_channel = 1; |
|
static unsigned long last_channel_hop = 0; |
|
|
|
#define MAX_CHANNEL 11 |
|
#define CHANNEL_HOP_INTERVAL 500 // milliseconds |
|
|
|
// ============================================================================ |
|
// GLOBAL VARIABLES |
|
void hop_channel() { |
|
unsigned long now = millis(); |
|
|
|
// Check if the CHANNEL_HOP_INTERVAL (300ms) has passed since the last hop |
|
if (now - last_channel_hop > CHANNEL_HOP_INTERVAL) { |
|
current_channel++; |
|
if (current_channel > MAX_CHANNEL) { |
|
current_channel = 1; // Loop back to channel 1 |
|
} |
|
|
|
esp_wifi_set_channel(current_channel, WIFI_SECOND_CHAN_NONE); |
|
last_channel_hop = now; |
|
printf("Hopped to channel %d\n", current_channel); |
|
} |
|
} |
|
|
|
|
|
// ============================================================================ |
|
// STRUCT DEFINITIONS FOR 802.11 PARSING |
|
// ============================================================================ |
|
|
|
// Standard 802.11 MAC header (24 bytes) |
|
typedef struct { |
|
uint16_t frame_ctrl; |
|
uint16_t duration_id; |
|
uint8_t addr1[6]; // Destination MAC (DA) |
|
uint8_t addr2[6]; // Source MAC (SA) / Transmitter Address (TA) |
|
uint8_t addr3[6]; // BSS ID (BSSID) / Receiver Address (RA) |
|
uint16_t seq_ctrl; |
|
// Note: addr4 is present in WDS/Mesh frames, but typically not here. |
|
} wifi_ieee80211_mac_hdr_t; |
|
|
|
// Full packet structure with the header and payload |
|
typedef struct { |
|
wifi_ieee80211_mac_hdr_t hdr; |
|
uint8_t payload[0]; // Start of the fixed parameters/payload |
|
} wifi_ieee80211_packet_t; |
|
|
|
//~ /** |
|
//~ * @brief Cycles through Wi-Fi channels to capture traffic across the 2.4 GHz band. |
|
//~ */ |
|
//~ void promiscuous_rx_cb(void* buffer, wifi_promiscuous_pkt_type_t type) { |
|
//~ // Cast the raw buffer to the ESP-IDF promiscuous packet structure |
|
//~ wifi_promiscuous_pkt_t* p = (wifi_promiscuous_pkt_t*)buffer; |
|
|
|
//~ // Use the signal length from the control structure |
|
//~ uint16_t packet_length = p->rx_ctrl.sig_len; |
|
|
|
//~ // Reset offset for a new packet |
|
//~ offset = 0; |
|
|
|
//~ // Hex dump logic |
|
//~ for (int i = 0; i < packet_length; i++) { |
|
//~ // Print the offset marker every 8 bytes |
|
//~ if (i % 8 == 0) { |
|
//~ // Use Serial.printf for standard Arduino environment |
|
//~ Serial.printf("%06X ", offset); |
|
//~ offset += 8; |
|
//~ } |
|
|
|
//~ // Print the byte in hex |
|
//~ Serial.printf("%02X ", p->payload[i]); |
|
|
|
//~ // Print a newline every 8 bytes, or if it's the last byte |
|
//~ if (i % 8 == 7) { |
|
//~ Serial.printf("\n"); |
|
//~ } else if ((i + 1) == packet_length) { |
|
//~ Serial.printf("\n"); |
|
//~ } |
|
//~ } |
|
//~ } |
|
|
|
|
|
// ============================================================================ |
|
// WIFI PROMISCUOUS MODE HANDLER |
|
// ============================================================================ |
|
|
|
/** |
|
* @brief Formats a MAC address array into a string. |
|
*/ |
|
String mac_to_string(const uint8_t* mac) { |
|
char buf[18]; |
|
sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", |
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
|
return String(buf); |
|
} |
|
|
|
/** |
|
* @brief Extracts and prints human-readable data from the Wi-Fi packet. |
|
*/ |
|
void promiscuous_rx_cb(void* buffer, wifi_promiscuous_pkt_type_t type) { |
|
// Cast the raw buffer to the promiscuous packet structure |
|
const wifi_promiscuous_pkt_t* ppkt = (wifi_promiscuous_pkt_t*)buffer; |
|
|
|
// Cast the payload to the 802.11 packet structure for header access |
|
const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; |
|
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; |
|
|
|
// Frame Control: Byte 0 of the MAC header contains Type (bits 2-3) and Subtype (bits 4-7) |
|
uint8_t frame_subtype = (hdr->frame_ctrl & 0xFC) >> 2; // Type and Subtype bits |
|
|
|
// Filter for Management Frames: Probe Request (Subtype 4) or Beacon (Subtype 8) |
|
//~ if (frame_subtype != 0x04 && frame_subtype != 0x08) { |
|
//~ return; // Ignore all other frame types |
|
//~ } |
|
|
|
// Determine the packet type string and where the IEs start |
|
const char *pkt_type_str = "UNKNOWN"; |
|
uint16_t ie_start_offset = 0; |
|
|
|
if (frame_subtype == 0x40) { |
|
// Probe Request (0x40): MAC Header (24) + Fixed Params (2) = 26 |
|
pkt_type_str = "PROBE REQUEST"; |
|
ie_start_offset = 26; |
|
} else if (frame_subtype == 0x80) { |
|
// Beacon (0x80): MAC Header (24) + Fixed Params (12) = 36 |
|
pkt_type_str = "BEACON"; |
|
ie_start_offset = 36; |
|
} else { |
|
return; // Should be caught by the first check, but safe guard. |
|
} |
|
|
|
// Pointer to the start of the Information Elements (IEs) |
|
uint8_t *ie_payload = (uint8_t *)ppkt->payload + ie_start_offset; |
|
uint16_t ie_payload_len = ppkt->rx_ctrl.sig_len - ie_start_offset; |
|
|
|
// Prepare for SSID extraction |
|
char ssid[33] = {0}; |
|
uint8_t ssid_length = 0; |
|
|
|
// Iterate through IEs to find the SSID (Tag 0) |
|
// The structure of an IE is: [Tag (1 byte)][Length (1 byte)][Data (Length bytes)] |
|
for (uint16_t i = 0; i < ie_payload_len; ) { |
|
uint8_t tag = ie_payload[i]; |
|
uint8_t len = ie_payload[i + 1]; |
|
|
|
// Check if this is the SSID element (Tag 0) |
|
if (tag == 0 && len <= 32) { |
|
ssid_length = len; |
|
// Copy data (which starts at i + 2) |
|
memcpy(ssid, &ie_payload[i + 2], ssid_length); |
|
ssid[ssid_length] = '\0'; // Null termination |
|
break; // Found SSID, stop searching IEs |
|
} |
|
|
|
// Move to the next IE: Current IE size (Tag + Length + Data) |
|
i += (2 + len); |
|
} |
|
|
|
// Apply the filter: Skip logging if SSID is a wildcard (length 0) |
|
// BUT only for PROBE REQUESTS. Beacons should always have a length > 0. |
|
if (frame_subtype == 0x04 && ssid_length == 0) { |
|
// This is a filtered wildcard probe request (SSID is NULL) |
|
// If you wanted to log that it happened: |
|
// Serial.printf("[CH%02d | %4d dBm] Wildcard Probe from %s\n", |
|
// current_channel, ppkt->rx_ctrl.rssi, mac_to_string(hdr->addr2).c_str()); |
|
return; |
|
} |
|
|
|
// ======================================================================== |
|
// PRINT HUMAN-READABLE OUTPUT |
|
// ======================================================================== |
|
|
|
Serial.printf("----------------------------------------------------------\n"); |
|
Serial.printf("| %-13s | CH %02d | RSSI %4d dBm |\n", |
|
pkt_type_str, current_channel, ppkt->rx_ctrl.rssi); |
|
Serial.printf("----------------------------------------------------------\n"); |
|
|
|
// Print MAC addresses (Source MAC is addr2) |
|
Serial.printf("SOURCE MAC: %s\n", mac_to_string(hdr->addr2).c_str()); |
|
Serial.printf("DESTINATION MAC: %s\n", mac_to_string(hdr->addr1).c_str()); |
|
Serial.printf("BSSID: %s\n", mac_to_string(hdr->addr3).c_str()); |
|
|
|
// Print the SSID |
|
if (ssid_length > 0) { |
|
Serial.printf("SSID: %s\n", ssid); |
|
} else { |
|
// This should only happen for certain types of management frames |
|
// that still need logging, or if IE parsing failed unexpectedly. |
|
Serial.printf("SSID: [Unknown/Empty]\n"); |
|
} |
|
|
|
Serial.printf("Packet Length: %u bytes\n", ppkt->rx_ctrl.sig_len); |
|
Serial.printf("----------------------------------------------------------\n\n"); |
|
} |
|
|
|
void setup() { |
|
Serial.begin(115200); |
|
delay(100); |
|
|
|
Serial.println("Starting Wi-Fi Promiscuous Sniffer..."); |
|
|
|
WiFi.mode(WIFI_MODE_STA); // WIFI_STA? |
|
WiFi.disconnect(); |
|
|
|
esp_wifi_set_channel(current_channel, WIFI_SECOND_CHAN_NONE); |
|
esp_wifi_set_promiscuous(true); |
|
esp_wifi_set_promiscuous_rx_cb(&promiscuous_rx_cb); |
|
|
|
Serial.println("Promiscuous mode enabled. Monitoring packets..."); |
|
last_channel_hop = millis(); |
|
} |
|
|
|
void loop() { |
|
hop_channel(); |
|
delay(10); |
|
} |