Fala Galera!

Cansados de dar perfumes e roupas pras suas mães ?? (Alguns filhos desnaturados dão panelas de pressão e toalhas de mesa). Pois bem, porque não dar a ela o fruto de intensas pesquisas tecnológicas, hein, hein, hein? Dê a ela um sniffer! Ela vai ficar orgulhosa.

Pra fazer aquela ferramenta que eu anunciei no último post, eu tenho que estudar pesadamente o filtro de pacotes. Já descobri que o Winpcap não me permite fazer modificações em pacotes ou exclusão deles… Por isso eu tenho que encontrar outro meio 🙁 Mas os planos já estão em andamento.

O fato é que eu descobri que criar um sniffer simples é uma coisa trivial… o pessoal do Winpcap deixou uns exemplos prontos feitos em C que podem ser aprendidos rapidamente. Eu vou mostrar, utilizando o exemplo deles, como funciona um sniffer, com código e tudo.

[[ Background ]]

Um sniffer é um programinha cara-de-pau que fica ali, meio que à toa, só “ouvindo” o os pacotes passando pela rede, mesmo que não sejam dele. Pra quem mora em condomínio, é mais ou menos como aquele cara que fica na portaria só olhando as correspondências dos outros: “O ‘seu’ João, lendo Playboy?!?!”.

Antes de saber como funciona o sniffer, você precisa saber como funciona a arquitetura da rede do ponto de vista do seu SO. Você já fez uma aplicação que utiliza sockets? Bem, se a resposta for sim, você provavelmente viu um conjunto de comandos pra você interagir com Sockets.

O Socket é uma entidade de alto nível do ponto de vista da rede. Internamente, o que existe é uma intensa troca de pacotes. Cada parte da conexão guarda o estado da conexão, mas a conexão não existe de fato, é um mito! Pelo menos fisicamente.

Quando você quer abrir uma conexão a um servidor remoto, você chama uma função da API do SO que abre um novo socket pra você. Essa sua chamada gera um evento para a pilha de protocolos e o tratamento começa no TCP.

Pilha TCP/IP
Pilha de Protocolos – TCP/IP

Uma vez que você está criando uma nova conexão, o TCP monta um pacote, com o flag SYN ligado, porta de destino e todo o resto, depois esse pacote passa para o módulo que trata o IP. Esse módulo pega o pacote montado pelo TCP, coloca ele na sua área de dados e monta o seu próprio cabeçalho. Essa sacanagem continua até que o pacote chega no driver da placa de rede.

Esse driver conhece os comandos da placa de rede e se comunica diretamente com ela. Ocorre que, para que esse driver comunique-se corretamente com os drivers de protocolo (sim, aqueles que tratam o TCP/IP) eles precisam falar um idioma comum. É o famoso driver NDIS, que trabalha em nível de kernel.

NDIS significa, Network Driver Interface Specification ou Especificação da Interface do Driver de Rede e é a especificação que define a comunicação entre os drivers de protocolo e o driver da placa.

Acontece que, neste ponto, ocorre a comunicação entre o driver da sua placa e o driver dos protocolos… ou não??? Bem, um novo driver NDIS pode ser instalado e mediar a comunicação entre os dois 🙂 Trata-se de uma pilha, não é verdade?

O novo driver instalado tem que falar NDIS. Para o driver da placa, ele é mais um driver de protocolo e para o driver de protocolo, ele é o driver da placa de rede.

Esquema NDIS

[[ Winpcap ]]

Esse aí em cima é o cara que vai fazer o trabalho sujo pra nós. Ele mesmo instala o driver em questão e ainda pergunta pra nós o que queremos fazer. Ele só tem uma limitação, ele não permite que a gente modifique os pacotes da rede 🙁 Teríamos que fazer isso no nível do kernel.. Faremos isso em outro post.

A partir daqui, eu suponho que você possua algum conhecimento da linguagem C e uma ferramenta pra trablhar com ela. Se não tiver, acompanhe os conceitos passivamente. Se tiver, tente compilar o programa porque vai ser bastante gratificante 😀

Winpcap é apenas uma biblioteca que você utiliza na sua aplicação. Uma vez que ela é uma biblioteca de alto nível, seu uso é muitíssimo simples. Pra você programar nela, você vai precisar baixar o Developer´s Pack.

Esse cara contém tudo o que você precisa pra compilar uma aplicação em C/C++ que utiliza o winpcap, como libs e headers. Pra configurar a sua aplicação, siga os passos do guia deles: Using WinPcap in your programs.

Bom, eu vou utilizar o exemplo pcap_filter, que vem com o developers pack, pra fazer. Vou começar pela execução do programa, pra que fique claro o que ele faz:


D:\…>pf
pf – Generic Packet Filter.
Usage:
pf -s source -o output_file_name [-f filter_string]

PF é o executável gerado pela sua compilação do packet filter. As entradas dele são:
source: Dispositivo (ou Interface) de Rede;
output_file_name: Arquivo pra onde o tráfego deve ser direcionado;
filter_string: String de filtro, no mesmo padrão do TCPDUMP.

Bom, o parâmetro source é o mais enjoado porque ele te pede o NOME do dispositivo de rede, não o IP, número, etc.. 🙁 o que é muito chato. Por isso, fiz uma modificação no programa, usando uma parte de outro exemplo, incluindo o parâmetro -l pra listar as suas interfaces 🙂

Agora melhorou:

