This is my another example of simple and inexpensive WiFi packet analyzer (also known as a WiFi sniffer). The heart of this project is ESP8266 WiFi module which is able to work in a promiscusous mode. This module allows IEEE802.11 network packets capturing for further analyzing. Because of the fact that ESP modules don’t listening on all channels at a time, additional code has been added in the main loop to switch channels in 2s intervals. Presented sniffer requires a callback function that will process all received promiscusous packets. Example callback function displays few basic information like packet channel, length, RSSI or MAC addresses. The code is using ESP-IDF and can be found on GitHub, click here.
Parts Required
- ESP8266 Development Module (for example based on ESP-12x)
Software
This code is written in C and can be compiled using xtensa-lx106-elf-gcc. Don’t know how to start? Please read about building ESP8266 toolchain for Linux.
/** * Copyright (c) 2019, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com> * ESP8266/016 * Example of WiFi sniffer. */ #include <stdint.h> #include <esp/gpio.h> #include "espressif/esp_common.h" #include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" typedef struct { signed rssi: 8; unsigned rate: 4; unsigned is_group: 1; unsigned: 1; unsigned sig_mode: 2; unsigned legacy_length: 12; unsigned damatch0: 1; unsigned damatch1: 1; unsigned bssidmatch0: 1; unsigned bssidmatch1: 1; unsigned MCS: 7; unsigned CWB: 1; unsigned HT_length: 16; unsigned Smoothing: 1; unsigned Not_Sounding: 1; unsigned: 1; unsigned Aggregation: 1; unsigned STBC: 2; unsigned FEC_CODING: 1; unsigned SGI: 1; unsigned rxend_state: 8; unsigned ampdu_cnt: 8; unsigned channel: 4; unsigned: 12; } wifi_pkt_rx_ctrl_t; typedef struct { wifi_pkt_rx_ctrl_t rx_ctrl; uint8_t payload[0]; /* ieee80211 packet buff */ } wifi_promiscuous_pkt_t; typedef struct { unsigned frame_ctrl:16; unsigned duration_id:16; uint8_t addr1[6]; /* receiver address */ uint8_t addr2[6]; /* sender address */ uint8_t addr3[6]; /* filtering address */ unsigned sequence_ctrl:16; uint8_t addr4[6]; /* optional */ } wifi_ieee80211_mac_hdr_t; typedef struct { wifi_ieee80211_mac_hdr_t hdr; uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */ } wifi_ieee80211_packet_t; extern sdk_wifi_promiscuous_cb_t sdk_promiscuous_cb; static void sniffer_init(void); static void sniffer_task(void *prv); static void sniffer_packet_handler(uint8_t *buf, uint16_t len); void user_init(void) { uart_set_baud(0, 115200); sniffer_init(); printf("SDK version:%s\n", sdk_system_get_sdk_version()); xTaskCreate(sniffer_task, "sniffer_task", 1024, NULL, 1, NULL); } void sniffer_init(void) { sdk_wifi_set_opmode(NULL_MODE); // reset AP mode sdk_wifi_set_channel(1); // set start channel sdk_promiscuous_cb = sniffer_packet_handler; // set promiscuous callback sdk_wifi_promiscuous_enable(true); // enable promiscuous mode } void sniffer_task(void *prv) { while (1) { vTaskDelay(2000 / portTICK_PERIOD_MS); sdk_wifi_set_channel(sdk_wifi_get_channel() % 13 + 1); } } void sniffer_packet_handler(uint8_t *buff, uint16_t len) { const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; printf("PACKET LEN=%u CHAN=%02d, RSSI=%02d," " ADDR1=%02x:%02x:%02x:%02x:%02x:%02x," " ADDR2=%02x:%02x:%02x:%02x:%02x:%02x," " ADDR3=%02x:%02x:%02x:%02x:%02x:%02x\n", len, sdk_wifi_get_channel(), (int32_t)ppkt->rx_ctrl.rssi, /* ADDR1 */ hdr->addr1[0],hdr->addr1[1],hdr->addr1[2], hdr->addr1[3],hdr->addr1[4],hdr->addr1[5], /* ADDR2 */ hdr->addr2[0],hdr->addr2[1],hdr->addr2[2], hdr->addr2[3],hdr->addr2[4],hdr->addr2[5], /* ADDR3 */ hdr->addr3[0],hdr->addr3[1],hdr->addr3[2], hdr->addr3[3],hdr->addr3[4],hdr->addr3[5] ); }