Merge pull request #1338 from justcallmekoko/develop

Add bluetooth fox hunt
This commit is contained in:
Just Call Me Koko
2026-06-29 13:29:57 -04:00
committed by GitHub
8 changed files with 569 additions and 277 deletions
+2 -2
View File
@@ -590,7 +590,7 @@ void Display::displayBuffer(bool do_clear)
}
}
void Display::showCenterText(const char* text, int y, bool small_pp)
void Display::showCenterText(const char* text, int y, bool small_pp, uint8_t text_size)
{
if (!text)
text = "";
@@ -598,7 +598,7 @@ void Display::showCenterText(const char* text, int y, bool small_pp)
size_t len = strlen(text);
if (!small_pp)
tft.setCursor((SCREEN_WIDTH - (len * (6 * BANNER_TEXT_SIZE))) / 2, y);
tft.setCursor((SCREEN_WIDTH - (len * (6 * text_size))) / 2, y);
else
tft.setCursor((SCREEN_WIDTH - (len * 6)) / 2, y);
+1 -1
View File
@@ -141,7 +141,7 @@ class Display
void getTouchWhileFunction(bool pressed);
void init();
void RunSetup();
void showCenterText(const char* text, int y, bool small_pp = false);
void showCenterText(const char* text, int y, bool small_pp = false, uint8_t text_size = BANNER_TEXT_SIZE);
void touchToExit();
void twoPartDisplay(String center_text);
void updateBanner(String msg);
+34
View File
@@ -337,6 +337,7 @@ void MenuFunctions::main(uint32_t currentTime)
(wifi_scan_obj.currentScanMode == WIFI_ATTACK_RICK_ROLL) ||
(wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_LIST) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_ALL) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_FOX_HUNT) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_RAYBAN) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_AIRTAG) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_AIRTAG_MON) ||
@@ -440,6 +441,7 @@ void MenuFunctions::main(uint32_t currentTime)
(wifi_scan_obj.currentScanMode == WIFI_ATTACK_RICK_ROLL) ||
(wifi_scan_obj.currentScanMode == WIFI_ATTACK_BEACON_LIST) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_ALL) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_FOX_HUNT) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_RAYBAN) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_AIRTAG) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_AIRTAG_MON) ||
@@ -1500,6 +1502,7 @@ void MenuFunctions::RunSetup()
extern LinkedList<IPAddress>* ipList;
extern LinkedList<ProbeReqSsid>* probe_req_ssids;
extern LinkedList<ssid>* ssids;
extern LinkedList<BleDevice>* ble_devices;
this->disable_touch = false;
@@ -1577,6 +1580,8 @@ void MenuFunctions::RunSetup()
gpsPOIMenu.list = new LinkedList<MenuNode>();
#endif
foxHuntMenu.list = new LinkedList<MenuNode>();
// Work menu names
mainMenu.name = text_table1[6];
wifiMenu.name = text_table1[7];
@@ -1630,6 +1635,8 @@ void MenuFunctions::RunSetup()
gpsPOIMenu.name = "GPS POI";
#endif
foxHuntMenu.name = "Fox Hunt";
// Build Main Menu
mainMenu.parentMenu = NULL;
this->addNodes(&mainMenu, text_table1[7], TFTGREEN, WIFI, [this]() {
@@ -2638,6 +2645,32 @@ void MenuFunctions::RunSetup()
this->drawStatusBar();
wifi_scan_obj.StartScan(BT_SCAN_RAYBAN, TFT_CYAN);
});
this->addNodes(&bluetoothSnifferMenu, "Fox Hunt", TFTCYAN, SCANNERS, [this]() {
foxHuntMenu.list->clear();
// Bluetooth Fox Hunt Menu
foxHuntMenu.parentMenu = &bluetoothSnifferMenu; // Second Menu is third menu parent
this->addNodes(&foxHuntMenu, text09, TFTLIGHTGREY, 0, [this]() {
this->changeMenu(foxHuntMenu.parentMenu, true);
});
for (int i = 0; i < ble_devices->size(); i++) {
BleDevice ble_device = ble_devices->get(i);
ble_device.selected = false;
ble_devices->set(i, ble_device);
uint8_t node_color = rssiToMenuColor(ble_devices->get(i).rssi);
String node_name = String(ble_devices->get(i).rssi) + " " + ble_devices->get(i).name;
this->addNodes(&foxHuntMenu, node_name.c_str(), node_color, 255, [this, i](){
BleDevice ble_device = ble_devices->get(i);
ble_device.selected = true;
ble_devices->set(i, ble_device);
display_obj.clearScreen();
this->drawStatusBar();
wifi_scan_obj.StartScan(BT_SCAN_FOX_HUNT, TFT_CYAN);
});
}
this->changeMenu(&foxHuntMenu, true);
});
// Bluetooth Attack menu
bluetoothAttackMenu.parentMenu = &bluetoothMenu; // Second Menu is third menu parent
@@ -2679,6 +2712,7 @@ void MenuFunctions::RunSetup()
this->drawStatusBar();
wifi_scan_obj.StartScan(BT_ATTACK_SPAM_ALL, TFT_MAGENTA);
});
#endif
//#ifndef HAS_ILI9341
+2
View File
@@ -196,6 +196,8 @@ class MenuFunctions
Menu evilPortalMenu;
Menu foxHuntMenu;
//static void lv_tick_handler();
// Menu icons
+458 -268
View File
@@ -34,6 +34,7 @@ LinkedList<AirTag>* airtags;
LinkedList<Flipper>* flippers;
LinkedList<IPAddress>* ipList;
LinkedList<ProbeReqSsid>* probe_req_ssids;
LinkedList<BleDevice>* ble_devices;
extern "C" int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3){
if (arg == 31337)
@@ -1062,9 +1063,36 @@ extern "C" {
#endif
}
}
else if (wifi_scan_obj.currentScanMode == BT_SCAN_ALL) {
else if ((wifi_scan_obj.currentScanMode == BT_SCAN_ALL) ||
(wifi_scan_obj.currentScanMode == BT_SCAN_FOX_HUNT)) {
if (buf >= 0)
{
BleDevice ble_device;
if (name_length > 0)
ble_device.name = name;
else
ble_device.name = mac;
ble_device.rssi = rssi;
memcpy(ble_device.mac, mac_char, sizeof(mac_char));
int device_match_check = wifi_scan_obj.seenBLEDevice(ble_device);
if (device_match_check >= 0) {
ble_device.selected = ble_devices->get(device_match_check).selected;
ble_device.name = ble_devices->get(device_match_check).name;
memcpy(ble_device.mac, ble_devices->get(device_match_check).mac, sizeof(mac_char));
ble_devices->set(device_match_check, ble_device);
//Serial.println(ble_devices->get(device_match_check).name + " RSSI updated: " + String(ble_devices->get(device_match_check).rssi));
return;
}
if (wifi_scan_obj.currentScanMode == BT_SCAN_FOX_HUNT)
return;
ble_devices->add(ble_device);
#ifndef HAS_MINI_SCREEN
display_string.concat(text_table4[0]);
#endif
@@ -1102,6 +1130,64 @@ extern "C" {
display_obj.loading = false;
}
#endif
/*Serial.println("\n========== BLE Advertisement ==========");
Serial.printf("Address: %s\n", advertisedDevice->getAddress().toString().c_str());
Serial.printf("Address Type: %u\n", advertisedDevice->getAddressType());
Serial.printf("RSSI: %d dBm\n", advertisedDevice->getRSSI());
Serial.printf("Adv Type: %u\n", advertisedDevice->getAdvType());
Serial.printf("Adv Length: %u\n", advertisedDevice->getAdvLength());
Serial.printf("Connectable: %s\n", advertisedDevice->isConnectable() ? "yes" : "no");
Serial.printf("Scannable: %s\n", advertisedDevice->isScannable() ? "yes" : "no");
Serial.printf("Legacy Adv: %s\n", advertisedDevice->isLegacyAdvertisement() ? "yes" : "no");
if (advertisedDevice->haveName()) {
Serial.printf("Name: %s\n", advertisedDevice->getName().c_str());
}
if (advertisedDevice->haveTXPower()) {
Serial.printf("TX Power: %d dBm\n", advertisedDevice->getTXPower());
}
if (advertisedDevice->haveAppearance()) {
Serial.printf("Appearance: 0x%04X\n", advertisedDevice->getAppearance());
}
if (advertisedDevice->haveAdvInterval()) {
Serial.printf("Adv Interval: %u units\n", advertisedDevice->getAdvInterval());
}
if (advertisedDevice->haveURI()) {
Serial.printf("URI: %s\n", advertisedDevice->getURI().c_str());
}
uint8_t svcCount = advertisedDevice->getServiceUUIDCount();
Serial.printf("Service UUIDs: %u\n", svcCount);
for (uint8_t i = 0; i < svcCount; i++) {
Serial.printf(" [%u] %s\n", i, advertisedDevice->getServiceUUID(i).toString().c_str());
}
uint8_t svcDataCount = advertisedDevice->getServiceDataCount();
Serial.printf("Service Data: %u\n", svcDataCount);
for (uint8_t i = 0; i < svcDataCount; i++) {
Serial.printf(" UUID [%u]: %s\n", i, advertisedDevice->getServiceDataUUID(i).toString().c_str());
printStringData(" Data", advertisedDevice->getServiceData(i));
}
uint8_t mfgCount = advertisedDevice->getManufacturerDataCount();
Serial.printf("Mfg Data Sets: %u\n", mfgCount);
for (uint8_t i = 0; i < mfgCount; i++) {
printStringData(" Mfg", advertisedDevice->getManufacturerData(i));
}
const std::vector<uint8_t> &payload = advertisedDevice->getPayload();
Serial.printf("Raw Payload [%u]: ", (unsigned)payload.size());
printHex(payload.data(), payload.size());
Serial.println();
Serial.println("=======================================");*/
}
}
else if (wifi_scan_obj.currentScanMode == WIFI_SCAN_WAR_DRIVE) {
@@ -1382,6 +1468,24 @@ extern "C" {
#endif
#endif
int WiFiScan::seenBLEDevice(BleDevice ble_device) {
for (int i = 0; i < ble_devices->size(); i++) {
//Serial.println("Comparing names " + ble_devices->get(i).name + " | " + ble_device.name);
if ((ble_devices->get(i).name == ble_device.name) || (ble_device.name == "")) {
//Serial.print("Comparing MACs ");
//Serial.print(macToString(ble_devices->get(i).mac));
//Serial.print(" | ");
//Serial.println(macToString(ble_device.mac));
//for (int x = 0; x < 6; x++) {
// if (ble_devices->get(i).mac[x] != ble_device.mac[x])
// return -1;
//}
return i;
}
}
return -1;
}
bool WiFiScan::isFlockCamera(const uint8_t* payload, size_t len, const String& name, String* serial_out) {
if (payload == nullptr || len < 4) {
return false;
@@ -1511,6 +1615,7 @@ void WiFiScan::RunSetup() {
stations = new LinkedList<Station>();
airtags = new LinkedList<AirTag>();
flippers = new LinkedList<Flipper>();
ble_devices = new LinkedList<BleDevice>();
ipList = new LinkedList<IPAddress>();
probe_req_ssids = new LinkedList<ProbeReqSsid>;
// for Pinescan
@@ -1631,6 +1736,12 @@ int WiFiScan::clearList(uint8_t list_type) {
airtags->remove(0);
return num_cleared;
}
else if (list_type == CLEAR_BLE) {
num_cleared = ble_devices->size();
while (ble_devices->size() > 0)
ble_devices->remove(0);
return num_cleared;
}
else if (list_type == CLEAR_FLIP) {
num_cleared = flippers->size();
while (flippers->size() > 0)
@@ -1983,6 +2094,7 @@ void WiFiScan::StartScan(uint8_t scan_mode, uint16_t color) {
else if (scan_mode == WIFI_ATTACK_AP_SPAM)
this->startWiFiAttacks(scan_mode, color, " AP Beacon Spam ");
else if ((scan_mode == BT_SCAN_ALL) ||
(scan_mode == BT_SCAN_FOX_HUNT) ||
(scan_mode == BT_SCAN_RAYBAN) ||
(scan_mode == BT_SCAN_AIRTAG) ||
(scan_mode == BT_SCAN_AIRTAG_MON) ||
@@ -2355,6 +2467,7 @@ void WiFiScan::StopScan(uint8_t scan_mode) {
if ((currentScanMode == BT_SCAN_ALL) ||
(currentScanMode == BT_SCAN_FOX_HUNT) ||
(currentScanMode == BT_SCAN_RAYBAN) ||
(currentScanMode == BT_SCAN_AIRTAG) ||
(currentScanMode == BT_SCAN_AIRTAG_MON) ||
@@ -2384,6 +2497,14 @@ void WiFiScan::StopScan(uint8_t scan_mode) {
this->analyzer_name_update = true;
#endif
for (int i = 0; i < ble_devices->size(); i++) {
if (ble_devices->get(i).selected) {
BleDevice ble_device = ble_devices->get(i);
ble_device.selected = false;
ble_devices->set(i, ble_device);
}
}
this->shutdownBLE();
this->ble_scanning = false;
#endif
@@ -5365,6 +5486,7 @@ void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color) {
NimBLEDevice::init("");
pBLEScan = NimBLEDevice::getScan(); //create new scan
if ((scan_mode == BT_SCAN_ALL) ||
(scan_mode == BT_SCAN_FOX_HUNT) ||
(scan_mode == BT_SCAN_RAYBAN) ||
(scan_mode == BT_SCAN_AIRTAG) ||
(scan_mode == BT_SCAN_AIRTAG_MON) ||
@@ -5380,6 +5502,8 @@ void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color) {
display_obj.tft.fillRect(0,16,TFT_WIDTH,16, color);
if (scan_mode == BT_SCAN_ALL)
display_obj.tft.drawCentreString(text_table4[41],TFT_WIDTH / 2,16,2);
else if (scan_mode == BT_SCAN_FOX_HUNT)
display_obj.tft.drawCentreString("Fox Hunt",TFT_WIDTH / 2,16,2);
else if (scan_mode == BT_SCAN_AIRTAG)
display_obj.tft.drawCentreString("Airtag Sniff",TFT_WIDTH / 2,16,2);
else if (scan_mode == BT_SCAN_AIRTAG_MON)
@@ -5401,12 +5525,21 @@ void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color) {
#endif
display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
#endif
if (scan_mode == BT_SCAN_ALL)
if (scan_mode == BT_SCAN_ALL) {
this->clearList(CLEAR_BLE);
#ifndef HAS_NIMBLE_2
pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanAllCallback(), false);
pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanAllCallback(), true);
#else
pBLEScan->setScanCallbacks(new bluetoothScanAllCallback(), false);
pBLEScan->setScanCallbacks(new bluetoothScanAllCallback(), true);
#endif
}
else if (scan_mode == BT_SCAN_FOX_HUNT) {
#ifndef HAS_NIMBLE_2
pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanAllCallback(), true);
#else
pBLEScan->setScanCallbacks(new bluetoothScanAllCallback(), true);
#endif
}
else if ((scan_mode == BT_SCAN_FLIPPER) ||
(scan_mode == BT_SCAN_RAYBAN) ||
(scan_mode == BT_SCAN_FLOCK) ||
@@ -9867,281 +10000,335 @@ uint16_t WiFiScan::rssiToColor(int8_t rssi) {
return TFT_RED;
}
// Upload one log file to WDG Wars.
// Mirrors backendUpload() but uses X-API-Key auth and wdgwars.pl endpoint.
/*bool WiFiScan::wdgwarsUpload(String filePath) {
//display.clearScreen();
//display.drawCenteredText("WDG Upload...", true);
delay(100);
if (!SD.exists(filePath)) {
//display.drawCenteredText(filePath + " not found", true);
Serial.println("[WDG] File not found: " + filePath);
return false;
}
String apiKey = settings_obj.loadSetting<String>(WDG_KEY_NAME);
if (apiKey.isEmpty()) {
#ifdef HAS_DIRECT_UPLOAD
// Upload one log file to WDG Wars.
// Mirrors backendUpload() but uses X-API-Key auth and wdgwars.pl endpoint.
bool WiFiScan::wdgwarsUpload(String filePath) {
//display.clearScreen();
//display.drawCenteredText("No WDG API key", true);
Serial.println("[WDG] No WDG Wars API key configured");
return false;
}
//display.drawCenteredText("WDG Upload...", true);
delay(100);
File fileToUpload = SD.open(filePath);
if (!fileToUpload) {
//display.clearScreen();
//display.drawCenteredText("Could not open file", true);
Serial.println("[WDG] Could not open: " + filePath);
return false;
}
// Build multipart body
String boundary = "----ESP32BOUNDARY";
String part1 = "--" + boundary + "\r\n";
part1 += "Content-Disposition: form-data; name=\"file\"; filename=\"" +
filePath + "\"\r\n";
part1 += "Content-Type: application/octet-stream\r\n\r\n";
String part2 = "\r\n--" + boundary + "--\r\n";
int totalLength = part1.length() + fileToUpload.size() + part2.length();
Serial.println("[WDG] File size: " + String(fileToUpload.size()));
Serial.println("[WDG] Total length: " + String(totalLength));
client->setInsecure();
if (!client->connect("wdgwars.pl", 443)) {
fileToUpload.close();
client->stop();
//display.clearScreen();
//display.drawCenteredText("WDG connect fail", true);
Serial.println("[WDG] Failed to connect to wdgwars.pl");
return false;
}
// HTTP request
client->println("POST /api/v2/upload-csv HTTP/1.1");
client->println("Host: wdgwars.pl");
client->println("User-Agent: ESP32Uploader/1.0");
client->println("Accept: application/json");
client->println("X-API-Key: " + apiKey);
client->println("Content-Type: multipart/form-data; boundary=" + boundary);
client->print("Content-Length: ");
client->println(totalLength);
client->println();
// Send body
client->print(part1);
const size_t CHUNK = 4096;
uint8_t buf[CHUNK];
size_t totalSent = 0;
uint8_t pct = 0;
String pctStr;
while (fileToUpload.available()) {
size_t n = fileToUpload.read(buf, CHUNK);
totalSent += n;
client->write(buf, n);
pct = (totalSent * 100) / fileToUpload.size();
//display.tft->drawRect(0, (TFT_HEIGHT / 3) * 2, TFT_WIDTH, TFT_HEIGHT - (TFT_HEIGHT / 3) * 2, ST77XX_BLACK);
//display.tft->setCursor(0, (TFT_HEIGHT / 3) * 2);
pctStr = String(pct) + "%";
//display.drawCenteredText(pctStr, false);
}
client->print(part2);
fileToUpload.close();
Serial.println("[WDG] Bytes sent: " + String(totalSent));
// Read response
String response;
unsigned long t = millis();
while (millis() - t < 5000) {
while (client->available()) {
char c = client->read();
response += c;
if (!SD.exists(filePath)) {
//display.drawCenteredText(filePath + " not found", true);
Serial.println("[WDG] File not found: " + filePath);
return false;
}
if (!client->connected() && !client->available())
break;
}
client->stop();
// Capture first 200 chars of response for log viewer
String respTrunc = response.length() > 200 ? response.substring(0, 200) : response;
Serial.println("[WDG] Response: " + respTrunc);
// WDG Wars returns 200 on success
bool ok = response.indexOf("202 Accepted") >= 0 ||
response.indexOf("\"ok\":true") >= 0;
//display.clearScreen();
//display.drawCenteredText(ok ? "WDG OK" : "WDG Failed", true);
delay(1000);
return ok;
}*/
// Upload one log file to Wigle
/*bool WiFiScan::wigleUpload(String filePath) {
//server.begin();
//display.clearScreen();
//display.drawCenteredText("Wigle Upload...", true);
delay(100);
if (!SD.exists(filePath)) {
//display.clearScreen();
//display.drawCenteredText(filePath + " not found", true);
Serial.println("File does not exist: " + filePath);
return false;
}
File fileToUpload = SD.open(filePath);
if (!fileToUpload) {
//display.clearScreen();
//display.drawCenteredText("Could not open file", true);
Serial.println("Could not open file: " + filePath);
return false;
}
// Load credentials
String username = settings_obj.loadSetting<String>("wu");
String token = settings_obj.loadSetting<String>("wt");
if (username.isEmpty() || token.isEmpty()) {
fileToUpload.close();
//display.clearScreen();
//display.drawCenteredText("No wigle creds", true);
Serial.println("Missing wigle credentials");
return false;
}
Serial.println("Username: " + username);
Serial.println("Token: " + token);
String boundary = "----ESP32BOUNDARY";
String contentType = "multipart/form-data; boundary=" + boundary;
// Build parts
String part1 = "--" + boundary + "\r\n";
part1 += "Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath + "\"\r\n";
part1 += "Content-Type: application/octet-stream\r\n\r\n";
String part2 = "\r\n--" + boundary + "\r\n";
part2 += "Content-Disposition: form-data; name=\"donate\"\r\n\r\non\r\n";
String part3 = "--" + boundary + "--\r\n";
int totalLength = part1.length() + fileToUpload.size() + part2.length() + part3.length();
Serial.println("part1.length(): " + String(part1.length()));
Serial.println("fileToUpload.size(): " + String(fileToUpload.size()));
Serial.println("part2.length(): " + String(part2.length()));
Serial.println("part3.length(): " + String(part3.length()));
Serial.println("Total Content-Length: " + String(totalLength));
Serial.print("File size: ");
Serial.println(fileToUpload.size());
// Connect manually via WiFiClientSecure
//WiFiClientSecure *client = new WiFiClientSecure();
//client.stop();
client->setInsecure();
if (!client->connect("api.wigle.net", 443)) {
fileToUpload.close();
//delete client;
client->stop();
//display.clearScreen();
//display.drawCenteredText("Could not connect", true);
Serial.println("Failed to connected to api.wigle.net");
return false;
}
Serial.println("Connected");
// Compose headers
String auth = base64Encode(username + ":" + token);
Serial.println("Finished encoding");
client->println("POST /api/v2/file/upload HTTP/1.1");
client->println("Host: api.wigle.net");
client->println("User-Agent: ESP32Uploader/1.0");
client->println("Accept: application/json");
client->println("Authorization: Basic " + auth);
client->println("Content-Type: " + contentType);
client->print("Content-Length: ");
client->println(totalLength);
client->println();
delay(100);
Serial.println("Finished sending header");
// Send body
client->print(part1);
const size_t BUFFER_SIZE = 4096; // 1KB at a time
uint8_t buffer[BUFFER_SIZE];
Serial.println("Finished sending part1");
uint8_t percent_sent = 0;
String display_percent = "";
size_t totalBytesSent = 0;
while (fileToUpload.available()) {
size_t bytesRead = fileToUpload.read(buffer, BUFFER_SIZE);
totalBytesSent += bytesRead;
Serial.print("Writing ");
Serial.print(totalBytesSent);
Serial.println(" bytes...");
percent_sent = (totalBytesSent * 100) / fileToUpload.size();
//display.tft->drawRect(0, (TFT_HEIGHT / 3) * 2, TFT_WIDTH, TFT_HEIGHT, ST77XX_BLACK);
//display.tft->setCursor(0, (TFT_HEIGHT / 3) * 2);
display_percent = (String)percent_sent + "%";
//display.drawCenteredText(display_percent, false);
client->write(buffer, bytesRead);
}
Serial.println("Uploaded file bytes: " + String(totalBytesSent));
client->print(part2);
client->print(part3);
Serial.println("Finished sending part2 and part3");
fileToUpload.close();
// Read response
String response;
unsigned long timeout = millis();
while (millis() - timeout < 5000) {
while (client->available()) {
char c = client->read();
response += c;
String apiKey = settings_obj.loadSetting<String>(WDG_KEY_NAME);
if (apiKey.isEmpty()) {
//display.clearScreen();
//display.drawCenteredText("No WDG API key", true);
Serial.println("[WDG] No WDG Wars API key configured");
return false;
}
File fileToUpload = SD.open(filePath);
if (!fileToUpload) {
//display.clearScreen();
//display.drawCenteredText("Could not open file", true);
Serial.println("[WDG] Could not open: " + filePath);
return false;
}
// Build multipart body
String boundary = "----ESP32BOUNDARY";
String part1 = "--" + boundary + "\r\n";
part1 += "Content-Disposition: form-data; name=\"file\"; filename=\"" +
filePath + "\"\r\n";
part1 += "Content-Type: application/octet-stream\r\n\r\n";
String part2 = "\r\n--" + boundary + "--\r\n";
int totalLength = part1.length() + fileToUpload.size() + part2.length();
Serial.println("[WDG] File size: " + String(fileToUpload.size()));
Serial.println("[WDG] Total length: " + String(totalLength));
client->setInsecure();
if (!client->connect("wdgwars.pl", 443)) {
fileToUpload.close();
client->stop();
//display.clearScreen();
//display.drawCenteredText("WDG connect fail", true);
Serial.println("[WDG] Failed to connect to wdgwars.pl");
return false;
}
// HTTP request
client->println("POST /api/v2/upload-csv HTTP/1.1");
client->println("Host: wdgwars.pl");
client->println("User-Agent: ESP32Uploader/1.0");
client->println("Accept: application/json");
client->println("X-API-Key: " + apiKey);
client->println("Content-Type: multipart/form-data; boundary=" + boundary);
client->print("Content-Length: ");
client->println(totalLength);
client->println();
// Send body
client->print(part1);
const size_t CHUNK = 4096;
uint8_t buf[CHUNK];
size_t totalSent = 0;
uint8_t pct = 0;
String pctStr;
while (fileToUpload.available()) {
size_t n = fileToUpload.read(buf, CHUNK);
totalSent += n;
client->write(buf, n);
pct = (totalSent * 100) / fileToUpload.size();
//display.tft->drawRect(0, (TFT_HEIGHT / 3) * 2, TFT_WIDTH, TFT_HEIGHT - (TFT_HEIGHT / 3) * 2, ST77XX_BLACK);
//display.tft->setCursor(0, (TFT_HEIGHT / 3) * 2);
pctStr = String(pct) + "%";
//display.drawCenteredText(pctStr, false);
}
client->print(part2);
fileToUpload.close();
Serial.println("[WDG] Bytes sent: " + String(totalSent));
// Read response
String response;
unsigned long t = millis();
while (millis() - t < 5000) {
while (client->available()) {
char c = client->read();
response += c;
}
if (!client->connected() && !client->available())
break;
}
client->stop();
// Capture first 200 chars of response for log viewer
String respTrunc = response.length() > 200 ? response.substring(0, 200) : response;
Serial.println("[WDG] Response: " + respTrunc);
// WDG Wars returns 200 on success
bool ok = response.indexOf("202 Accepted") >= 0 ||
response.indexOf("\"ok\":true") >= 0;
//display.clearScreen();
//display.drawCenteredText(ok ? "WDG OK" : "WDG Failed", true);
delay(1000);
return ok;
}
if (millis() - timeout > 5000)
Serial.println("Timeout reached");
if (!client->connected())
Serial.println("Client disconnected");
client->stop();
// Upload one log file to Wigle
bool WiFiScan::wigleUpload(String filePath) {
//server.begin();
String respTrunc = response.length() > 200 ? response.substring(0, 200) : response;
Serial.println("[WIGLE] Response: " + respTrunc);
//display.clearScreen();
//display.drawCenteredText("Wigle Upload...", true);
bool ok = response.indexOf("200 OK") >= 0;
delay(100);
//display.clearScreen();
//display.drawCenteredText(ok ? "WIGLE OK" : "WIGLE Failed", true);
if (!SD.exists(filePath)) {
//display.clearScreen();
//display.drawCenteredText(filePath + " not found", true);
Serial.println("File does not exist: " + filePath);
return false;
}
return ok;
}*/
File fileToUpload = SD.open(filePath);
if (!fileToUpload) {
//display.clearScreen();
//display.drawCenteredText("Could not open file", true);
Serial.println("Could not open file: " + filePath);
return false;
}
// Load credentials
String username = settings_obj.loadSetting<String>("wu");
String token = settings_obj.loadSetting<String>("wt");
if (username.isEmpty() || token.isEmpty()) {
fileToUpload.close();
//display.clearScreen();
//display.drawCenteredText("No wigle creds", true);
Serial.println("Missing wigle credentials");
return false;
}
Serial.println("Username: " + username);
Serial.println("Token: " + token);
String boundary = "----ESP32BOUNDARY";
String contentType = "multipart/form-data; boundary=" + boundary;
// Build parts
String part1 = "--" + boundary + "\r\n";
part1 += "Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath + "\"\r\n";
part1 += "Content-Type: application/octet-stream\r\n\r\n";
String part2 = "\r\n--" + boundary + "\r\n";
part2 += "Content-Disposition: form-data; name=\"donate\"\r\n\r\non\r\n";
String part3 = "--" + boundary + "--\r\n";
int totalLength = part1.length() + fileToUpload.size() + part2.length() + part3.length();
Serial.println("part1.length(): " + String(part1.length()));
Serial.println("fileToUpload.size(): " + String(fileToUpload.size()));
Serial.println("part2.length(): " + String(part2.length()));
Serial.println("part3.length(): " + String(part3.length()));
Serial.println("Total Content-Length: " + String(totalLength));
Serial.print("File size: ");
Serial.println(fileToUpload.size());
// Connect manually via WiFiClientSecure
//WiFiClientSecure *client = new WiFiClientSecure();
//client.stop();
client->setInsecure();
if (!client->connect("api.wigle.net", 443)) {
fileToUpload.close();
//delete client;
client->stop();
//display.clearScreen();
//display.drawCenteredText("Could not connect", true);
Serial.println("Failed to connected to api.wigle.net");
return false;
}
Serial.println("Connected");
// Compose headers
String auth = base64Encode(username + ":" + token);
Serial.println("Finished encoding");
client->println("POST /api/v2/file/upload HTTP/1.1");
client->println("Host: api.wigle.net");
client->println("User-Agent: ESP32Uploader/1.0");
client->println("Accept: application/json");
client->println("Authorization: Basic " + auth);
client->println("Content-Type: " + contentType);
client->print("Content-Length: ");
client->println(totalLength);
client->println();
delay(100);
Serial.println("Finished sending header");
// Send body
client->print(part1);
const size_t BUFFER_SIZE = 4096; // 1KB at a time
uint8_t buffer[BUFFER_SIZE];
Serial.println("Finished sending part1");
uint8_t percent_sent = 0;
String display_percent = "";
size_t totalBytesSent = 0;
while (fileToUpload.available()) {
size_t bytesRead = fileToUpload.read(buffer, BUFFER_SIZE);
totalBytesSent += bytesRead;
Serial.print("Writing ");
Serial.print(totalBytesSent);
Serial.println(" bytes...");
percent_sent = (totalBytesSent * 100) / fileToUpload.size();
//display.tft->drawRect(0, (TFT_HEIGHT / 3) * 2, TFT_WIDTH, TFT_HEIGHT, ST77XX_BLACK);
//display.tft->setCursor(0, (TFT_HEIGHT / 3) * 2);
display_percent = (String)percent_sent + "%";
//display.drawCenteredText(display_percent, false);
client->write(buffer, bytesRead);
}
Serial.println("Uploaded file bytes: " + String(totalBytesSent));
client->print(part2);
client->print(part3);
Serial.println("Finished sending part2 and part3");
fileToUpload.close();
// Read response
String response;
unsigned long timeout = millis();
while (millis() - timeout < 5000) {
while (client->available()) {
char c = client->read();
response += c;
}
}
if (millis() - timeout > 5000)
Serial.println("Timeout reached");
if (!client->connected())
Serial.println("Client disconnected");
client->stop();
String respTrunc = response.length() > 200 ? response.substring(0, 200) : response;
Serial.println("[WIGLE] Response: " + respTrunc);
bool ok = response.indexOf("200 OK") >= 0;
//display.clearScreen();
//display.drawCenteredText(ok ? "WIGLE OK" : "WIGLE Failed", true);
return ok;
}
#endif
void WiFiScan::runFoxHunt(uint32_t currentTime) {
#ifdef HAS_SCREEN
if (currentTime - this->last_ui_update >= 100)
this->last_ui_update = millis();
else
return;
display_obj.tft.fillRect(0, (TFT_HEIGHT / 3), TFT_WIDTH, TFT_HEIGHT / 3, TFT_BLACK);
#ifdef HAS_BT
if (currentScanMode == BT_SCAN_FOX_HUNT) {
for (int i = 0; i < ble_devices->size(); i++) {
if (ble_devices->get(i).selected) {
int targ_rssi = ble_devices->get(i).rssi;
#ifdef HAS_MINI_SCREEN
display_obj.tft.setTextSize(1);
#else
display_obj.tft.setTextSize(2);
#endif
display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK);
#ifdef HAS_MINI_SCREEN
display_obj.showCenterText(ble_devices->get(i).name.c_str(), (TFT_HEIGHT / 4), true);
#else
display_obj.showCenterText(ble_devices->get(i).name.c_str(), (TFT_HEIGHT / 4), false, 2);
#endif
#ifdef HAS_MINI_SCREEN
display_obj.tft.setTextSize(2);
#else
display_obj.tft.setTextSize(3);
#endif
display_obj.tft.setTextColor(rssiToColorScaled(targ_rssi), TFT_BLACK);
#ifdef HAS_MINI_SCREEN
display_obj.showCenterText(String(targ_rssi).c_str(), (TFT_HEIGHT / 3), false, 2);
#else
display_obj.showCenterText(String(targ_rssi).c_str(), (TFT_HEIGHT / 3), false, 3);
#endif
display_obj.tft.fillRect(0, (TFT_HEIGHT / 4) * 3, rssiToBarWidth(targ_rssi), 20, rssiToColorScaled(targ_rssi));
display_obj.tft.fillRect(rssiToBarWidth(targ_rssi), (TFT_HEIGHT / 4) * 3, TFT_WIDTH, 20, TFT_BLACK);
}
}
}
#endif
#endif
}
// Function for updating scan status
void WiFiScan::main(uint32_t currentTime)
@@ -10175,6 +10362,9 @@ void WiFiScan::main(uint32_t currentTime)
this->channelHop(true);
}
}
else if (currentScanMode == BT_SCAN_FOX_HUNT) {
this->runFoxHunt(currentTime);
}
else if (currentScanMode == WIFI_SCAN_DETECT_FOLLOW) {
if (currentTime - initTime >= this->channel_hop_delay * HOP_DELAY) {
initTime = millis();
+20 -5
View File
@@ -14,12 +14,12 @@
#include <NimBLEDevice.h> // 1.3.8, 2.3.2
#endif
#ifdef HAS_IDF_3
/*#ifdef HAS_IDF_3
extern "C" {
#include "esp_netif.h"
#include "esp_netif_net_stack.h"
}
#endif
#endif*/
//#include <WiFi.h>
#include <ESP32Ping.h>
@@ -68,8 +68,10 @@
#include "LedInterface.h"
#endif
//#include <WiFiClientSecure.h>
//#include "mbedtls/sha256.h"
#ifdef HAS_DIRECT_UPLOAD
#include <WiFiClientSecure.h>
#include "mbedtls/sha256.h"
#endif
#define bad_list_length 3
@@ -160,6 +162,7 @@
#define BT_SCAN_RAYBAN 81
#define BT_ATTACK_APPLE_JUICE 82
#define WIFI_SCAN_DISPLAY_AP_INFO 83
#define BT_SCAN_FOX_HUNT 84
#define WIFI_ATTACK_FUNNY_BEACON 99
@@ -211,6 +214,7 @@
#define CLEAR_PINE 5
#define CLEAR_MULTI 6
#define CLEAR_SSID 7
#define CLEAR_BLE 8
extern EvilPortal evil_portal_obj;
@@ -274,6 +278,13 @@ struct Flipper {
String name;
};
struct BleDevice {
uint8_t mac[6];
String name;
bool selected = false;
int rssi = -128;
};
#ifdef HAS_PSRAM
extern struct mac_addr* mac_history;
#endif
@@ -304,7 +315,9 @@ class WiFiScan
uint mac_history_cursor = 0;
uint8_t channel_hop_delay = 1;
//WiFiClientSecure *client = new WiFiClientSecure();
#ifdef HAS_DIRECT_UPLOAD
WiFiClientSecure *client = new WiFiClientSecure();
#endif
int x_pos; //position along the graph x axis
float y_pos_x; //current graph y axis position of X value
@@ -593,6 +606,7 @@ class WiFiScan
bool wigleUpload(String filePath);
bool wdgwarsUpload(String filePath);
void runFoxHunt(uint32_t currentTime);
void throwThatShitInACircle();
void displayTargetFilter();
void displayTransmitRate();
@@ -869,6 +883,7 @@ class WiFiScan
bool checkFlockOUI(const uint8_t mac[6]);
bool startWiFi(String ssid, String password, bool gui = true);
bool isFlockCamera(const uint8_t* payload, size_t len, const String& name, String* serial_out);
int seenBLEDevice(BleDevice ble_device);
uint16_t rssiToColor(int8_t rssi);
bool isMetaIdentifier(uint16_t id);
bool isBlockedIdentifier(uint16_t id);
+4
View File
@@ -538,6 +538,7 @@
#define HAS_NIMBLE_2
#define HAS_IDF_3
//#define HAS_SIMPLEX_DISPLAY
//#define HAS_DIRECT_UPLOAD
#endif
#if defined(MARAUDER_M5_NANO_C6)
@@ -2430,6 +2431,9 @@
#define BANNER_TIME GRAPH_REFRESH
#define TFT_WIDTH 0
#define TFT_HEIGHT 0
#define TFT_BLACK 0
#define TFT_WHITE 0
#define TFT_CYAN 0
+48 -1
View File
@@ -257,7 +257,7 @@ inline uint16_t getNextPort(uint16_t port) {
return port + 1;
}
String base64Encode(const String& input) {
inline String base64Encode(const String& input) {
size_t outputLen;
unsigned char output[256];
mbedtls_base64_encode(output, sizeof(output), &outputLen,
@@ -265,4 +265,51 @@ String base64Encode(const String& input) {
return String((char*)output).substring(0, outputLen);
}
inline static void printHex(const uint8_t *data, size_t len) {
for (size_t i = 0; i < len; i++) {
if (data[i] < 0x10) Serial.print('0');
Serial.print(data[i], HEX);
if (i + 1 < len) Serial.print(' ');
}
}
inline static void printStringData(const char *label, const std::string &data) {
Serial.printf("%s [%u]: ", label, (unsigned)data.length());
printHex((const uint8_t *)data.data(), data.length());
Serial.println();
}
inline static uint8_t rssiToMenuColor(int rssi) {
if (rssi >= -60) {
return TFTGREEN;
} else if (rssi >= -70) {
return TFTYELLOW;
} else if (rssi >= -80) {
return TFTORANGE;
} else {
return TFTRED;
}
}
inline static uint16_t rssiToColorScaled(int rssi) {
// Clamp to expected BLE RSSI range
rssi = constrain(rssi, -100, -40);
// Map RSSI to 0-255
uint8_t green = map(rssi, -100, -40, 0, 255);
uint8_t red = 255 - green;
// RGB888 -> RGB565
return ((red & 0xF8) << 8) |
((green & 0xFC) << 3);
}
inline static int rssiToBarWidth(int rssi) {
// Clamp to the full possible RSSI range
rssi = constrain(rssi, -127, 0);
// Map RSSI to a width between 0 and TFT_WIDTH
return map(rssi, -127, 0, 0, TFT_WIDTH);
}
#endif