1. 程式人生 > >【ESP8266】ESP8266 sniffer使用示例(基於NONOS SDK)

【ESP8266】ESP8266 sniffer使用示例(基於NONOS SDK)

前言

想接觸ESP8266 sniffer也是比較巧合,因為看到國外論壇一篇文章激發了我的興趣,他是使用ESPduino開發環境的,但我習慣於官方SDK開發,就想著能不能找到相關API介面。一查資料,還真的有。就是ESP8266 sniffer。

根據樂鑫官網顯示,2017年05月05日ESP8266 NONOS SDK已經升級到2.1.0版本了。版本都迭代更新到2.1了,發現自己對Sniffer 相關介面還沒有一定的瞭解,所以特此寫下本文記錄,也方便後來者學習參考。

面向的讀者

本文主要面向ESP8266開發者。建議閱讀本文前請確定自己具備以下基礎,否則可能會有看不懂的地方或者使用示例工程達不到預期效果的情況,包括但不限於:

  • 閱讀ESP8266 NONOS SDK原始碼的基礎能力
  • ESP8266開發環境的搭建、燒錄和除錯

提醒:ESP8266 NONOS SDK v2.0編譯出來的二進位制檔案,燒寫地址和以前版本有點不一樣。

開發環境

  • Windows 10 x64
  • ESP8266 IDE 2.0(安信可)
  • NodeMCU(4MB Flash ESP8266)

Sniffer介紹

那麼什麼是snffer呢?sniffer可以翻譯為混雜模式,ESP8266可以進入該模式,接收空中的 IEEE802.11 包。SDK主要提供的介面有:

介面 說明
wifi_promiscuous_enable 開啟混雜模式
wifi_promiscuous_set_mac 設定 sniffer 模式時的 MAC 地址過濾
wifi_set_promiscuous_rx_cb 註冊混雜模式下的接收資料回撥函式,每收到一包資料,都會進入註冊的回撥函式。
wifi_get_channel 獲取通道號
wifi_set_channel 設定通道號,用於混雜模式

更具體的詳細說明可以看文件。

核心程式碼

下面放上核心程式碼,檔案是sniffer.c,主要有兩個初始化:sniffer_initsniffer_init_in_system_init_done

sniffer_init是在user_init裡面呼叫,做一些定時器的初始化,sniffer_init_in_system_init_done是放在ESP8266底層系統初始化後的回撥函式(相關API:system_init_done_cb),如果不放在這裡呼叫可能會導致sniffer功能初始化不成功。

channelHop函式和promisc_cb函式是定時器回撥函式。

channelHop每呼叫一次會改變ESP8266掃描WiFi的channel,可在include/sniffer.h修改CHANNEL_HOP_INTERVAL數值,博主工程預設是3000ms。

promisc_cb函式則可以參考SDK文件的wifi_set_promiscuous_rx_cb API函式說明:「每收到一包資料,都會進入註冊的回撥函式」。該函式就是對這些資料包進行處理,然後打印出Client的MAC和AP的MAC。

//省略……

void ICACHE_FLASH_ATTR
channelHop(void *arg)
{
    // 1 - 13 channel hopping
    uint8 new_channel = wifi_get_channel() % 12 + 1;
    os_printf("** hop to %d **\t    Client MAC\t\t    AP MAC\r\n", new_channel);
    wifi_set_channel(new_channel);
}

//省略……

static void ICACHE_FLASH_ATTR
promisc_cb(uint8_t *buf, uint16_t len)
{
    if (len == 12){
        struct RxControl *sniffer = (struct RxControl*) buf;
    } else if (len == 128) {
        struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf;
    } else {
        struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
        int i=0;
        // Check MACs

        // 如果MAC地址和上一次一樣就返回
        if(0==os_memcmp(temp_mac, &sniffer->buf[4], 6)){
            return;
        }

        // 快取上次的MAC,避免重複列印
        for (i=0; i<6; i++){
            temp_mac[i] = sniffer->buf[i+4];
        }

        #if SNIFFER_TEST
            os_printf("-> %3d: %d", wifi_get_channel(), len);
            printmac(sniffer->buf, 4);
            printmac(sniffer->buf, 10);
            os_printf("\n");
        #endif

        // 判斷client
        for (i=0; i<6; i++) if (sniffer->buf[i+4] != client[i]) return;
        printmac(sniffer->buf, 4);

        os_printf("\r\n");
        os_printf("\trssi:%d\r\n", sniffer->rx_ctrl.rssi);
        os_printf("\tchannel:%d\r\n", sniffer->rx_ctrl.channel);
        os_printf("\trate:%d\r\n", sniffer->rx_ctrl.rate);
        os_printf("\tsig_mode:%d\r\n",sniffer->rx_ctrl.sig_mode);

        // 判斷AP
        //for (i=0; i<6; i++) if (sniffer->buf[i+10] != ap[i]) return;
        //printmac(sniffer->buf, 10);

        //os_timer_disarm(&channelHop_timer);
    }
}

void ICACHE_FLASH_ATTR
sniffer_init(void)
{
    // Promiscuous works only with station mode
    wifi_set_opmode(STATION_MODE);

    //省略……

    os_timer_disarm(&channelHop_timer);
    os_timer_setfn(&channelHop_timer, (os_timer_func_t *) channelHop, NULL);
    os_timer_arm(&channelHop_timer, CHANNEL_HOP_INTERVAL, 1);
}

void ICACHE_FLASH_ATTR
sniffer_init_in_system_init_done(void)
{
    // Set up promiscuous callback
    wifi_set_channel(1);
    wifi_promiscuous_enable(0);
    wifi_set_promiscuous_rx_cb(promisc_cb);
    wifi_promiscuous_enable(1);
}

user_main.c初始化程式碼如下:

void ICACHE_FLASH_ATTR
system_init_done(void)
{
    sniffer_init_in_system_init_done();
}

void ICACHE_FLASH_ATTR
user_init()
{
    uart_init(115200, 115200);
    os_printf("\r\n\r\nSDK version:%s\n", system_get_sdk_version());

    sniffer_init();

    // Continue to 'sniffer_system_init_done'
    system_init_done_cb(system_init_done);
}

另外,如果有時間的話,會考慮分析一下原始檔中WiFi相關的結構體(比如struct RxControl),隨後會更新本文。

示例工程

那麼下面就放上我測試通過的sniffer使用示例工程。看工程學習使用是最快速的方法。使用本工程可以抓取Client和AP的MAC地址。另外博主已經能成功抓到自己手機的MAC了。

示例工程:

本工程效果圖:

效果圖

本文最後編輯時間:2017年7月