【嵌入式开发】自定义AT指令实现sniffer网络嗅探功能

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mgsweet/article/details/80003551

基础

该功能是在NON-OS SDK下实现的。

Non-OS SDK 是不不基于操作系统的 SDK,提供 IOT_Demo 和 AT 的编译。Non-OS SDK 主要使⽤用定时器和回调函数的方式实现各个功能事件的嵌套,达到特定条件下触发特定功能函数的目的。Non-OS SDK 使用 espconn 接口实现网络操作,用户需要按照 espconn 接口的使用规则进行软件开发。

下面给出官方文档中的SDK使用流程图:

这里写图片描述

这里就不过多阐述如何编译并烧写固件的问题,稍微提两点:

其一就是如果大家要编译example里的文件
记得将对应的文件夹从example中拖到主目录下,比如我写好的固件叫作at那对应应该放的位置就应该是:
这里写图片描述

其二就是关于AT固件的烧写格式(注意文档中用到的是Mbit,32Mbit = 4 MB):
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

自定义AT指令

自定义 AT 指令命名时,使用英文字符,请勿使⽤用其他特殊字符或数字。 AT 基于 ESP8266_NONOS_SDK 编译,ESP8266_NONOS_SDK/example/at中提供了开发者自定 AT指令的示例。乐鑫原本提供的 AT 指令以库文件 libat.a 的形式提供,将包含在编译⽣生成的 AT BIN 固件中。

首先是参照样例程序注册我们的AT指令

extern void at_exeCmdCiupdate(uint8_t id);
at_funcationType at_custom_cmd[] = {
    {"+TEST", 5, at_testCmdTest, at_queryCmdTest, at_setupCmdTest, at_exeCmdTest},
    {"+MACSNIFFER", 11, at_testMacSniffer, NULL, NULL,  at_exeMacSniffer}
};

第一个参数是我们命令的名字,如上图,运行固件后发送AT+MACSNIFFER样式的命令即可执行我们的程序。
第二个参数 11 是命令字符串的长度。
而一条指令可以有4种类型,下面给出官方文档截图:
这里写图片描述

由于我们只需要测试指令和执行指令,所以只在参数3和6里填上对应的函数即可,其余填入NULL

然后需要在user_init(void)里注册我们的AT指令

//注册用户自定义的 AT 指令
at_cmd_array_regist(&at_custom_cmd[0], sizeof(at_custom_cmd)/sizeof(at_custom_cmd[0]));

接着是实现我们的函数,这里只给出执行函数的实现

void ICACHE_FLASH_ATTR
at_exeMacSniffer(uint8_t id)
{
    uint8 buffer[32] = {0};

    os_sprintf(buffer, "\r\n");
    at_port_print(buffer);

    at_getMac();
    //at_response_ok();
}

为什么要有 ICACHE_FLASH_ATTR, 文档如是说:
这里写图片描述

添加了ICACHE_FLASH_ATTR的代码通常比使用IRAM_ATTR标记的代码执行得慢。然而, 像大多数嵌入式平台一样,ESP8266 的 iRAM 空间有限,因此建议一般代码添加 ICACHE_FLASH_ATTR,仅对执行行效率要求高的代码添加 ICACHE_FLASH_ATTR

这里我没让我的函数立刻response ok, 因为是使用回调的机制,我不想在我没给出结果的时候就返回OK,同时我也不想在我运行我的指令的时候,还能输入别的AT指令,所以我还需要接着添加一句代码:at_enter_special_state(),这句代码保证了在我的指令运行期间,其他指令输入时,命令行返回busyat_getMac() 定义如下:

void ICACHE_FLASH_ATTR
at_getMac() 
{
    at_enter_special_state();

    // Promiscuous works only with station mode
    wifi_set_opmode(STATION_MODE);

    #ifdef DEBUG
    uart_init(115200, 115200);
    UART_SetPrintPort(0);
    #endif

    // Scan all aps.
    struct scan_config scanCof;
    os_bzero(&scanCof, sizeof(struct scan_config));
    scanCof.show_hidden = 1;
    wifi_station_scan(&scanCof, wifi_scan_done_cb);
}

其中由于嗅探只能运行在STATION_MODE模式下,所以要用wifi_set_opmode(STATION_MODE)进行设置。

然后我们需要对嗅探的频道进行设置,由于我们其实并不需要嗅探那些WIFI不支持的频道(我们只需要对范围内WIFI所支持的频道进行嗅探),我们需要获取WIFI的频道信息,相应的API定义如下:
这里写图片描述这里写图片描述

