mirror of
https://github.com/justcallmekoko/ESP32Marauder.git
synced 2026-07-30 23:50:26 -07:00
Merge pull request #1033 from justcallmekoko/develop
Add MAC tracking backend
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
#include <LinkedList.h>
|
||||
#include <SPI.h>
|
||||
//#include <lvgl.h>
|
||||
#include <Ticker.h>
|
||||
//#include <Ticker.h>
|
||||
#include "SPIFFS.h"
|
||||
#include "Assets.h"
|
||||
|
||||
|
||||
@@ -518,6 +518,9 @@ void GpsInterface::setGPSInfo() {
|
||||
|
||||
this->datetime = this->dt_string_from_gps();
|
||||
|
||||
this->lat_int = nmea.getLatitude();
|
||||
this->lon_int = nmea.getLongitude();
|
||||
|
||||
this->lat = String((float)nmea.getLatitude()/1000000, 7);
|
||||
this->lon = String((float)nmea.getLongitude()/1000000, 7);
|
||||
long alt = 0;
|
||||
@@ -543,6 +546,14 @@ String GpsInterface::getLon() {
|
||||
return this->lon;
|
||||
}
|
||||
|
||||
int32_t GpsInterface::getLatInt() {
|
||||
return this->lat_int;
|
||||
}
|
||||
|
||||
int32_t GpsInterface::getLonInt() {
|
||||
return this->lon_int;
|
||||
}
|
||||
|
||||
float GpsInterface::getAlt() {
|
||||
return this->altf;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ class GpsInterface {
|
||||
bool getGpsModuleStatus();
|
||||
String getLat();
|
||||
String getLon();
|
||||
int32_t getLatInt();
|
||||
int32_t getLonInt();
|
||||
float getAlt();
|
||||
float getAccuracy();
|
||||
String getDatetime();
|
||||
@@ -93,6 +95,8 @@ class GpsInterface {
|
||||
String notparsed_nmea_sentence = "";
|
||||
String lat = "";
|
||||
String lon = "";
|
||||
int32_t lat_int = 0;
|
||||
int32_t lon_int = 0;
|
||||
float altf = 0.0;
|
||||
float accuracy = 0.0;
|
||||
String datetime = "";
|
||||
|
||||
@@ -203,6 +203,7 @@ void MenuFunctions::main(uint32_t currentTime)
|
||||
{
|
||||
// Stop the current scan
|
||||
if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_DETECT_FOLLOW) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
|
||||
@@ -300,6 +301,7 @@ void MenuFunctions::main(uint32_t currentTime)
|
||||
{
|
||||
// Stop the current scan
|
||||
if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_DETECT_FOLLOW) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
|
||||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION) ||
|
||||
@@ -1669,6 +1671,11 @@ void MenuFunctions::RunSetup()
|
||||
this->drawStatusBar();
|
||||
wifi_scan_obj.StartScan(WIFI_SCAN_SIG_STREN, TFT_CYAN);
|
||||
});
|
||||
this->addNodes(&wifiSnifferMenu, "MAC Monitor", TFTMAGENTA, NULL, SCANNERS, [this]() {
|
||||
display_obj.clearScreen();
|
||||
this->drawStatusBar();
|
||||
wifi_scan_obj.StartScan(WIFI_SCAN_DETECT_FOLLOW, TFT_MAGENTA);
|
||||
});
|
||||
|
||||
// Build Wardriving menu
|
||||
#ifdef HAS_GPS
|
||||
|
||||
@@ -193,7 +193,7 @@ class MenuFunctions
|
||||
|
||||
Menu evilPortalMenu;
|
||||
|
||||
static void lv_tick_handler();
|
||||
//static void lv_tick_handler();
|
||||
|
||||
// Menu icons
|
||||
|
||||
@@ -260,7 +260,7 @@ class MenuFunctions
|
||||
Menu infoMenu;
|
||||
Menu apInfoMenu;
|
||||
|
||||
Ticker tick;
|
||||
//Ticker tick;
|
||||
|
||||
uint16_t x = -1, y = -1;
|
||||
boolean pressed = false;
|
||||
|
||||
+404
-5
@@ -6,6 +6,9 @@
|
||||
struct mac_addr* mac_history = nullptr;
|
||||
#endif
|
||||
|
||||
MacEntry WiFiScan::mac_entries[mac_history_len];
|
||||
uint8_t WiFiScan::mac_entry_state[mac_history_len];
|
||||
|
||||
int num_beacon = 0;
|
||||
int num_deauth = 0;
|
||||
int num_probe = 0;
|
||||
@@ -1739,6 +1742,9 @@ void WiFiScan::RunSetup() {
|
||||
mac_history = (struct mac_addr*) ps_malloc(mac_history_len * sizeof(struct mac_addr));
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < mac_history_len; i++)
|
||||
mac_entry_state[i] = 0;
|
||||
|
||||
#ifdef HAS_BT
|
||||
watch_models = new WatchModel[26] {
|
||||
{0x1A, "Fallback Watch"},
|
||||
@@ -2099,6 +2105,8 @@ void WiFiScan::StartScan(uint8_t scan_mode, uint16_t color)
|
||||
StopScan(scan_mode);
|
||||
else if (scan_mode == WIFI_SCAN_PROBE)
|
||||
RunProbeScan(scan_mode, color);
|
||||
else if (scan_mode == WIFI_SCAN_DETECT_FOLLOW)
|
||||
RunProbeScan(scan_mode, color);
|
||||
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE)
|
||||
RunProbeScan(scan_mode, color);
|
||||
else if (scan_mode == WIFI_SCAN_EVIL_PORTAL)
|
||||
@@ -2455,6 +2463,7 @@ void WiFiScan::StopScan(uint8_t scan_mode)
|
||||
(currentScanMode == WIFI_CONNECTED) ||
|
||||
(currentScanMode == BT_SCAN_FLOCK) ||
|
||||
(currentScanMode == BT_SCAN_FLOCK_WARDRIVE) ||
|
||||
(currentScanMode == WIFI_SCAN_DETECT_FOLLOW) ||
|
||||
(currentScanMode == LV_JOIN_WIFI) ||
|
||||
(this->wifi_initialized))
|
||||
{
|
||||
@@ -2608,6 +2617,16 @@ bool WiFiScan::mac_cmp(struct mac_addr addr1, struct mac_addr addr2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiScan::mac_cmp(uint8_t addr1[6], uint8_t addr2[6]) {
|
||||
//Return true if 2 mac_addr structs are equal.
|
||||
for (int y = 0; y < 6 ; y++) {
|
||||
if (addr1[y] != addr2[y]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAS_BT
|
||||
void WiFiScan::copyNimbleMac(const BLEAddress &addr, unsigned char out[6]) {
|
||||
#ifndef HAS_DUAL_BAND
|
||||
@@ -2622,7 +2641,7 @@ bool WiFiScan::mac_cmp(struct mac_addr addr1, struct mac_addr addr2) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WiFiScan::seen_mac(unsigned char* mac) {
|
||||
bool WiFiScan::seen_mac(unsigned char* mac, bool simple) {
|
||||
//Return true if this MAC address is in the recently seen array.
|
||||
|
||||
struct mac_addr tmp;
|
||||
@@ -2630,6 +2649,7 @@ bool WiFiScan::seen_mac(unsigned char* mac) {
|
||||
tmp.bytes[x] = mac[x];
|
||||
}
|
||||
|
||||
|
||||
for (int x = 0; x < mac_history_len; x++) {
|
||||
if (this->mac_cmp(tmp, mac_history[x])) {
|
||||
return true;
|
||||
@@ -2638,6 +2658,303 @@ bool WiFiScan::seen_mac(unsigned char* mac) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t WiFiScan::seen_mac_int(unsigned char* mac, bool simple) {
|
||||
//Return true if this MAC address is in the recently seen array.
|
||||
|
||||
uint8_t tmp[6];
|
||||
for (int x = 0; x < 6 ; x++) {
|
||||
tmp[x] = mac[x];
|
||||
}
|
||||
|
||||
for (int x = 0; x < mac_history_len; x++) {
|
||||
if (this->mac_cmp(tmp, mac_entries[x].mac)) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline uint32_t WiFiScan::hash_mac(const uint8_t mac[6]) {
|
||||
uint32_t hash = 2166136261u; // FNV offset basis
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
hash ^= mac[i];
|
||||
hash *= 16777619u; // FNV prime
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int WiFiScan::update_mac_entry(const uint8_t mac[6]) {
|
||||
const uint32_t now_ms = millis();
|
||||
const uint32_t start_idx = hash_mac(mac) & (mac_history_len - 1);
|
||||
|
||||
int32_t first_tombstone = -1;
|
||||
|
||||
for (uint32_t probe = 0; probe < mac_history_len; probe++) {
|
||||
const uint32_t idx = (start_idx + probe) & (mac_history_len - 1);
|
||||
|
||||
switch (mac_entry_state[idx]) {
|
||||
|
||||
case EMPTY_ENTRY:
|
||||
// Insert new entry (prefer earlier tombstone if found)
|
||||
if (first_tombstone >= 0) {
|
||||
insert_mac_entry(first_tombstone, mac, now_ms);
|
||||
} else {
|
||||
insert_mac_entry(idx, mac, now_ms);
|
||||
}
|
||||
return EMPTY_ENTRY;
|
||||
|
||||
case TOMBSTONE_ENTRY:
|
||||
// Remember first tombstone for possible reuse
|
||||
if (first_tombstone < 0) {
|
||||
first_tombstone = idx;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALID_ENTRY:
|
||||
// Check for MAC match
|
||||
if (memcmp(mac_entries[idx].mac, mac, 6) == 0) {
|
||||
mac_entries[idx].last_seen_ms = now_ms;
|
||||
|
||||
#ifdef HAS_GPS
|
||||
mac_entries[idx].last_lat_e6 = gps_obj.getLatInt();
|
||||
mac_entries[idx].last_lon_e6 = gps_obj.getLonInt();
|
||||
#endif
|
||||
|
||||
if (mac_entries[idx].frame_count < UINT16_MAX) {
|
||||
mac_entries[idx].frame_count++;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Table full: evict something (simple policy: overwrite first tombstone or oldest)
|
||||
evict_and_insert(mac, now_ms);
|
||||
|
||||
return TOMBSTONE_ENTRY;
|
||||
}
|
||||
|
||||
inline void WiFiScan::insert_mac_entry(uint32_t idx, const uint8_t mac[6], uint32_t now_ms) {
|
||||
memcpy(mac_entries[idx].mac, mac, 6);
|
||||
mac_entries[idx].last_seen_ms = now_ms;
|
||||
mac_entries[idx].frame_count = 1;
|
||||
#ifdef HAS_GPS
|
||||
mac_entries[idx].first_lat_e6 = gps_obj.getLatInt();
|
||||
mac_entries[idx].first_lon_e6 = gps_obj.getLonInt();
|
||||
mac_entries[idx].last_lat_e6 = gps_obj.getLatInt();
|
||||
mac_entries[idx].last_lon_e6 = gps_obj.getLonInt();
|
||||
#else
|
||||
mac_entries[idx].first_lat_e6 = 0;
|
||||
mac_entries[idx].first_lon_e6 = 0;
|
||||
mac_entries[idx].last_lat_e6 = 0;
|
||||
mac_entries[idx].last_lon_e6 = 0;
|
||||
#endif
|
||||
mac_entries[idx].following = false;
|
||||
mac_entries[idx].dloc = 0;
|
||||
mac_entry_state[idx] = VALID_ENTRY;
|
||||
}
|
||||
|
||||
void WiFiScan::evict_and_insert(const uint8_t mac[6], uint32_t now_ms) {
|
||||
constexpr uint32_t EVICT_AGE_MS = TRACK_EVICT_SEC * 1000UL;
|
||||
|
||||
// 1) Safety: if any tombstone exists, reuse it.
|
||||
for (uint32_t i = 0; i < mac_history_len; i++) {
|
||||
if (mac_entry_state[i] == TOMBSTONE_ENTRY) {
|
||||
insert_mac_entry(i, mac, now_ms);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Find a VALID entry older than EVICT_AGE_MS (prefer oldest among those).
|
||||
int32_t best_old_idx = -1;
|
||||
uint32_t best_old_age = 0;
|
||||
|
||||
// 3) Fallback: track absolute oldest VALID entry in case none exceed TRACK_EVICT_SEC.
|
||||
int32_t oldest_idx = -1;
|
||||
uint32_t oldest_age = 0;
|
||||
|
||||
for (uint32_t i = 0; i < mac_history_len; i++) {
|
||||
if (mac_entry_state[i] != VALID_ENTRY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t age = (uint32_t)(now_ms - mac_entries[i].last_seen_ms);
|
||||
|
||||
// Track absolute oldest
|
||||
if (oldest_idx < 0 || age > oldest_age) {
|
||||
oldest_idx = (int32_t)i;
|
||||
oldest_age = age;
|
||||
}
|
||||
|
||||
// Track best eviction candidate over threshold
|
||||
if (age > EVICT_AGE_MS) {
|
||||
if (best_old_idx < 0 || age > best_old_age) {
|
||||
best_old_idx = (int32_t)i;
|
||||
best_old_age = age;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Choose eviction target
|
||||
int32_t victim = (best_old_idx >= 0) ? best_old_idx : oldest_idx;
|
||||
|
||||
if (victim >= 0) {
|
||||
// Optional: mark tombstone first (not strictly required if overwriting,
|
||||
// but keeps state transitions explicit).
|
||||
mac_entry_state[victim] = TOMBSTONE_ENTRY;
|
||||
|
||||
// Overwrite with new entry
|
||||
insert_mac_entry((uint32_t)victim, mac, now_ms);
|
||||
Serial.println(macToString(mac_entries[victim].mac) + " expired");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got here, something is inconsistent (no valid entries but "full" insert path).
|
||||
// Just insert at 0.
|
||||
insert_mac_entry(0, mac, now_ms);
|
||||
}
|
||||
|
||||
static inline uint32_t age_ms(uint32_t now_ms, uint32_t last_seen_ms) {
|
||||
return (uint32_t)(now_ms - last_seen_ms); // wrap-safe
|
||||
}
|
||||
|
||||
static inline int32_t iabs32(int32_t v) {
|
||||
return (v < 0) ? -v : v;
|
||||
}
|
||||
|
||||
// Uses e6 degrees. No meters conversion at runtime.
|
||||
// Writes computed location delta (e6 degrees) into out_dloc if provided.
|
||||
static inline bool is_following_candidate_light(
|
||||
const MacEntry& e,
|
||||
uint32_t now_ms,
|
||||
int32_t* out_dloc = nullptr
|
||||
) {
|
||||
const bool has_first = !(e.first_lat_e6 == 0 && e.first_lon_e6 == 0);
|
||||
const bool has_last = !(e.last_lat_e6 == 0 && e.last_lon_e6 == 0);
|
||||
if (!has_first || !has_last) {
|
||||
if (out_dloc)
|
||||
*out_dloc = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional freshness limit (avoid super old "following" marks)
|
||||
const uint32_t MAX_AGE_MS = 10UL * 60UL * 1000UL; // 10 minutes
|
||||
if (age_ms(now_ms, e.last_seen_ms) > MAX_AGE_MS) {
|
||||
if (out_dloc)
|
||||
*out_dloc = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Movement threshold:
|
||||
// meters are e6 degrees = meters * 9
|
||||
const int32_t THRESH_E6 = (int32_t)(75 * 9); // ~75 m to-do: needs tuning
|
||||
|
||||
int32_t dlat = iabs32(e.last_lat_e6 - e.first_lat_e6);
|
||||
int32_t dlon = iabs32(e.last_lon_e6 - e.first_lon_e6);
|
||||
|
||||
// Rough longitude scaling for mid-latitudes (~0.75)
|
||||
dlon = (dlon * 3) / 4;
|
||||
|
||||
// Cheap distance proxy
|
||||
int32_t d = (dlat > dlon) ? dlat : dlon;
|
||||
|
||||
if (out_dloc) *out_dloc = d;
|
||||
|
||||
return d >= THRESH_E6;
|
||||
}
|
||||
|
||||
// Returns how many entries were written to out_top10 (0..10)
|
||||
uint8_t WiFiScan::build_top10_for_ui(MacEntry* out_top10, MacSortMode mode) {
|
||||
if (!out_top10) return 0;
|
||||
|
||||
const uint32_t now_ms = millis();
|
||||
|
||||
int32_t top_idx[10];
|
||||
uint8_t top_count = 0;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
top_idx[i] = -1;
|
||||
|
||||
auto better = [&](uint32_t a_idx, uint32_t b_idx) -> bool {
|
||||
const MacEntry& A = mac_entries[a_idx];
|
||||
const MacEntry& B = mac_entries[b_idx];
|
||||
|
||||
const bool A_follow = is_following_candidate_light(A, now_ms);
|
||||
const bool B_follow = is_following_candidate_light(B, now_ms);
|
||||
|
||||
// Following entries always rank ahead of non-following
|
||||
if (A_follow != B_follow)
|
||||
return A_follow && !B_follow;
|
||||
|
||||
// Original sort rules
|
||||
if (mode == MacSortMode::MOST_FRAMES) {
|
||||
if (A.frame_count != B.frame_count)
|
||||
return A.frame_count > B.frame_count;
|
||||
|
||||
return age_ms(now_ms, A.last_seen_ms) < age_ms(now_ms, B.last_seen_ms);
|
||||
} else {
|
||||
const uint32_t ageA = age_ms(now_ms, A.last_seen_ms);
|
||||
const uint32_t ageB = age_ms(now_ms, B.last_seen_ms);
|
||||
if (ageA != ageB)
|
||||
return ageA < ageB;
|
||||
|
||||
return A.frame_count > B.frame_count;
|
||||
}
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < mac_history_len; i++) {
|
||||
if (mac_entry_state[i] != VALID_ENTRY)
|
||||
continue;
|
||||
|
||||
if (top_count < 10) {
|
||||
int pos = (int)top_count;
|
||||
while (pos > 0 && top_idx[pos - 1] >= 0 && better(i, (uint32_t)top_idx[pos - 1])) {
|
||||
top_idx[pos] = top_idx[pos - 1];
|
||||
pos--;
|
||||
}
|
||||
top_idx[pos] = (int32_t)i;
|
||||
top_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t worst_idx = top_idx[9];
|
||||
if (worst_idx < 0)
|
||||
continue;
|
||||
|
||||
if (better(i, (uint32_t)worst_idx)) {
|
||||
int pos = 9;
|
||||
while (pos > 0 && top_idx[pos - 1] >= 0 && better(i, (uint32_t)top_idx[pos - 1])) {
|
||||
top_idx[pos] = top_idx[pos - 1];
|
||||
pos--;
|
||||
}
|
||||
top_idx[pos] = (int32_t)i;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t k = 0; k < top_count; k++) {
|
||||
const int32_t src = top_idx[k];
|
||||
if (src >= 0) {
|
||||
int32_t dloc = 0;
|
||||
out_top10[k] = mac_entries[(uint32_t)src];
|
||||
out_top10[k].following = is_following_candidate_light(out_top10[k], now_ms, &dloc);
|
||||
out_top10[k].dloc = dloc;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t k = top_count; k < 10; k++) {
|
||||
memset(&out_top10[k], 0, sizeof(MacEntry));
|
||||
}
|
||||
|
||||
return top_count;
|
||||
}
|
||||
|
||||
|
||||
void WiFiScan::save_mac(unsigned char* mac) {
|
||||
//Save a MAC address into the recently seen array.
|
||||
if (this->mac_history_cursor >= mac_history_len) {
|
||||
@@ -2793,7 +3110,8 @@ void WiFiScan::RunPingScan(uint8_t scan_mode, uint16_t color)
|
||||
display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
|
||||
#endif
|
||||
this->current_scan_ip = this->gateway;
|
||||
Serial.println("Cleared IPs: " + (String)this->clearIPs());
|
||||
Serial.print(F("Cleared IPs: "));
|
||||
Serial.println((String)this->clearIPs());
|
||||
if (scan_mode == WIFI_PING_SCAN)
|
||||
Serial.println(F("Starting Ping Scan with..."));
|
||||
else if (scan_mode == WIFI_ARP_SCAN)
|
||||
@@ -3387,7 +3705,8 @@ void WiFiScan::setMac() {
|
||||
((currentWiFiMode == WIFI_MODE_AP) || (currentWiFiMode == WIFI_MODE_APSTA) || (currentWiFiMode == WIFI_MODE_NULL)))
|
||||
Serial.printf("Failed to set AP MAC: %s | 0x%X\n", macToString(this->ap_mac), result);
|
||||
else if ((currentWiFiMode == WIFI_MODE_AP) || (currentWiFiMode == WIFI_MODE_APSTA) || (currentWiFiMode == WIFI_MODE_NULL))
|
||||
Serial.println("Successfully set AP MAC: " + macToString(this->ap_mac));
|
||||
Serial.print(F("Successfully set AP MAC: "));
|
||||
Serial.println(macToString(this->ap_mac));
|
||||
|
||||
// Do the station
|
||||
result = esp_wifi_set_mac(WIFI_IF_STA, this->sta_mac);
|
||||
@@ -3395,7 +3714,8 @@ void WiFiScan::setMac() {
|
||||
((currentWiFiMode == WIFI_MODE_STA) || (currentWiFiMode == WIFI_MODE_APSTA)))
|
||||
Serial.printf("Failed to set STA MAC: %s | 0x%X\n", macToString(this->sta_mac), result);
|
||||
else if ((currentWiFiMode == WIFI_MODE_STA) || (currentWiFiMode == WIFI_MODE_APSTA))
|
||||
Serial.println("Successfully set STA MAC: " + macToString(this->sta_mac));
|
||||
Serial.print(F("Successfully set STA MAC: "));
|
||||
Serial.println(macToString(this->sta_mac));
|
||||
}
|
||||
|
||||
void WiFiScan::RunSetMac(uint8_t * mac, bool ap) {
|
||||
@@ -4870,8 +5190,10 @@ void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color)
|
||||
display_obj.tft.setTextColor(TFT_BLACK, color);
|
||||
#ifdef HAS_FULL_SCREEN
|
||||
display_obj.tft.fillRect(0,16,TFT_WIDTH,16, color);
|
||||
if (scan_mode != BT_SCAN_FLOCK)
|
||||
if (scan_mode == WIFI_SCAN_PROBE)
|
||||
display_obj.tft.drawCentreString(text_table4[40],TFT_WIDTH / 2,16,2);
|
||||
else if (scan_mode == WIFI_SCAN_DETECT_FOLLOW)
|
||||
display_obj.tft.drawCentreString("MAC Monitor",TFT_WIDTH / 2,16,2);
|
||||
else {
|
||||
Serial.println(F("Starting WiFi sniff for Flock..."));
|
||||
display_obj.tft.drawCentreString("Flock Sniff",TFT_WIDTH / 2,16,2);
|
||||
@@ -7271,6 +7593,34 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (wifi_scan_obj.currentScanMode == WIFI_SCAN_DETECT_FOLLOW) {
|
||||
|
||||
// Skip if is a management frame that isn't probe request
|
||||
/*if (type == WIFI_PKT_MGMT) {
|
||||
if (snifferPacket->payload[0] != 0x40)
|
||||
return;
|
||||
}*/
|
||||
|
||||
char addr[] = "00:00:00:00:00:00";
|
||||
getMAC(addr, snifferPacket->payload, 10);
|
||||
|
||||
int frame_check = wifi_scan_obj.update_mac_entry(src_addr);
|
||||
|
||||
/*Serial.print(addr);
|
||||
|
||||
if (frame_check == EMPTY_ENTRY) {
|
||||
Serial.println(" Added to table.");
|
||||
}
|
||||
else if (frame_check == VALID_ENTRY) {
|
||||
Serial.println(" Updated in table");
|
||||
}
|
||||
else if (frame_check == TOMBSTONE_ENTRY) {
|
||||
Serial.println(" Evicted");
|
||||
}
|
||||
else {
|
||||
Serial.println(" Frames: " + (String)mac_entries[frame_check].frame_count + " Last Seen: " + (String)(millis() - mac_entries[frame_check].last_seen_ms));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiScan::stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
|
||||
@@ -10166,6 +10516,44 @@ void WiFiScan::portScan(uint8_t scan_mode, uint16_t targ_port) {
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiScan::updateTrackerUI() {
|
||||
MacEntry ui_list[10];
|
||||
uint8_t n = this->build_top10_for_ui(ui_list, MacSortMode::MOST_FRAMES);
|
||||
|
||||
#ifdef HAS_SCREEN
|
||||
display_obj.tft.fillRect(0,
|
||||
(STATUS_BAR_WIDTH * 2) + 1 + EXT_BUTTON_WIDTH,
|
||||
TFT_WIDTH,
|
||||
TFT_HEIGHT - STATUS_BAR_WIDTH + 1,
|
||||
TFT_BLACK);
|
||||
display_obj.tft.setCursor(0, (STATUS_BAR_WIDTH * 2) + CHAR_WIDTH + EXT_BUTTON_WIDTH);
|
||||
display_obj.tft.setTextSize(1);
|
||||
#endif
|
||||
|
||||
Serial.println(F("---------------"));
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (ui_list[i].following) {
|
||||
#ifdef HAS_SCREEN
|
||||
display_obj.tft.setTextColor(TFT_RED, TFT_BLACK);
|
||||
#endif
|
||||
Serial.print(F("FOLLOWING "));
|
||||
}
|
||||
else {
|
||||
#ifdef HAS_SCREEN
|
||||
display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAS_SCREEN
|
||||
display_obj.tft.println(macToString(ui_list[i].mac) + " Tx: " + (String)ui_list[i].frame_count + " " + (String)((millis() - ui_list[i].last_seen_ms) / 1000) + "s " + (String)ui_list[i].dloc);
|
||||
#endif
|
||||
|
||||
Serial.print(macToString(ui_list[i].mac));
|
||||
Serial.println(" Frames: " + (String)ui_list[i].frame_count + " Last Seen: " + (String)((millis() - ui_list[i].last_seen_ms) / 1000) + "s");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function for updating scan status
|
||||
void WiFiScan::main(uint32_t currentTime)
|
||||
@@ -10189,6 +10577,17 @@ void WiFiScan::main(uint32_t currentTime)
|
||||
channelHop();
|
||||
}
|
||||
}
|
||||
else if (currentScanMode == WIFI_SCAN_DETECT_FOLLOW) {
|
||||
if (currentTime - initTime >= this->channel_hop_delay * HOP_DELAY) {
|
||||
initTime = millis();
|
||||
channelHop();
|
||||
}
|
||||
|
||||
if (currentTime - this->last_ui_update >= 1000) {
|
||||
this->last_ui_update = millis();
|
||||
this->updateTrackerUI();
|
||||
}
|
||||
}
|
||||
else if ((currentScanMode == BT_SCAN_FLOCK) ||
|
||||
(currentScanMode == BT_SCAN_FLOCK_WARDRIVE) ||
|
||||
(currentScanMode == BT_SCAN_WAR_DRIVE) ||
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
#define BT_SCAN_SIMPLE 73
|
||||
#define BT_SCAN_SIMPLE_TWO 74
|
||||
#define BT_SCAN_FLOCK_WARDRIVE 75
|
||||
#define WIFI_SCAN_DETECT_FOLLOW 76
|
||||
|
||||
#define WIFI_ATTACK_FUNNY_BEACON 99
|
||||
|
||||
@@ -219,6 +220,24 @@ esp_err_t esp_wifi_80211_tx(wifi_interface_t ifx, const void *buffer, int len, b
|
||||
esp_err_t esp_base_mac_addr_set(uint8_t *Mac);
|
||||
#endif
|
||||
|
||||
#define EMPTY_ENTRY 0
|
||||
#define VALID_ENTRY 1
|
||||
#define TOMBSTONE_ENTRY 2
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MacEntry {
|
||||
uint8_t mac[6];
|
||||
uint32_t last_seen_ms;
|
||||
uint16_t frame_count;
|
||||
int32_t first_lat_e6;
|
||||
int32_t first_lon_e6;
|
||||
int32_t last_lat_e6;
|
||||
int32_t last_lon_e6;
|
||||
bool following;
|
||||
int32_t dloc;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct AirTag {
|
||||
String mac; // MAC address of the AirTag
|
||||
std::vector<uint8_t> payload; // Payload data
|
||||
@@ -237,6 +256,11 @@ struct Flipper {
|
||||
extern struct mac_addr* mac_history;
|
||||
#endif
|
||||
|
||||
enum class MacSortMode : uint8_t {
|
||||
MOST_RECENT,
|
||||
MOST_FRAMES
|
||||
};
|
||||
|
||||
class WiFiScan
|
||||
{
|
||||
private:
|
||||
@@ -281,6 +305,7 @@ class WiFiScan
|
||||
//int num_deauth = 0; // RED
|
||||
|
||||
uint32_t initTime = 0;
|
||||
uint32_t last_ui_update = 0;
|
||||
bool run_setup = true;
|
||||
void initWiFi(uint8_t scan_mode);
|
||||
uint8_t bluetoothScanTime = 5;
|
||||
@@ -525,6 +550,7 @@ class WiFiScan
|
||||
NimBLEAdvertisementData GetUniversalAdvertisementData(EBLEPayloadType type);
|
||||
#endif
|
||||
|
||||
void updateTrackerUI();
|
||||
void showNetworkInfo();
|
||||
void setNetworkInfo();
|
||||
void fullARP();
|
||||
@@ -540,6 +566,7 @@ class WiFiScan
|
||||
uint8_t getSecurityType(const uint8_t* beacon, uint16_t len);
|
||||
void addAnalyzerValue(int16_t value, int rssi_avg, int16_t target_array[], int array_size);
|
||||
bool mac_cmp(struct mac_addr addr1, struct mac_addr addr2);
|
||||
bool mac_cmp(uint8_t addr1[6], uint8_t addr2[6]);
|
||||
void clearMacHistory();
|
||||
void executeWarDrive();
|
||||
void executeSourApple();
|
||||
@@ -610,6 +637,9 @@ class WiFiScan
|
||||
|
||||
//LinkedList<ssid>* ssids;
|
||||
|
||||
static MacEntry mac_entries[mac_history_len];
|
||||
static uint8_t mac_entry_state[mac_history_len];
|
||||
|
||||
// Stuff for RAW stats
|
||||
uint32_t mgmt_frames = 0;
|
||||
uint32_t data_frames = 0;
|
||||
@@ -730,7 +760,12 @@ class WiFiScan
|
||||
#ifdef HAS_SCREEN
|
||||
int8_t checkAnalyzerButtons(uint32_t currentTime);
|
||||
#endif
|
||||
bool seen_mac(unsigned char* mac);
|
||||
bool seen_mac(unsigned char* mac, bool simple = true);
|
||||
int16_t seen_mac_int(unsigned char* mac, bool simple = true);
|
||||
int update_mac_entry(const uint8_t mac[6]);
|
||||
inline void insert_mac_entry(uint32_t idx, const uint8_t mac[6], uint32_t now_ms);
|
||||
void evict_and_insert(const uint8_t mac[6], uint32_t now_ms);
|
||||
uint8_t build_top10_for_ui(MacEntry* out_top10, MacSortMode mode);
|
||||
void save_mac(unsigned char* mac);
|
||||
#ifdef HAS_BT
|
||||
void copyNimbleMac(const BLEAddress &addr, unsigned char out[6]);
|
||||
@@ -811,5 +846,6 @@ class WiFiScan
|
||||
static void pineScanSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type); // Pineapple
|
||||
static int extractPineScanChannel(const uint8_t* payload, int len); // Pineapple
|
||||
static void multiSSIDSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type); // MultiSSID
|
||||
static inline uint32_t hash_mac(const uint8_t mac[6]);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
//#define MARAUDER_V8
|
||||
//// END BOARD TARGETS
|
||||
|
||||
#define MARAUDER_VERSION "v1.9.1"
|
||||
#define MARAUDER_VERSION "v1.10.0"
|
||||
|
||||
#define GRAPH_REFRESH 100
|
||||
|
||||
#define TRACK_EVICT_SEC 120 // Seconds before marking tracked MAC as tombstone
|
||||
|
||||
#define DUAL_BAND_CHANNELS 51
|
||||
|
||||
//// HARDWARE NAMES
|
||||
|
||||
@@ -156,6 +156,14 @@ String macToString(uint8_t macAddr[6]) {
|
||||
return String(macStr);
|
||||
}
|
||||
|
||||
String macToString(const uint8_t macAddr[6]) {
|
||||
char macStr[18]; // 17 characters for "XX:XX:XX:XX:XX:XX" + 1 null terminator
|
||||
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
macAddr[0], macAddr[1], macAddr[2],
|
||||
macAddr[3], macAddr[4], macAddr[5]);
|
||||
return String(macStr);
|
||||
}
|
||||
|
||||
void convertMacStringToUint8(const String& macStr, uint8_t macAddr[6]) {
|
||||
// Ensure the input string is in the format "XX:XX:XX:XX:XX:XX"
|
||||
if (macStr.length() != 17) {
|
||||
|
||||
Reference in New Issue
Block a user