Introdução
Bem-vindo ao laboratório de Construção de um Scanner de Portas.
Um scanner de portas é uma ferramenta fundamental na segurança de rede, usada para identificar portas abertas e serviços em execução em uma máquina alvo. Ferramentas como o Nmap são incrivelmente poderosas, mas entender como elas funcionam nos bastidores é essencial para qualquer aspirante a profissional de cibersegurança ou engenheiro de rede.
Neste laboratório, você construirá seu próprio scanner de portas TCP personalizado do zero usando Python. Você começará analisando argumentos de linha de comando e, em seguida, implementará a lógica central de conexão de socket para verificar se uma porta está aberta. Depois disso, você adicionará tratamento de exceções para tornar sua ferramenta robusta e, finalmente, implementará multithreading para acelerar drasticamente o processo de varredura. Ao final deste laboratório, você terá um scanner de portas funcional e rápido, além de uma compreensão sólida de programação de rede em Python.
O objetivo educacional não é substituir ferramentas maduras como o Nmap. É expor as partes móveis que essas ferramentas automatizam: tratamento de entrada, tentativas de conexão, tempos limite (timeouts), interpretação de resultados e compensações de desempenho. Uma vez que você mesmo constrói um scanner básico, as ferramentas profissionais tornam-se muito menos misteriosas.
Analisar Argumentos de Linha de Comando
Nesta etapa, você estabelecerá a base do seu scanner de portas criando um script Python que pode aceitar argumentos de linha de comando. Argumentos de linha de comando permitem que os usuários especifiquem o endereço IP de destino e o intervalo de portas que desejam verificar sem modificar o código.
Este é um exemplo de separação da lógica da ferramenta da entrada do usuário. Boas ferramentas de segurança são reutilizáveis porque o código permanece o mesmo enquanto o operador altera os parâmetros a partir da linha de comando.
Usaremos o módulo nativo argparse do Python, que facilita a criação de interfaces de linha de comando amigáveis.
Primeiro, certifique-se de estar no diretório do seu projeto. É aqui que você criará e executará seu script Python.
cd ~/project
Crie um novo arquivo chamado scanner.py usando o editor de texto nano:
nano scanner.py
Copie e cole o código a seguir no editor. Este código importa o módulo argparse e define uma função para lidar com as entradas do IP de destino e do intervalo de portas.
import argparse
import sys
def get_arguments():
parser = argparse.ArgumentParser(description="A simple TCP port scanner")
parser.add_argument("-t", "--target", dest="target", help="Target IP address", required=True)
parser.add_argument("-p", "--ports", dest="ports", help="Port range to scan (e.g., 1-1024)", default="1-1024")
options = parser.parse_args()
return options
if __name__ == "__main__":
options = get_arguments()
## Extract the target and ports from the user's input
target_ip = options.target
ports_input = options.ports
print(f"[*] Target set to: {target_ip}")
print(f"[*] Port range set to: {ports_input}")
Salve o arquivo pressionando Ctrl+O e, em seguida, Enter. Se quiser continuar diretamente para a próxima etapa, você pode permanecer dentro do nano e continuar editando o mesmo arquivo. Use Ctrl+X apenas quando quiser retornar ao terminal.
Agora, teste seu script executando-o com Python. Passaremos o IP de localhost (127.0.0.1) e um pequeno intervalo de portas (7990-8010).
python3 scanner.py -t 127.0.0.1 -p 7990-8010
Você deverá ver uma saída semelhante a esta:
[*] Target set to: 127.0.0.1
[*] Port range set to: 7990-8010
Se você tentar executar o script sem o argumento -t, o argparse gerará automaticamente uma mensagem de erro e mostrará o uso correto, tornando sua ferramenta mais fácil de usar a partir do terminal.
Implementar a Lógica de Conexão de Socket
Nesta etapa, você implementará a funcionalidade principal do scanner de portas: a conexão de socket.
No nível do protocolo, uma varredura de conexão TCP básica é simples: tente abrir uma conexão TCP com uma porta de destino e observe se ela é bem-sucedida. Essa ideia é suficiente para explicar o comportamento de uma grande classe de scanners de portas.
Um socket é um ponto final para enviar ou receber dados através de uma rede de computadores. Para verificar se uma porta está aberta, seu scanner tentará estabelecer uma conexão TCP com o endereço IP de destino em uma porta específica. Se a conexão for bem-sucedida, a porta está aberta; se falhar ou atingir o tempo limite, a porta está fechada ou filtrada.
Se o scanner.py ainda estiver aberto no nano, continue editando-o. Caso contrário, reabra-o:
nano ~/project/scanner.py
Atualize seu código para incluir o módulo socket e adicione a lógica de varredura. Substitua todo o conteúdo do arquivo pelo seguinte código:
import argparse
import sys
import socket
def get_arguments():
parser = argparse.ArgumentParser(description="A simple TCP port scanner")
parser.add_argument("-t", "--target", dest="target", help="Target IP address", required=True)
parser.add_argument("-p", "--ports", dest="ports", help="Port range to scan (e.g., 1-1024)", default="1-1024")
return parser.parse_args()
def scan_port(ip, port):
## Create a new socket using the IPv4 address family and TCP protocol
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set a timeout of 1 second so the scanner doesn't hang on closed ports
sock.settimeout(1)
## connect_ex returns 0 if the connection was successful
result = sock.connect_ex((ip, port))
if result == 0:
print(f"[+] Port {port} is OPEN")
## Always close the socket after using it
sock.close()
if __name__ == "__main__":
options = get_arguments()
target_ip = options.target
## Parse the start and end ports from the input string
start_port, end_port = map(int, options.ports.split('-'))
print(f"[*] Scanning target {target_ip} from port {start_port} to {end_port}...\n")
## Loop through the specified range and scan each port sequentially
for port in range(start_port, end_port + 1):
scan_port(target_ip, port)
print("\n[*] Scan completed.")
Salve o arquivo com Ctrl+O e Enter. Você pode permanecer no nano e continuar para a próxima etapa, pois o script não precisa ser testado ainda, a menos que você queira verificar seu progresso.
Neste código, socket.socket(socket.AF_INET, socket.SOCK_STREAM) cria um socket TCP. O método connect_ex() é particularmente útil para scanners porque retorna um indicador de erro (como 0 para sucesso) em vez de levantar uma exceção quando uma conexão falha, o que mantém nosso código limpo.
Execute seu script atualizado contra sua máquina local. O ambiente de laboratório possui um servidor web oculto rodando na porta 8000, então vamos escanear um intervalo que a inclua:
python3 scanner.py -t 127.0.0.1 -p 7990-8010
Você deverá ver uma saída semelhante à seguinte, identificando com sucesso a porta 8000 como aberta:
[*] Scanning target 127.0.0.1 from port 7990 to 8010...
[+] Port 8000 is OPEN
[*] Scan completed.
Dependendo do ambiente do laboratório, você também pode ver outras portas abertas na saída. O resultado importante para esta etapa é que a porta 8000 apareça como aberta.
Lidar com Exceções de Conexão
Nesta etapa, você tornará seu scanner mais robusto adicionando tratamento de exceções.
Ferramentas de segurança passam grande parte de sua vida em condições imperfeitas: entradas incorretas, hosts inacessíveis, redes lentas e execuções interrompidas. O tratamento de exceções não é um enfeite. É parte de tornar a ferramenta utilizável durante operações reais.
Operações de rede são imprevisíveis. O usuário pode fornecer um nome de host inválido, a rede pode cair ou o usuário pode querer cancelar a varredura no meio do caminho pressionando Ctrl+C. Sem o tratamento de exceções adequado, seu script Python travará e exibirá um erro de rastreamento (traceback) feio para o usuário.
Usaremos blocos try-except para capturar esses erros específicos e tratá-los de forma elegante.
Se você ainda estiver editando o scanner.py, continue trabalhando na mesma sessão do nano. Caso contrário, reabra-o:
nano ~/project/scanner.py
Atualize seu código para incluir o tratamento de exceções. Substitua o conteúdo pela seguinte versão atualizada:
import argparse
import sys
import socket
def get_arguments():
parser = argparse.ArgumentParser(description="A simple TCP port scanner")
parser.add_argument("-t", "--target", dest="target", help="Target IP address", required=True)
parser.add_argument("-p", "--ports", dest="ports", help="Port range to scan (e.g., 1-1024)", default="1-1024")
return parser.parse_args()
def scan_port(ip, port):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((ip, port))
if result == 0:
print(f"[+] Port {port} is OPEN")
sock.close()
except socket.gaierror:
## This exception is raised for address-related errors, like an invalid hostname
print("\n[-] Hostname could not be resolved.")
sys.exit(1)
except socket.error:
## This catches general socket errors
print("\n[-] Could not connect to server.")
sys.exit(1)
if __name__ == "__main__":
options = get_arguments()
target_ip = options.target
try:
start_port, end_port = map(int, options.ports.split('-'))
except ValueError:
print("[-] Invalid port format. Please use start-end (e.g., 1-1024)")
sys.exit(1)
print(f"[*] Scanning target {target_ip} from port {start_port} to {end_port}...\n")
try:
for port in range(start_port, end_port + 1):
scan_port(target_ip, port)
except KeyboardInterrupt:
## This catches the Ctrl+C signal from the user
print("\n[-] Scan canceled by user. Exiting...")
sys.exit(1)
print("\n[*] Scan completed.")
Salve o arquivo com Ctrl+O e Enter. Se quiser testar o tratamento de exceções imediatamente, saia com Ctrl+X e execute os comandos abaixo. Caso contrário, você pode continuar diretamente para a etapa final e sair do nano apenas quando estiver pronto para executar o scanner finalizado.
Agora, vamos testar o tratamento de exceções. Tente executar o script com um formato de porta inválido para acionar o ValueError:
python3 scanner.py -t 127.0.0.1 -p 8000,8001
Você deverá ver uma mensagem de erro limpa em vez de uma falha do Python:
[-] Invalid port format. Please use start-end (e.g., 1-1024)
Em seguida, inicie uma varredura em um grande intervalo de portas e, enquanto estiver em execução, pressione Ctrl+C no teclado para cancelá-la:
python3 scanner.py -t 127.0.0.1 -p 1-5000
Ao pressionar Ctrl+C, o script capturará o KeyboardInterrupt e sairá de forma elegante:
[*] Scanning target 127.0.0.1 from port 1 to 5000...
[-] Scan canceled by user. Exiting...
Se a sua varredura for concluída antes que você possa interrompê-la, execute-a novamente com um intervalo maior, como 1-9000, e pressione Ctrl+C enquanto ela ainda estiver em execução.
Adicionar Multithreading para Velocidade
Nesta etapa final, você resolverá o maior problema do seu scanner atual: a velocidade.
No momento, seu scanner verifica uma porta de cada vez. Se uma porta estiver filtrada e levar 1 segundo para atingir o tempo limite, escanear 1.000 portas levará 1.000 segundos. Para corrigir isso, usaremos multithreading. O multithreading permite que seu programa execute várias tarefas simultaneamente. Em vez de esperar que uma verificação de porta termine antes de iniciar a próxima, criaremos uma nova thread para cada porta.
Se você manteve o scanner.py aberto, continue editando-o. Caso contrário, reabra-o:
nano ~/project/scanner.py
Importe o módulo threading e atualize o loop principal para usar threads. Substitua o conteúdo por esta versão final:
import argparse
import sys
import socket
import threading
def get_arguments():
parser = argparse.ArgumentParser(description="A simple TCP port scanner")
parser.add_argument("-t", "--target", dest="target", help="Target IP address", required=True)
parser.add_argument("-p", "--ports", dest="ports", help="Port range to scan (e.g., 1-1024)", default="1-1024")
return parser.parse_args()
def scan_port(ip, port):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((ip, port))
if result == 0:
print(f"[+] Port {port} is OPEN")
sock.close()
except (socket.gaierror, socket.error):
pass
if __name__ == "__main__":
options = get_arguments()
target_ip = options.target
try:
start_port, end_port = map(int, options.ports.split('-'))
except ValueError:
print("[-] Invalid port format. Please use start-end (e.g., 1-1024)")
sys.exit(1)
print(f"[*] Scanning target {target_ip} from port {start_port} to {end_port}...\n")
try:
## Create a list to keep track of active threads
threads = []
for port in range(start_port, end_port + 1):
## Create a new thread for each port scan
thread = threading.Thread(target=scan_port, args=(target_ip, port))
threads.append(thread)
thread.start()
## Wait for all threads to complete before exiting
for thread in threads:
thread.join()
except KeyboardInterrupt:
print("\n[-] Scan canceled by user. Exiting...")
sys.exit(1)
print("\n[*] Scan completed.")
Salve o arquivo (Ctrl+O, Enter) e saia (Ctrl+X).
Observe como adicionamos threading.Thread(target=scan_port, args=(target_ip, port)). Isso diz ao Python para executar a função scan_port em segundo plano. O loop thread.join() garante que o programa principal espere que todas as varreduras em segundo plano terminem antes de imprimir "Scan completed."
Agora, execute uma varredura em um grande intervalo de portas. Você notará que ela termina muito mais rápido do que a versão sequencial anterior.
python3 scanner.py -t 127.0.0.1 -p 1-9000
A saída será semelhante a esta:
[*] Scanning target 127.0.0.1 from port 1 to 9000...
[+] Port 8000 is OPEN
[*] Scan completed.
Dependendo dos serviços em execução no ambiente do laboratório, você pode ver outras portas abertas além da 8000. Isso é normal.
Você construiu com sucesso um scanner de portas TCP rápido e multithread em Python.
Resumo
Neste laboratório, você construiu com sucesso um scanner de portas TCP multithread personalizado do zero usando Python. Você começou aprendendo a analisar argumentos de linha de comando usando o módulo argparse para tornar sua ferramenta flexível. Em seguida, mergulhou na programação de rede implementando conexões de socket para detectar portas abertas. Para garantir que sua ferramenta fosse robusta, você adicionou tratamento de exceções para gerenciar erros de rede e interrupções do usuário de forma elegante. Finalmente, você implementou multithreading, transformando um script sequencial lento em um scanner concorrente de alta velocidade.
Essa experiência prática desmistifica como as ferramentas profissionais de varredura de rede operam nos bastidores e fornece uma base sólida para uma exploração mais aprofundada em segurança de rede e desenvolvimento em Python.



