ESP8266 嗅探并发送 Mac 地址
ESP8266 Sniffing and sending Mac address
我正在尝试让我的 ESP8266 嗅探附近的设备,然后通过 HTTP 请求发布它们。目的是记录我和室友在家的时间。然后在未来,触发某些任务,例如打开 on/off 灯,无论我们是否在家。我根本不关心 mac 地址的数据包内容。
所以我找到了这个脚本,它打印出附近设备的 mac 地址,由 kalanda 创建:esp8266-sniffer。
以及此 HTTP 发布脚本 ESP8266 http get requests.
我尝试将这两者结合起来,并在回调函数中让 ESP 发送找到的数据,但 ESP 似乎没有建立 wifi 连接。
我尝试使用不同的 WIFI 模式:STATION_MODE、SOFTAP_MODE、STATIONAP_MODE。 None 他们同时处理嗅探和 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);
}
下面是聚合探测请求(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();
}
请记住使用 arduino-esp8266 库的最新版本(实际上是预发布版 - 2.4.0-rc1 或更新版本),因为这些 WiFiEvents 是最近才添加的。
如果您还没有 ArduinoJson 库,可以使用库管理器(Sketch -> Include Library -> Manage Libraries...)下载。
我正在尝试让我的 ESP8266 嗅探附近的设备,然后通过 HTTP 请求发布它们。目的是记录我和室友在家的时间。然后在未来,触发某些任务,例如打开 on/off 灯,无论我们是否在家。我根本不关心 mac 地址的数据包内容。
所以我找到了这个脚本,它打印出附近设备的 mac 地址,由 kalanda 创建:esp8266-sniffer。 以及此 HTTP 发布脚本 ESP8266 http get requests.
我尝试将这两者结合起来,并在回调函数中让 ESP 发送找到的数据,但 ESP 似乎没有建立 wifi 连接。
我尝试使用不同的 WIFI 模式:STATION_MODE、SOFTAP_MODE、STATIONAP_MODE。 None 他们同时处理嗅探和 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);
}
下面是聚合探测请求(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();
}
请记住使用 arduino-esp8266 库的最新版本(实际上是预发布版 - 2.4.0-rc1 或更新版本),因为这些 WiFiEvents 是最近才添加的。
如果您还没有 ArduinoJson 库,可以使用库管理器(Sketch -> Include Library -> Manage Libraries...)下载。