ESP8266抓包并发送Mac地址

7
我正在尝试让我的ESP8266嗅探附近的设备,然后通过HTTP请求将它们发布出来。目的是记录我和室友在家的时间。然后在未来,触发某些任务,如根据我们是否在家打开/关闭灯光。我不关心数据包内容,只关心MAC地址。
到目前为止,我找到了kalanda创建的脚本,可以打印出附近设备的MAC地址:esp8266-sniffer。以及这个HTTP发布脚本ESP8266 http get requests
我试图将这两个组合起来,在回调函数中使ESP发送找到的数据,但似乎ESP没有建立WiFi连接。
我尝试使用不同的WIFI模式:STATION_MODE、SOFTAP_MODE、STATIONAP_MODE。但它们都不能同时进行嗅探和HTTP请求。我知道STATIONAP_MODE存在一些缺陷。我发现它必须同时切换,但不幸的是我不是ESP专家,不知道该怎么做。
以下是我的代码(对我的任何垃圾编码表示抱歉):
#include <ESP8266WiFi.h>       // added this
#include <ESP8266HTTPClient.h> // added this

const char* ssid     = "**********";  // Wifi SSID
const char* password = "**********";  // Wifi Password
String main_url      = "http://*********.php?"; // Website url to post the information
String temp_url      = "";                      // Url with information

extern "C" {
  #include <user_interface.h>
}

#define DATA_LENGTH           112

#define TYPE_MANAGEMENT       0x00
#define TYPE_CONTROL          0x01
#define TYPE_DATA             0x02
#define SUBTYPE_PROBE_REQUEST 0x04


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 SnifferPacket{
    struct RxControl rx_ctrl;
    uint8_t data[DATA_LENGTH];
    uint16_t cnt;
    uint16_t len;
};

static void showMetadata(SnifferPacket *snifferPacket) {

  unsigned int frameControl = ((unsigned int)snifferPacket->data[1] << 8) + snifferPacket->data[0];

  uint8_t version      = (frameControl & 0b0000000000000011) >> 0;
  uint8_t frameType    = (frameControl & 0b0000000000001100) >> 2;
  uint8_t frameSubType = (frameControl & 0b0000000011110000) >> 4;
  uint8_t toDS         = (frameControl & 0b0000000100000000) >> 8;
  uint8_t fromDS       = (frameControl & 0b0000001000000000) >> 9;

  // Only look for probe request packets
  if (frameType != TYPE_MANAGEMENT ||
      frameSubType != SUBTYPE_PROBE_REQUEST)
        return;

  Serial.print("RSSI: ");
  Serial.print(snifferPacket->rx_ctrl.rssi, DEC);

  Serial.print(" Ch: ");
  Serial.print(wifi_get_channel());

  char addr[] = "00:00:00:00:00:00";
  getMAC(addr, snifferPacket->data, 10);
  Serial.print(" Peer MAC: ");
  Serial.print(addr);

  uint8_t SSID_length = snifferPacket->data[25];
  Serial.print(" SSID: ");
  printDataSpan(26, SSID_length, snifferPacket->data);



  Serial.println();
  if (WiFi.status() == WL_CONNECTED) //Check WiFi connection status 
  { 

    HTTPClient http;  //Declare an object of class HTTPClient
    temp_url = main_url;
    temp_url = temp_url + "mac=30:a8:db:96:a4:75";
    temp_url = temp_url + "&rssi=-90";
    temp_url = temp_url + "&ssid=none";
    http.begin(temp_url);  //Specify request destination
    int httpCode = http.GET();                                                                  //Send the request
    temp_url = "";

    if (httpCode > 0) 
    { //Check the returning code

      String payload = http.getString();   //Get the request response payload
      Serial.println(payload);                     //Print the response payload

    }

    http.end();   //Close connection

  }
  else
  {
    Serial.println("Wifi connection failed"); //Prints out this
  }

}

/**
 * Callback for promiscuous mode
 */
static void ICACHE_FLASH_ATTR sniffer_callback(uint8_t *buffer, uint16_t length) {
  struct SnifferPacket *snifferPacket = (struct SnifferPacket*) buffer;
  showMetadata(snifferPacket);
}

static void printDataSpan(uint16_t start, uint16_t size, uint8_t* data) {
  for(uint16_t i = start; i < DATA_LENGTH && i < start+size; i++) {
    Serial.write(data[i]);    
  }
}

static void getMAC(char *addr, uint8_t* data, uint16_t offset) {
  sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", data[offset+0], data[offset+1], data[offset+2], data[offset+3], data[offset+4], data[offset+5]);
}