依然要用到回调的机制,所以我们继续完成下一个回调函数,该函数主要是对频道进行筛选,然后设置更换频道的定时器,保证扫描总时长为10秒,每扫描到一个mac执行promisc_cb()回调。

void ICACHE_FLASH_ATTR
wifi_scan_done_cb(void *arg, STATUS status) 
{
    queue_i = 0;
    os_memset(temp_mac, 0, 30 * sizeof(uint8_t));

    if (status != OK) {
        os_printf("Err: wifi_scan_done_cb status error.\r\n");
        at_leave_special_state();
        at_response_error();
        return;
    }

    struct bss_info *bss = (struct bss_info *)arg;
    bss = STAILQ_NEXT(bss, next);   //ignore first

    int bss_count = 0;
    while (bss) 
    {
        if (bss->channel != 0)
        {
            channel_check[bss->channel] = 1;
        }
        bss = STAILQ_NEXT(bss, next);
    }

    int i;
    #ifdef DEBUG
    os_printf("Channel check: ");
    #endif

    for (i = 1; i < 14; i++) {
        #ifdef DEBUG
        os_printf("%d ", channel_check[i]);
        #endif

        if (channel_check[i]) bss_count++;
    }

    #ifdef DEBUG
    os_printf("\r\n");
    #endif

    //os_printf("Total bss count: %d\r\n", bss_count);

    wifi_set_channel(1);
    wifi_promiscuous_enable(0);
    wifi_set_promiscuous_rx_cb(promisc_cb);
    wifi_promiscuous_enable(1);

    channel_i = 1;

    uint32_t timeInterval = 10000 / bss_count;

    #ifdef DEBUG
    os_printf("bss_count: %d timeInterval: %d\r\n", bss_count, timeInterval);
    #endif

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

接着完成每检测到一个mac的回调,这里首先需要判断包的长度,然后通过判断报文里的内容判断包是否为重传,重传的不要,包的类型,以及由于我要筛选掉路由器的mac和由ARP协助产生的 ff:ff:ff:ff:ff:ff mac地址,同时使获得的MAC不重复,我做了多重过滤。

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 {
        FrameCtrlPtr frame_ctrl_ptr = NULL;
        frame_ctrl_ptr =
            (FrameCtrlPtr)(buf + sizeof(struct RxControl));
        if (frame_ctrl_ptr->type != FRAME_TYPE_DATA)
        {
            return;
        }

        // Ignore retry package.
        if (frame_ctrl_ptr->retry == 1) return;


        struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
        int i=0;
        // Check MACs

        // AP->Station, get station
        if (frame_ctrl_ptr->fromDS == 1)
        {   
            // ignore ff:ff:ff:ff:ff:ff
            if(0==os_memcmp(noused_mac, &sniffer->buf[4], 6)){
                return;
            }

            if (NULL == macListHead)
            {
                macListHead = (macListNote *)os_malloc(sizeof(macListNote));
                for (i = 0; i < 6; i++)
                {
                    macListHead->mac[i] = sniffer->buf[i+4];
                }
                macListHead->rssiSum = (int)(sniffer->rx_ctrl.rssi);
                macListHead->channel = sniffer->rx_ctrl.channel;
                macListHead->repeatCount = 1;
                macListHead->next = NULL;
            } 
            else 
            {
                macListNote *p = macListHead;
                while(p != NULL) {
                    if (0==os_memcmp(p->mac, &sniffer->buf[4], 6)) 
                    {   
                        p->rssiSum = p->rssiSum + (int)(sniffer->rx_ctrl.rssi);
                        p->repeatCount++;
                        return;
                    }
                    if (p->next == NULL) 
                    {
                        p->next = (macListNote *)os_malloc(sizeof(macListNote));
                        p = p->next;
                        for (i = 0; i < 6; i++)
                        {
                            p->mac[i] = sniffer->buf[i+4];
                        }
                        p->rssiSum = (int)(sniffer->rx_ctrl.rssi);
                        p->channel = sniffer->rx_ctrl.channel;
                        p->repeatCount = 1;
                        p->next = NULL;
                    }
                    p = p->next;
                }
            }
            return;
        }

        // Station->AP, get station
        if (frame_ctrl_ptr->toDS == 1)
        {
            // ignore ff:ff:ff:ff:ff:ff
            if(0==os_memcmp(noused_mac, &sniffer->buf[10], 6)){
                return;
            }

            if (NULL == macListHead)
            {
                macListHead = (macListNote *)os_malloc(sizeof(macListNote));
                for (i = 0; i < 6; i++)
                {
                    macListHead->mac[i] = sniffer->buf[i+10];
                }
                macListHead->rssiSum = (int)(sniffer->rx_ctrl.rssi);
                macListHead->channel = sniffer->rx_ctrl.channel;
                macListHead->repeatCount = 1;
                macListHead->next = NULL;
            } 

            else 
            {
                macListNote *p = macListHead;
                while(p != NULL) {
                    if (0==os_memcmp(p->mac, &sniffer->buf[10], 6))
                    {
                        p->rssiSum = p->rssiSum + (int)(sniffer->rx_ctrl.rssi);
                        p->repeatCount++;
                        return;
                    }
                    if (p->next == NULL) 
                    {
                        p->next = (macListNote *)os_malloc(sizeof(macListNote));
                        p = p->next;
                        for (i = 0; i < 6; i++)
                        {
                            p->mac[i] = sniffer->buf[i+10];
                        }
                        p->rssiSum = (int)(sniffer->rx_ctrl.rssi);
                        p->channel = sniffer->rx_ctrl.channel;
                        p->repeatCount = 1;
                        p->next = NULL;
                    }
                    p = p->next;
                }
            }
            return;
        }
    }
}

