Edilei Marcos de Oliveira <edileioliveira@hotmail.com>
Instituto de Informática
Universidade Norte do Paraná
1. Introdução
2. A ferramenta de monitoração
3. Modelo em camadas TCP/IP
4. A biblioteca Winsock
5. Decodifição dos datagramas IP
6. Apresentação da ferramenta de monitoração
7. Conclusão
Referências bibliográficas
O objetivo principal deste artigo é apresentar as rotinas da Winsock necessárias para a implementação de uma ferramenta de monitoração de tráfego de rede TCP/IP em uma rede ETHERNET. Esta ferramenta de monitoração trabalha com a captura, decodificação e cópia dos datagramas IPv4 em buffer, obedecendo às regras de captura definidas em filtro. Para a captura de todos os pacotes que estiverem transitando pelo segmento de rede, o dispositivo de comunicação deve ser configurado para operar em modo promíscuo. Para isto, no Windows, usa-se um driver de captura externo (de terceiros) ou usa-se um socket promíscuo configurado através das rotinas da Winsock 2.2 do Windows 2000 ou superior. Ferramentas como TCPDUMP e ETHEREAL utilizam driver de captura externo para rodarem, porém, a partir do Windows 2000, pode-se usar a API Winsock 2.2 ou superior do próprio sistema operacional para capturar os datagramas IP. Os códigos aqui apresentados estão em C++ sobre o ambiente Visual C++ 6.0.
Através da análise dos pacotes TCP/IP capturados da rede, o administrador de rede pode gerenciar melhor sua rede, pela análise dos pacotes ICMP; realizar mapeamento das conexões TCP/IP de um determinado host, pela análise de tráfego originado ou destinado a determinado host; verificar níveis de segurança, observando se há dados secretos que não estão sendo criptografados; descobrir os serviços (SMTP, DNS, FTP, HTTP e outros) que os hosts da rede ETHERNET oferecem ou requisitam.
Como o domínio de captura pode ser muito amplo, é interessante estabelecer algumas restrições de captura. Algumas regras de filtragem podem ser definidas: por endereço, por tipo de protocolo, por porta de comunicação ou por string. Desta forma, a análise fica concentrada nos pacotes que safisfazem as regras do filtro.
As seguintes etapas são realizadas pela ferramenta:
Existem dois modelos dominantes sobre a divisão dos protocolos em camadas. O primeiro foi proposto pela ISO, é conhecido como modelo OSI composto por 7 camadas. O segundo modelo, mais adequado a realidade, designa que a pilha de protocolos TCP/IP (tabela 1) é composta por 4 camadas ou níveis: aplicação, transporte, rede, física.
Aplicação HTTP, SMTP, POP3, DNS, FTP, SNMP, DHCP, telnet, EGP e outros |
Mensagens (Correspondem às camadas OSI 5, 6 e 7) | Protocolos usados pelos programas aplicativos. Os programas aplicativos passam as mensagens apropriadas ao protocolo de transporte para transmissão. |
Transporte TCP, UDP |
Pacotes ou mensagens (Correspondem à camada OSI 4) | TCP: protocolo de transferência confiável, com conexão
e com controle de fluxo. UDP: protocolo de transferência de mensagens não confiável, sem conexão e sem controle de fluxo. |
Rede IP, ICMP |
Datagramas IP (Correspondem à camada OSI 3) | IP: protocolo de roteamento, sem conexão. ICMP: é parte do datagrama IP, protocolo para deteção de erros de rede usado por hosts e roteadores. |
Física Drivers de dispositivo e protocolos de acesso ao meio |
Quadros de rede (Correspondem às camadas OSI 1 e 2) | Os protocolos da camada física recebem e transmitem os datagramas IP através da rede conectada. |
Tabela 1 - Camadas TCP/IP
Na perspectiva da ferramenta de monitoração, o diagrama 1 ilustra o relacionamento entre os protocolos. O socket em modo promíscuo recebe o datagrama IP, representado em dois campos: cabeçalho e área de dados. O cabeçalho do datagrama IP possui um campo que indica qual pacote (TCP, UDP, ICMP ou outros) está encapsulado em sua área de dados e outro campo que especifica o tamanho do cabeçalho em palavras de 32 bits, permitindo determinar o início da área de dados do datagrama. Os pacotes TCP ou UDP contêm um cabeçalho e podem conter uma área de dados (alguns pacotes não possuem área de dados porque transportam somente as informações de controle). Os cabeçalhos dos pacotes TCP e UDP possuem campos de porta de origem e porta de destino que identificam os programas aplicativos nas extremidades da conexão, um campo de comprimento do cabeçalho que permite determinar o início da área de dados quando existir.
Diagrama 1 - Relacionamento entre os protocolos TCP/IP
Todas as operações de recebimento de datagramas IP serão realizadas através de chamadas às rotinas da Winsock 2.2 , que é uma biblioteca do Windows que provê recursos de comunicação de dados. Antes que comecemos a utilizar a biblioteca Winsock, nós devemos declarar o cabeçalho: #include <winsock2.h> e importar a biblioteca ws2_32.lib. O trecho de código abaixo inicializa a biblioteca para uso.
/*armazena informações sobre a inicialização da Winsock */ WSAData info; WORD versao; versao= MAKEWORD(2,2); /*inicialização da biblioteca WS2_32.DLL versão 2.2*/
if (WSAStartup(versao, &info) != 0) { MessageBox(NULL, "A inicialização da Winsock falhou!", "WSAStartup", MB_OK);
}
A rotina WSAStartup serve para inicializar a biblioteca Winsock. O parâmetro versao informa a versão da biblioteca requisitada e o parâmetro info obtém informações sobre a biblioteca inicializada.
Depois de terminar de usar a biblioteca Winsock a aplicação deverá chamar a rotina WSACleanup para desregistrar a biblioteca WS2_32.DLL e liberar os recursos alocados para ela.
/* libera recursos da biblioteca alocada */ WSACleanup();
Após a inicialização da biblioteca Winsock é executada a criação de um socket.
/*criação do socket */ sd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
O primeiro argumento (AF_INET) da função socket especifica que o protocolo a ser usado pelo socket pertence a família TCP/IP. O segundo argumento (SOCK_RAW) especifica que os datagramas IP podem encapsular qualquer tipo de protocolo, inclusive protocolos que estão em fase experimental. O terceiro argumento (IPPROTO_IP) especifica o protocolo IP. A função socket retorna um descritor de socket.
Depois da criação do socket é necessário configurá-lo através da função bind. O primeiro argumento da função bind é o descritor de socket para a captura dos pacotes. Para o segundo argumento, passa-se o endereço da variável interfaceRecepcao do tipo SOCKADDR_IN, que contém as informações de endereço IP da interface utilizada na captura, a família de protocolos (TCP/IP) e a porta de operação, e realiza-se um cast com a estrutura SOCKADDR. O terceiro argumento é o comprimento da variável interfaceRecepcao.
A função WSAIoctl é usada para configurar o socket ou para retornar parâmetros operacionais associados ao socket. No caso, usamos a palavra SIO_RCVALL para configurar o socket em modo promíscuo que habilita o socket a receber todos os datagramas IP da rede. Privilégios de administrador são exigidos sobre o computador local para configurar este socket. SIO_RCVALL está disponível em Windows 2000 ou superior. O comando SIO_RCVALL, antes de ser usado, deve ser definido como: #define SIO_RCVALL WSAIOW(IOC_VENDOR,1).
... /*configurações para o socket */ SOCKADDR_IN interfaceRecepcao; /* configuração da interface de recepção */ /* endereço IP da inferface de captura */
interfaceRecepcao.sin_addr.S_un = enderecoIp.S_un; /* comunicação TCP/IP */ interfaceRecepcao.sin_family = AF_INET; interfaceRecepcao.sin_port = htons(0); /* associa o endereço IP da interface de captura ao socket */ if (bind(sd, (SOCKADDR *) &interfaceRecepcao, sizeof(interfaceRecepcao))==SOCKET_ERROR) { wsprintf(mensagem,"bind() falhou. Código de erro: %d",WSAGetLastError()); MessageBox(NULL,"Função bind() falhou","Aviso!",MB_OK); return 0; } int comprimentoBufferEntrada; comprimentoBufferEntrada = 1; /*configura o socket para modo promíscuo */ if (WSAIoctl(sd, SIO_RCVALL,&comprimentoBufferEntrada, sizeof(comprimentoBufferEntrada),NULL,0,&bytesRetorno,NULL, NULL) == SOCKET_ERROR) { wsprintf(mensagem, "WSAIoctl(SIO_RCVALL) falhou. Código de erro: %d", WSAGetLastError()); MessageBox(NULL,"Função WSAIoctl() falhou","Aviso!",MB_OK);
return 0; } ...
Devemos chamar a função WSARecv para receber o datagrama IP através do socket. O primeiro argumento da função WSARecv é o descritor de socket que foi configurado para modo promíscuo. O segundo argumento é um ponteiro para um array de estrutura WSABUF. Cada estrutura WSABUF possui um ponteiro para um buffer e o comprimento do buffer em bytes. O terceiro argumento está informando que o array de WSABUF possui tamanho um. O quarto argumento é um ponteiro para um número que armazena o número de bytes recebidos após a chamada WSARecv se completar.
... char mensagem[100]; unsigned long comprimento; unsigned long flags; flags=0; comprimento=0; /* recebe os pacotes na estrutura wsabuf */ if (SOCKET_ERROR==WSARecv(sd,wsabuf, 1, &comprimento, &flags, NULL, NULL)) { wsprintf(mensagem, "WSARecv() falhou. Código de erro: %d",WSAGetLastError()); MessageBox(NULL, mensagem, "Problema no recebimento!", MB_OK); return 0; } wsabuf->len=comprimento;
...
Logo após que a chamada WSARecv se complete apropriadamente, o sistema começa a realizar a decodificação parcial do datagrama IP, que consiste numa seqüência de bits brutos, para verificar se o pacote atende às regras de filtragem. Caso o pacote atenda às regras do filtro, ele é bufferizado, senão é descartado. Este processo de captura e decodificação repete-se até que o buffer esteja cheio ou o usuário interrompa o processo de captura.
Como o pacote recebido sempre é um datagrama IP, a decodificação se inicia pela análise dos campos do seu cabeçalho que contêm informações sobre o protocolo que sua área de dados transporta. Desencapsula-se o datagrama IP e se avalia o protocolo que está nele contido, começando-se pela análise dos campos do seu cabeçalho que contêm informações sobre o possível protocolo contido em sua área de dados.
O diagrama 2 apresenta as classes de decodificação dos protocolos IP, TCP, UDP e ICMP (só o cabeçalho comum entre os vários tipos de mensagens ICMP) em notação UML. A relação de dependência das classes de decodificação TCP, UDP e ICMP com a classe IP, é considerada devido ao contexto de decodificação.
Após a chamada à função WSARecv se completar adequadamente, vamos decodificar parcialmente o datagrama IP que está no buffer em uma estrutura do tipo WSABUF. Primeiramente, decodificamos apenas alguns campos importantes para defrontarmos com as regras de filtragem dos pacotes.O trecho de código a seguir contém métodos para a decodificação parcial do datagrama IP pertencentes à classe de decodificação CDatagramaIP.
... /* recebe o datagrama IP em formato bruto para decodicação */ void CDatagramaIP::decodificarParcialmenteDatagramaIP(WSABUF *wsabuf) { versao_e_comprimento = (BYTE) wsabuf->buf[0]; tamanhoTotal = (BYTE) wsabuf->buf[2]*256 + (BYTE) wsabuf->buf[3];> protocolo = (BYTE) wsabuf->buf[9]; ipOrigem = (BYTE) wsabuf->buf[12]*16777216 + (BYTE) wsabuf->buf[13]*65536 + (BYTE) wsabuf->buf[14]*256 + (BYTE) wsabuf->buf[15]; ipDestino = (BYTE) wsabuf->buf[16]*16777216 + (BYTE) wsabuf->buf[17]*65536 + (BYTE) wsabuf->buf[18]*256 + (BYTE) wsabuf->buf[19]; /* dados do datagrama IP*/ dados.len=tamanhoTotal - obterTamanhoCabecalho(); dados.buf=&wsabuf->buf[obterTamanhoCabecalho()]; } /* obtém o tamanho do cabecalho em bytes */ unsigned char CDatagramaIP::obterTamanhoCabecalho() { unsigned char aux; aux = versao_e_comprimento; /*determina os valores dos 4 bits menos significativos */ aux = aux & 15; return aux*4; } /* obtém tamanho total do datagrama IP unsigned short CDatagramaIP::obterTamanhoTotal() { return tamanhoTotal; } /* obtém código do protocolo encapsulado no datagrama IP int CDatagramaIP::obterCodigoProtocolo() { return protocolo; } /* obtém o endereço IP de destino do datagrama no formato de um número inteiro */ unsigned int CDatagramaIP::obterIpDestinoNumerico() { return ipDestino; } /* obtém o endereço IP de origem do datagrama no formato de um número inteiro */ unsigned int CDatagramaIP::obterIpOrigemNumerico() { return ipOrigem; } /* obtém os dados do datagrama IP */ WSABUF CDatagramaIP::obterDadosIP() { return dados; } ...
Diagrama 2 - Classes de decodificação TCP/IP
A figura 1 mostra um conjunto de datagramas IP capturados da rede e que contêm em um comum a string rnp em sua área de dados. O pacote de número 22, que está em foco, por exemplo, tem 1500 bytes de tamanho, encapsula os protocolos TCP e HTTP, foi transmitido do host 200.130.9.7, usando a porta de conexão 80 (serviço HTTP), para o host 200.186.215.183 na porta de conexão 1249. Podemos verificar, na área de dados do pacote, que se trata de um texto legível, isto é, sem criptografia da informação.
Figura 1 - Tela da ferramenta contendo pacotes capturados
Este artigo teve como foco a apresentação das primitivas da API Winsock 2.2 para criação e uso de um socket em modo promíscuo para desenvolvimento de uma ferramenta de monitoração do tráfego de rede TCP/IP sobre uma rede ETHERNET. A avaliação do tráfego de rede permite: gerenciar melhor a rede, verificar níveis de segurança, mapear conexões e descobrir os serviços oferecidos ou utilizados pela rede.
[1] COMER, D. E.; STEVENS, D. L. Internetworking with TCP/IP. New Jersey: Prentice-Hall, v.3, 1997.
[2] COMER, D. E.; STEVENS, D. L. Interligação em Rede com TCP/IP. Rio de Janeiro: Campus, v.1, 1998.
[3] WELCOME TO THE MSDN LIBRARY. Welcome to the MSDN Library. Disponível em: <http://msdn.microsoft.com/library>. Acesso em 01 set. 2002.
[4] CAPTURE PACKETS WITH WINSOCK. Capture packets with Winsock. Disponível em: <http://www.securityfriday.com/Topics/caputure_packet.html>. Acesso em 22 abr. 2002.
[5] POSTEL, J. User Datagram Protocol. RFC 768, aug. 1980. Disponível em: <http://www.ietf.org/rfc/rfc0768.txt?number=768>. Acesso em 22 abr. 2002.
[6] INFORMATION SCIENCES INSTITUTE. UNIVERSITY OF SOUTHERN CALIFORNIA. Internet Protocol. RFC 791, sep. 1981. Disponível em: <http://www.ietf.org/rfc/rfc0791.txt?number=791>. Acesso em 22 abr. 2002.
[7] POSTEL, J. Internet Control Message Protocol. RFC 792, sep. 1981. Disponível em: <http://www.ietf.org/rfc/rfc0792.txt?number=792>. Acesso em 22 abr. 2002.
[8] INFORMATION SCIENCES INSTITUTE. UNIVERSITY OF SOUTHERN CALIFORNIA. Transmission Control Protocol. Disponível em: <http://www.ietf.org/rfc/rfc0793.txt?number=793>. Acesso em 22 abr. 2002.