Scanner de portas com Arduino Nano

Scanner de portas com Arduino

O shield ethernet ENC28J60 para Arduino Nano é um shield fácil de configurar, uma vez que foi concebido para um Arduino específico. Mas não vamos ficar no trivial; que tal configurar um scanner de portas com Arduino, que detecta os hosts de uma rede e varre por N portas nesses hosts? Então, bora dar sequência à série de sockets, mas dessa vez com ethernet e Arduino.

Configuração básica do ethernet shield para Arduino Nano

Preste atenção nesse parágrafo: tudo o que você precisa fazer é subir o sketch abaixo após instalar a biblioteca UIPEthernet na IDE do Arduino ou qualquer outra IDE. Não é necessário se preocupar com pinout. Mas vamos ver como configurar o DHCP-Server também em uma máquina Linux, utilizando o dnsmasq. Primeiro, o sketch:

#include <Arduino.h>
#include <UIPEthernet.h>
/*
 * DESCRIÇÃO DO SKETCH DEFAULT:
 * This TcpClient example gets its local ip-address via dhcp and sets
 * up a tcp socket-connection to 192.168.0.1 port 5000 every 5 Seconds.
 * After sending a message it waits for a response. After receiving the
 * response the client disconnects and tries to reconnect after 5 seconds.
 *
 * 
  IPAddress localIP();
  IPAddress subnetMask();
  IPAddress gatewayIP();
  IPAddress dnsServerIP();
*/

EthernetClient client;

unsigned long int next = 0;

uint16_t port = 1234;

void setup() {
    Serial.begin(9600);
    uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};
    Ethernet.begin(mac); //Configure IP address via DHCP
}

void loop() {
    if (((signed long)(millis() - next)) > 0){
        next = millis() + 5000;
        // replace hostname with name of machine running tcpserver.pl
        //      if (client.connect("server.local",port))
        if (client.connect(IPAddress(192,168,100,1),68)){ 
            client.println(F("DATA from Client"));
            int size;
            while ((client.available()==0) && (millis()<next)){
            #if defined(ESP8266)
              wdt_reset();
            #endif
            }
            while((size = client.available()) > 0){
                uint8_t* msg = (uint8_t*)malloc(size+1);
                memset(msg, 0, size+1);
                size = client.read(msg,size);
                free(msg);
            }
            //disconnect client
            client.stop();
        }
    }
}

O primeiro include é necessário se estiver utilizando VS Code. Em um dos vídeos anteriores, disponível em nosso canal DobitaobyteBrasil no Youtube, mostrei alguns breves recursos do VS Code que para mim são vantagens sobre a IDE do Arduino. Com esse código, tudo o que você precisa fazer é conectar o cabo de rede ao shield e à sua rede, mas aqui estou usando o notebook para escrever o artigo, então estou conectado pelo WiFi e usando a ethernet para fazer o artigo. Por essa razão, configurei um servidor DHCP usando o dnsmasq.

Após subir o sketch, mantive um sniffer rodando na interface de rede eth0 para ler a tentativa de recebimento de IP. Se estiver trafegando esse tipo de dado, ótimo; o shield ethernet estará funcionando adequadamente. Se algo se apresentar diferente e não for atribuído IP à interface do Arduino, talvez seja interessante avaliar os códigos de mensagem do bootp, descritos nesse link.

Scanner de portas com Arduino - sniffing DHCP

A transação com o servidor DHCP acontece na porta 68, mas “não” tem relação com a porta de conexão de serviço utilizada para troca de mensagens “após” receber o IP atribuído por DHCP:

Scanner de portas com Arduino - bootp

Agora leia descompromissadamente a partir daqui.

Configurando o DNSMasq como servidor DHCP

Utilizo um Raspberry como servidor DNS usando bind9 e tenho um servidor DNS rodando no notebook, configurado pelo DNSMasq, para o caso do RPi falhar. Apenas adicionei a configuração relacionada ao servidor DHCP abaixo da configuração do DNS no arquivo /etc/dnsmasq.conf:

listen-address=127.0.0.1,192.168.1.200
interface=wlan0
server=8.8.8.8
server=8.8.4.4
server=192.168.1.2
port=53

# A D I C I O N E     A S     L I N H A S    A B A I X O
interface=eth0
no-dhcp-interface=wlan0
dhcp-range=192.168.100.2,192.168.100.3,24h
bind-interfaces

Antes de subir o serviço de sistema, executei manualmente o dnsmasq para ver a transação do Arduino com o servidor DHCP, além de estar fazendo o sniffing, caso ocorresse alguma falha na comunicação.

sudo dnsmasq -dd -C /etc/dnsmasq.conf

O resultado no shell deve ser semelhante a esse:

Mas a configuração foi extremamente tranquila e tanto no debug quanto no sniffing, foi possível ver o handshake acontecer sem problemas:

Se a interface ethernet do computador não estiver cabeada, o dnsmasq falhará como serviço. Nesse caso execute manualmente o comando acima após cabear:

dnsmasq -dd -C /etc/dnsmasq.conf

Ou, reinicie o serviço:

sudo service dnsmasq restart

Teste de comunicação

Ainda antes de implementar o código do scanner, podemos fazer proveito do sketch de exemplo e permitir o estabelecimento de conexão e troca de mensagem com o computador para aferirmos o funcionamento das interfaces. Nesse caso, utilize esse socket server em Python:

import socket
HOST = '192.168.100.1'
PORT = 1234            
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
orig = (HOST, PORT)
tcp.bind(orig)
tcp.listen(1)
while True:
    con, client = tcp.accept()
    print( 'Client: ', client)
    while True:
        msg = con.recv(6)
        if not msg: break
        #print( client, msg)
        for i in range(len(msg)):
            print(chr(msg[i]),end=' ')

    print(" ")
    print( 'Closing connection to: ', client)
    con.close()

Para executá-lo:

python sockServNano.py

E dentro do intervalo determinado no Arduino Nano, deve aparecer no terminal mensagens como:

Scanner de portas com Arduino - troca de mensagem de teste

Uma vez comprovado o funcionamento, hora de implementar o código do nosso scanner de portas com Arduino.

Código do scanner de portas com Arduino Nano

Já adianto que o código não é eficiente, mas funciona bem. Se estiver utilizando VS Code, a modificação que precisa ser feita será mais fácil. Segure Ctrl e clique em client.connect. Abrir-se-á a implementação do método connect em uma aba, cujo arquivo correspondente é o UIPClient.cpp. Nele, troque o timeout de 1000 para 500:

#if UIP_CONNECT_TIMEOUT > 0
      int32_t timeout = millis() + 400 * UIP_CONNECT_TIMEOUT;
#endif

Então mais uma vez, segure Ctrl e clique sobre UIP_CONNECT_TIMEOUT e abrir-se-á o header uipethernet-conf.h, no qual você deverá mudar o valor de UIP_CONNECT_TIMEOUT para 1.

Salve, compile e faça o upload desse sketch:

#include <Arduino.h>
#include <UIPEthernet.h>
/*
 * DESCRIÇÃO DO SKETCH DEFAULT:
 * This TcpClient example gets its local ip-address via dhcp and sets
 * up a tcp socket-connection to 192.168.0.1 port 5000 every 5 Seconds.
 * After sending a message it waits for a response. After receiving the
 * response the client disconnects and tries to reconnect after 5 seconds.
 *
 * 
  IPAddress localIP();
  IPAddress subnetMask();
  IPAddress gatewayIP();
  IPAddress dnsServerIP();
*/

EthernetClient client;

unsigned long int next = 0;

uint16_t port = 23;
uint8_t  i    = 1;

IPAddress target;

void setup() {
    client.setTimeout(20); //ms
    Serial.begin(9600);
    uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};
    Ethernet.begin(mac); //Configure IP address via DHCP
    target = Ethernet.localIP();
    Serial.print("Local address: ");
    Serial.println(target);
}

void loop() {
    if (((signed long)(millis() - next)) > 0){
        next = millis() + 2000;
        target[3] = i;
        i = i >253 ? 1 : i+1;
        
        Serial.print("host: ");
        Serial.println(target);
        for (port = 23; port<80; port++){
            Serial.print("Tentando porta ");
            Serial.println(port);
            if (client.connect(target,port)){
                client.setTimeout(20); //ms
                Serial.print("Porta aberta em ");
                Serial.print(target);
                Serial.print(": ");
                Serial.println(port);
                client.stop();
            }
            client.stop();
        }
    }
}

Range do scanner de portas com Arduino Nano

No sketch tem um loop:

for (port = 23; port<80; port++)

Iniciei da porta do telnet somente até a porta 80. Nessa linha que devemos definir a faixa de portas para a varredura. O resultado exibido na serial será semelhante a esse:

Scanner de portas com Arduino em ação

Outras opções de bibliotecas

Achei mais simples utilizar essa biblioteca, mas temos outras opções que podem ser mais viáveis, dependendo do intuito. Claro, para o scanner eu recomendo a UIPEthernet, mas temos também a EtherSia, EtherShield e a ETHER_28J60.

Onde comprar o Ethernet Shield Nano

Esse que tenho é um pouco diferente, mas é a mesma controladora desse ethernet shield Nano da Saravati. Sugiro a compra com eles, pois a contrapartida é que são confiáveis e por serem parceiros do blog, proporcionam artigos como esse. Incentive a continuidade dessa parceria, seja com esse shield ou outro dos muitos itens que a Saravati oferece!

Vídeo

Se não é inscrito ainda, inscreva-se em nosso canal DobitaobyteBrasil no Youtube, prestigiando assim o canal e o blog. Se gostar do vídeo, deixe seu like.

O link direto para o vídeo é esse. Até a próxima!

 

Revisão: Ricardo Amaral de Andrade