#define CHANNEL_HOP_INTERVAL_MS   1000
static os_timer_t channelHop_timer;

/**
 * Callback for channel hoping
 */
void channelHop()
{
  // hoping channels 1-14
  uint8 new_channel = wifi_get_channel() + 1;
  if (new_channel > 14)
    new_channel = 1;
  wifi_set_channel(new_channel);
}

#define DISABLE 0
#define ENABLE  1

void setup() {
  // set the WiFi chip to "promiscuous" mode aka monitor mode
  Serial.begin(115200);



  delay(10);
  wifi_set_opmode(STATION_MODE);
  wifi_set_channel(1);
  wifi_promiscuous_enable(DISABLE);
  delay(10);
  wifi_set_promiscuous_rx_cb(sniffer_callback);
  delay(10);
  wifi_promiscuous_enable(ENABLE);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {

    delay(1000);
    Serial.print("Connecting..");

  }
  Serial.println("");
  Serial.println("Connected..");

  // setup the channel hoping callback timer
  os_timer_disarm(&channelHop_timer);
  os_timer_setfn(&channelHop_timer, (os_timer_func_t *) channelHop, NULL);
  os_timer_arm(&channelHop_timer, CHANNEL_HOP_INTERVAL_MS, 1);
}

void loop() {
  delay(10);  
}
1个回答

9

这是一个聚合探针请求(MAC地址和RSSI)的代码,它会在3秒内将它们使用JSON格式发送到指定服务器的端点(WIFI_AP_STA模式):

#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266HTTPClient.h>
#include <vector>

const char* apSsid     = "ap-ssid";
const char* apPassword = "ap-password";
const char* clientSsid     = "client-ssid";
const char* clientPassword = "client-password";

HTTPClient http;

WiFiEventHandler probeRequestPrintHandler;

String macToString(const unsigned char* mac) {
  char buf[20];
  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}

std::vector<WiFiEventSoftAPModeProbeRequestReceived> myList;

void onProbeRequestPrint(const WiFiEventSoftAPModeProbeRequestReceived& evt) {
  myList.push_back(evt);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Hello!");

  // Don't save WiFi configuration in flash - optional
  WiFi.persistent(false);

  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(apSsid, apPassword);
  WiFi.begin(clientSsid, clientPassword);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("");
  probeRequestPrintHandler = WiFi.onSoftAPModeProbeRequestReceived(&onProbeRequestPrint);
}

void loop() {
  delay(3000);
  String json = "";
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  JsonArray& probes = root.createNestedArray("probes");
  for(WiFiEventSoftAPModeProbeRequestReceived w : myList){
    JsonObject& probe = probes.createNestedObject();
    probe["address"] = macToString(w.mac);
    probe["rssi"] = w.rssi;
  }
  myList.clear();
  root.printTo(json);
  Serial.println("json:" + json);

  http.begin("http://example.com/api/v1/probe");
  http.addHeader("Content-Type", "application/json");
  http.POST(json);
  http.end();
}

请记得使用最新版本(实际上是预发布版本 - 2.4.0-rc1或更高版本)的arduino-esp8266库,因为这些WiFiEvents是最近添加的。
如果您还没有ArduinoJson库,请使用库管理器(Sketch -> 包含库 -> 管理库...)下载。

它运行得非常好,非常感谢@Defozo!不过我有一些问题。 首先,在你的例子中,变量clientSsid和clientPassword是用来干什么的?我将它们设置为我们wifi网络的ssid和密码,不确定是否应该这样做? 此外,这个代码是检查一个特定的信道还是在站点模式下检查所有可能的信道?最后,是否可以包括SSID? 如果有人对Web服务器代码以及数据库表感兴趣,请给我打电话。 - Gheotic
1
为了接收探测请求,ESP8266应处于AP模式(需要arduino-esp8266库),因此apSsidapPassword是用于访问点的,即esp8266正在创建的访问点。所以实际上,我认为它们可以是随机的 - 可能不是将它们设置为与您的网络ssid / pass相同的最佳方法,因为某些设备可能会连接到您的esp8266而不是您的网络。 - Defozo
1
据我所知,arduino-esp8266库API仅提供MAC和RSSI变量,因此为了接收设备正在寻找的网络的SSID,您需要使用SDK调用修改代码。 - Defozo
哦,那么我们能够连接到我们的“wifi”网络,但是没有互联网连接就有意义了…… - Gheotic
好的,我觉得我现在还不够聪明能够做到那一点。希望他们以后会添加这个功能。 - Gheotic
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接