D:\…>pf -l
rpcap://\Device\NPF_GenericDialupAdapter
Description: Network adapter ‘Adapter for generic dialup and VPN capture’ on local host
Loopback: no
rpcap://\Device\NPF_{252D595B-9555-4CEA-8A40-420697934D49}
Description: Network adapter ‘SiS NIC SISNIC’ on local host
Loopback: no
Address Family: #2
Address Family Name: AF_INET
Address: 192.168.254.3
Netmask: 255.255.255.0
Broadcast Address: 255.255.255.255

rpcap://\Device\NPF_{D4256D4C-2FC9-462F-B00C-3EA02F54AD17}
Description: Network adapter ‘WAN (PPP/SLIP) Interface’ on local host
Loopback: no
Address Family: #2
Address Family Name: AF_INET
Address: 189.12.1.1 (Obs: Mudei o IP por razões óbvias)
Netmask: 255.255.255.255
Broadcast Address: 255.255.255.255

Bom, agora que eu tenho o nome do meu dispositivo de rede (Vou utilizar o da WAN, que é o da Internet), eu posso chamar a versão o programa com o parâmetro source:

D:\…>pf -s \Device\NPF_{D4256D4C-2FC9-462F-B00C-3EA02F54AD17} -o pacote.dat -f “tcp port 80”
^C

Eu executei uma busca no Yahoo e depois fui olhar o arquivo pacote.dat gerado:

47 45 54 20 2F 5F 79 6C 68 3D 58 33 6F 44 4D 54 ; GET /_ylh=X3oDMT
46 69 5A 7A 42 75 4F 47 46 78 42 46 39 54 41 7A ; FiZzBuOGFxBF9TAz
45 34 4D 7A 51 79 4F 54 6B 79 42 48 42 70 5A 41 ; E4MzQyOTkyBHBpZA
4D 78 4D 7A 59 35 4D 54 67 45 64 47 56 7A 64 41 ; MxMzY5MTgEdGVzdA
4D 77 42 48 52 74 63 47 77 44 59 6E 4A 66 61 57 ; MwBHRtcGwDYnJfaW
35 6B 5A 58 67 2D 2F 72 2F 62 73 77 31 2F 2A 2D ; 5kZXg-/r/bsw1/*-
68 74 74 70 3A 2F 2F 62 72 2E 73 65 61 72 63 68 ; http://br.search
2E 79 61 68 6F 6F 2E 63 6F 6D 2F 73 65 61 72 63 ; .yahoo.com/searc
68 3F 70 3D 48 61 63 6B 65 61 6E 64 6F 26 73 65 ; h?p=Hackeando&se
61 72 63 68 73 75 62 6D 69 74 3D 42 75 73 63 61 ; archsubmit=Busca

Aee 😀 … Mas isso é o de menos!! O importante é saber como ele funciona!

[[ Olhando o Código ]]

A função pcap_open_live abaixo abre o dispositivo para captura.

if ((fp = pcap_open_live(source, // Nome do dispositivo
65536, // Tamanho de cada pacote a ser analisado
1, // Modo promíscuo
1000, // timeout de leitura
errbuf // buffer de erro
)) == NULL)
{
fprintf(stderr,”\nUnable to open the adapter.\n”);
return -2;
}

fp agora é um ponteiro para o dispositivo. Utilizamos a função pcap_compile para compilar o código de filtro que foi repassado para a aplicação. No caso do meu exemplo, foi tcp port 80, para filtrar somente tráfego HTTP.

pcap_compile(fp, &fcode, filter, 1, NetMask)

Depois temos que utilizar a função pcap_setfilter para associar o filtro compilado (fcode) ao nosso dispositivo.

pcap_setfilter(fp, &fcode)

Já que queremos gravar a saída em um arquivo, chamamos a função pcap_dump_open para associar a saída a um arquivo.

dumpfile= pcap_dump_open(fp, ofilename);

Finalmente, temos o nosso loop de captura, que utiliza a função pcap_next_ex. Esta função retorna apenas quando da chegada de um novo pacote, ou quando ocorre um timeout (que deve ser tratado).

//start the capture
while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0)
{

if(res == 0)
/* Timeout elapsed */
continue;

//save the packet on the dump file
pcap_dump((unsigned char *) dumpfile, header, pkt_data);

}

As estruturas header e pkt_data contêm, respectivamente, o cabeçalho e os dados do pacote.

Por fim, a fumção pcap_dump é utilizada para gravar no arquivo de dump aberto anteriormente.

Existe outra forma de ficar em loop fazendo a captura, que é utilizar a função pcap_loop. Esta função recebe um ponteiro para uma função callback a ser chamada quando um pacote chegar.

pcap_loop(adhandle, 0, packet_handler, NULL);
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)

[[ Porque isso é legal?? ]]

Bom, se você está observando todo o tráfego da sua rede, você pode fazer a brincadeira que você quiser, desde dar uma olhadinha na senha dos outros, até abrir conexões com um IP falso por aí. Você pode, inclusive, sequestrar uma conexão, utilizando race conditions.

Você sempre pode utilizar uma ferramenta pronta, mas você vai ficar limitado ao que ela permite fazer. Se você quiser testar uma teoria sua, vai ficar difícil dessa maneira.

Espero ter contribuído para a construção do conhecimento. Aposto que sua mãe vai adorar o presente.

Dúvidas? Sugestões? Xingamentos? Aguardo feedback.

Abraços!