25 de outubro de 2021

Do bit Ao Byte

Embarcados, Linux e programação

Scanner de portas com ESP32

Scanner de portas

Esse artigo é uma prova de conceito (portanto, funcional), mas não é um projeto completo para se tornar produto. Tenho publicado artigos por esses dias sobre sockets com ESP8266 e ESP32, tanto client quanto server. Se necessário, parte desse link.

Mas antes de expor o código do scanner de portas com ESP32, vamos aos conceitos.

OSI

O modelo conceitual OSI é o “mapa” da pilha de comunicação de uma rede TCP/IP. Na primeira camada, a física, temos o dispositivo conectado, seja ele qual for. Na camada de enlace os dispositivos fazem o primeiro handshake, como por exemplo solicitar endereçamento IP. Na camada 3, a camada de rede, começa a comunicação TCP/IP.

Como discorri em outros artigos, toda a comunicação começa com a abertura de um socket, seja TCP ou UDP. As portas UDP não estabelecem conexão e seu header é menor do que o do protocolo TCP, uma vez que não há garantia de entrega e nem do ordenamento dos pacotes. Mas desse emaranhado de informações, o que nos interessa de fato é: uma comunicação começa com a abertura de uma conexão ao socket TCP. E esse scanner de portas faz a análise justamente das portas TCP.

Mas o que é um scanner de portas?

Um scanner de portas é uma ferramenta de redes para procurar por portas de serviço abertas. No exemplo, estou buscando por dispositivos da minha rede que estejam com o serviço SSH abertos para conexão. Qualquer porta pode ser escaneada, inclusive todas de um determinado host, mas esse outro recurso exibirei em um próximo artigo.

Recursos que podem ser implementados

Dá pra implementar muita coisa ainda. Por exemplo, fazer log do escaneamento, conectar a um serviço de rede próprio do dispositivo, fazendo a descoberta inteligente do serviço na rede, gerar alertas, deixando-o como monitor da rede etc.

Mas essa prova de conceito está limitada a alguns fatores:

  • Somente analisa portas TCP.
  • Somente analisa hosts de uma rede classe C, ou um rede com subnet de máscara /24.
  • Somente exibe na tela o host e a porta de serviço encontrada.
  • A conexão WiFi nesse scanner de portas está estática.

Para estudo é ótimo, uma vez que inclui apenas o essencial para o aprendizado.

Enfim, vamos ao código:

#include <Arduino.h>
#include <WiFi.h>
#include "heltec.h"

#define OLED_UPDATE_INTERVAL 500

const char* ssid       = "coloque o ssid de sua rede"; 
const char* password   = "coloque sua senha"; 

void scanHostsByPort(uint16_t port){
    //considerando apenas rede classe C ou, no mínimo, máscara /24
    String host               = WiFi.localIP().toString(); //identifica a rede em que está (ex: 192.168.10.123)
    uint8_t last_index_of_dot = host.lastIndexOf(".")+1; //descobre onde termina a rede e começa o host (ex: rede 192.168.10. host 123)

    String net                = host.substring(0,last_index_of_dot); //separa a rede do host (ex: 192.168.10.)
    
    //Hora de fazer o loop, varrendo os hosts e descobrindo se a porta está aberta
    for (uint8_t h=1; h<255;h++){ //0 é reserva de rede e 255 é o broadcast
        String target = net + String(h);
        WiFiClient client;

        if (client.connect(target.c_str(), port)) {
                Serial.print("Porta aberta em ");
                Serial.println(target);
                client.stop(); 

                String msgH = "host    " + String(h);
                String msgP = "port    " + String(port);
                Heltec.display->clear();
                Heltec.display->drawString(0, 0, msgH);
                Heltec.display->drawString(0, 10, msgP);
                Heltec.display->display();
        } 
        else{
            Serial.print("Nada em ");
            Serial.println(target);
        }
        
    }

}

void setupWIFI(){
  Heltec.display->clear();
  Heltec.display->drawString(0, 0, "Connecting...");
  Heltec.display->drawString(0, 10, String(ssid));
  Heltec.display->display();
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  //WiFi.onEvent(WiFiEvent);
  WiFi.setAutoConnect(true);
  WiFi.setAutoReconnect(true);    
  //WiFi.setHostname(HOSTNAME);
  WiFi.begin(ssid, password);
  byte count = 0;
  while(WiFi.status() != WL_CONNECTED && count < 10)
  {
    count ++;
    delay(500);
    Serial.print(".");
  }
  Heltec.display->clear();
  if(WiFi.status() == WL_CONNECTED){
    Heltec.display->drawString(0, 0, "Conectado");
    Heltec.display->drawString(0, 10, "Ate logo");
    Heltec.display->display();
    delay(5000);
    Heltec.display->clear();
     Heltec.display->display();
  }
  else{
    Heltec.display->drawString(0, 0, "Connect False");
    Heltec.display->display();
  }
  Heltec.display->clear();
}

void setup(){
   pinMode(0,INPUT_PULLDOWN);
    Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
    pinMode(25, OUTPUT);
    while (!Serial) {
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    setupWIFI();
    //se escolhida a opção 2, a primeira chamada pode ser no setup:
    vTaskDelay(pdMS_TO_TICKS(2000));
    scanHostsByPort(22);
    
}

void loop() {
    
}

Placa Heltec Wireless Stick ESP32

Para esse artigo utilizei a placa Heltec Wireless Stick da CurtoCircuito, cujo link do produto é esse. É uma placa excelente, contendo LoRa, um display OLED onboard e muita beleza com um ESP32 controlando tudo.

Vídeo

O Vídeo de apresentação do conceito está disponível em nosso canal DobitaobyteBrasil no Youtube, com link direto para o vídeo aqui. Se não é inscrito ainda, inscreva-se e deixe seu like se gostar do vídeo.

Até a próxima!

 

Revisão: Ricardo Amaral de Andrade