由于不能使用STL库进行重复性判断,所以自己实现了个单向链表来判断重复,同时将重复获得的MAC的RSSI进行了求平均的操作,链表结构如下:

struct macListNote {
  uint8_t mac[6];
  int rssiSum;
  unsigned channel:4;
  uint8_t repeatCount;
  struct macListNote *next;
};
typedef struct macListNote macListNote;

当然,关于收到包的结构体,参考ESP8266 技术参考里的 Sniffer应用设计说明 ,我们需要定义如下结构体:

struct RxControl {
    signed rssi:8;              // signal intensity of packet
    unsigned rate:4;
    unsigned is_group:1;
    unsigned:1;
    unsigned sig_mode:2;        // 0:is 11n packet; 1:is not 11n packet;
    unsigned legacy_length:12;  // if not 11n packet, shows length of packet.
    unsigned damatch0:1;
    unsigned damatch1:1;
    unsigned bssidmatch0:1;
    unsigned bssidmatch1:1;
    unsigned MCS:7;         // if is 11n packet, shows the modulation
                            // and code used (range from 0 to 76)
    unsigned CWB:1;         // if is 11n packet, shows if is HT40 packet or not
    unsigned HT_length:16;  // if is 11n packet, shows length of packet.
    unsigned Smoothing:1;
    unsigned Not_Sounding:1;
    unsigned:1;
    unsigned Aggregation:1;
    unsigned STBC:2;
    unsigned FEC_CODING:1;  // if is 11n packet, shows if is LDPC packet or not.
    unsigned SGI:1;
    unsigned rxend_state:8;
    unsigned ampdu_cnt:8;
    unsigned channel:4;     // which channel this packet in.
    unsigned:12;
};

struct FrameCtrl {
  uint8 protocol:2;
  uint8 type:2;
  uint8 subtype:4;

  uint8 toDS:1;
  uint8 fromDS:1;
  uint8 moreFlag:1;
  uint8 retry:1;
  uint8 pwrMgmt:1;
  uint8 moreData:1;
  uint8 protectedframe:1;
  uint8 order:1;
};

struct LenSeq {
    uint16_t length;
    uint16_t seq;
    uint8_t  address3[6];
};

struct sniffer_buf {
    struct RxControl rx_ctrl;
    uint8_t buf[36];
    uint16_t cnt;
    struct LenSeq lenseq[1];
};

struct sniffer_buf2{
    struct RxControl rx_ctrl;
    uint8_t buf[112];
    uint16_t cnt;
    uint16_t len;
};

//record wifi AP's info
struct ApInfo
{
  char ssid[33];
  uint8_t bssid[6];//MAC address
  uint8_t channel;
  struct ApInfo *next;  
};

就此,我们的sniffer网络嗅探AT指令终于完成了,而关于SNIFFER的DEMO,官方说是要申请,但其实大家可以到 https://github.com/AngelLiang/ESP8266-Demos/tree/master/6_3-sniffer_app 这个库里进行学习,更多知识请自行查看官方提供的各种文档。

展开阅读全文

没有更多推荐了,返回首页