¿Cómo implementamos un sniffer de paquetes de red?

Antes de implementar el sniffer de paquetes de red, debe comprender cómo funciona el sniffer de paquetes.
Los rastreadores de paquetes existen en forma de software o hardware y pueden capturar el tráfico de red que es tanto entrante como saliente y monitorear el uso de contraseñas y los nombres de usuario junto con otra información confidencial. Un sniffer de paquetes le permite configurar la interfaz de la red para ver toda la información que se transmite a través de la red. Cuando los datos pasan a través del sistema, se capturan y almacenan en la memoria para que se pueda analizar la información. Normalmente, una computadora solo mira los paquetes dirigidos a él e ignora el resto del tráfico en la red. Pero cuando se configura un sniffer de paquetes en una computadora, la interfaz de red del sniffer se configura en modo promiscuo . Esto significa que está mirando todo lo que sucede.
Un sniffer de paquetes generalmente se puede configurar de una de dos maneras:
Sin filtrar : captura todos los paquetes
Filtrado : captura solo aquellos paquetes que contienen elementos de datos específicos.

Puede implementar un sniffer de paquetes simple utilizando las herramientas proporcionadas por los sistemas Unix / Linux

Conteo de paquetes
Para determinar el recuento de paquetes en el archivo, utilizamos el siguiente comando:

tcpdump -nn -r capture.pcap | wc -l

Dado que el archivo generalmente contiene una gran cantidad de datos, en lugar de usar el comando ‘cat’, es mejor canalizar el archivo a ‘head’ para que podamos tener una visión clara de él. Esto se puede lograr mediante el siguiente comando:

tcpdump -nn -r capture.pcap | cabeza

Los campos del archivo PCAP estarían en el siguiente orden:
1 vez
2. Protocolo de red
3. IP de origen
4. Puerto de origen
5. IP de destino
6. Puerto de destino

Para concentrarse en uno de los campos anteriores, elimine algunos de ellos mediante el uso de comandos para tuberías y filtros. Por ejemplo, el siguiente comando se usa para obtener solo la dirección IP de origen y su puerto:

tcpdump -nn -r capture.pcap | corte -f 3 -d “” | cabeza

Para filtrar el archivo para obtener TCP / IP y excluir el tráfico de Capa 2, agregue la opción ‘tcp’ o ‘udp’ al final del comando:

tcpdump -nn -r capture.pcap ‘tcp’ o ‘udp’ | corte -f 3 -d ““ | cabeza

Para obtener solo la dirección IP sin el puerto, simplemente corte las otras columnas a partir de “.”

tcpdump -nn -r capture.pcap ‘tcp’ o ‘udp’ | corte -f 3 -d ““ | corte -f 1-4 -d “.” | cabeza

Implementación de rastreadores de paquetes usando C :

Los rastreadores de paquetes se pueden codificar mediante el uso de sockets api proporcionados por el núcleo, o mediante el uso de alguna biblioteca de captura de paquetes como libpcap.

Uso de bibliotecas Pcap:

Debe usar programas como Pcap o WinPcap para lograr esto. Estos programas se utilizan como una interfaz de programación de aplicaciones para la captura de paquetes.

Estas bibliotecas están disponibles en C, Java y Python (también en otros lenguajes)
Referencias de estas bibliotecas en varios idiomas:

  • Perl: Net :: Pcap
  • Python: python-libpcap, Pcapy
  • Ruby: paqueteFu
  • Tcl: tclpcap, tcap, pktsrc
  • Java: jpcap, jNetPcap, Jpcap, Pcap4j
  • .NET: SharpPcap, Pcap.Net

Un programa sniffer básico que usa pcap:

int main (int argc, char ** argv)
{
int i;
char * dev;
char errbuf [PCAP_ERRBUF_SIZE];
pcap_t * descr;
const u_char * paquete;
struct pcap_pkthdr hdr;
struct ether_header * eptr; / * net / ethernet.h * /
struct bpf_program fp; / * mantener programa compilado * /
bpf_u_int32 maskp; /* máscara de subred */
bpf_u_int32 netp; / * ip * /

if (argc! = 2) {
fprintf (stdout, “Uso:% s \” expresión \ “\ n”
, argv [0]);
devuelve 0;
}

/ * Ahora obtén un dispositivo * /
dev = pcap_lookupdev (errbuf);
    
if (dev == NULL) {
fprintf (stderr, “% s \ n”, errbuf);
salida (1);
}
/ * Obtener la dirección de red y la máscara * /
pcap_lookupnet (dev, & netp, & maskp, errbuf);

/ * dispositivo abierto para leer en modo promiscuo * /
descr = pcap_open_live (dev, BUFSIZ, 1, -1, errbuf);
if (descr == NULL) {
printf (“pcap_open_live ():% s \ n”, errbuf);
salida (1);
}

/ * Ahora compilaremos la expresión de filtro * /
if (pcap_compile (descr, & fp, argv [1], 0, netp) == -1) {
fprintf (stderr, “Error al llamar a pcap_compile \ n”);
salida (1);
}

/ * establece el filtro * /
if (pcap_setfilter (descr, & fp) == -1) {
fprintf (stderr, “Error al configurar el filtro \ n”);
salida (1);
}

/ * bucle para la función de devolución de llamada * /
pcap_loop (descr, -1, my_callback, NULL);
devuelve 0;
}

Uso de sockets api proporcionados por el kernel

Para codificar un sniffer muy simple en C, los pasos serían
1. Cree un zócalo sin procesar.
2. Póngalo en un bucle recvfrom y reciba datos sobre él.
Un socket sin procesar cuando se coloca en el bucle recvfrom recibe todos los paquetes entrantes. Esto se debe a que no está vinculado a una dirección o puerto en particular.

sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
mientras que (1)
{
data_size = recvfrom (sock_raw, buffer, 65536, 0, & saddr, & saddr_size);
}

Eso es todo. El búfer mantendrá los datos olfateados o recogidos. La parte de olfateo está realmente completa por aquí. La siguiente tarea es leer el paquete capturado, analizarlo y presentarlo al usuario en un formato legible.
El siguiente código muestra un ejemplo de tal sniffer. Tenga en cuenta que solo detecta paquetes entrantes.
Código

#include // Para cosas estándar
#include // malloc
#include // memset
#include // Proporciona declaraciones para el encabezado icmp
#include // Proporciona declaraciones para el encabezado udp
#include // Proporciona declaraciones para el encabezado tcp
#include // Proporciona declaraciones para el encabezado ip
#include
#include

vacío ProcessPacket (unsigned char *, int);
vacío print_ip_header (unsigned char *, int);
void print_tcp_packet (unsigned char *, int);
void print_udp_packet (unsigned char *, int);
void print_icmp_packet (unsigned char *, int);
vacío PrintData (unsigned char *, int);

int sock_raw;
ARCHIVO * archivo de registro;
int tcp = 0, udp = 0, icmp = 0, otros = 0, igmp = 0, total = 0, i, j;
struct sockaddr_in source, dest;

int main ()
{
int saddr_size, data_size;
struct sockaddr saddr;
struct in_addr in;
    
unsigned char * buffer = (unsigned char *) malloc (65536); //¡Es grande!
    
archivo de registro = fopen (“log.txt”, “w”);
if (logfile == NULL) printf (“No se puede crear el archivo”);
printf (“Iniciando … \ n”);
// Crea un socket sin procesar que olfatee
sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
if (calcetín_raw <0)
{
printf (“Error de socket \ n”);
retorno 1;
}
mientras que (1)
{
saddr_size = sizeof saddr;
// Recibe un paquete
data_size = recvfrom (sock_raw, buffer, 65536, 0, & saddr, & saddr_size);
if (tamaño_datos <0)
{
printf (“Error de recepción, no se pudieron obtener los paquetes \ n”);
retorno 1;
}
// Ahora procesa el paquete
ProcessPacket (buffer, tamaño_de_datos);
}
close (calcetín);
printf (“Terminado”);
devuelve 0;
}

vacío ProcessPacket (buffer de char * sin signo, tamaño int)
{
// Obtenga la parte del encabezado IP de este paquete
struct iphdr * iph = (struct iphdr *) buffer;
++ total;
switch (iph-> protocol) // Verifique el protocolo y haga lo que corresponda …
{
caso 1: // Protocolo ICMP
++ icmp;
// PrintIcmpPacket (Buffer, Tamaño);
descanso;
        
caso 2: // Protocolo IGMP
++ igmp;
descanso;
        
caso 6: // Protocolo TCP
++ tcp;
print_tcp_packet (buffer, tamaño);
descanso;
        
caso 17: // Protocolo UDP
++ udp;
print_udp_packet (buffer, tamaño);
descanso;
        
predeterminado: // Algún otro protocolo como ARP, etc.
++ otros;
descanso;
}
printf (“TCP:% d UDP:% d ICMP:% d IGMP:% d Otros:% d Total:% d \ r”, tcp, udp, icmp, igmp, otros, total);
}

vacío print_ip_header (unsigned char * Buffer, int Size)
{
iphdrlen corto sin signo;
        
struct iphdr * iph = (struct iphdr *) Buffer;
iphdrlen = iph-> ihl * 4;
    
memset (& source, 0, sizeof (fuente));
source.sin_addr.s_addr = iph-> saddr;
    
memset (& dest, 0, sizeof (dest));
dest.sin_addr.s_addr = iph-> daddr;
    
fprintf (archivo de registro, “\ n”);
fprintf (archivo de registro, “Encabezado IP \ n”);
fprintf (archivo de registro, “| -IP Version:% d \ n”, (unsigned int) iph-> version);
fprintf (archivo de registro, “| -IP Longitud del encabezado:% d DWORDS o% d Bytes \ n”, (unsigned int) iph-> ihl, ((unsigned int) (iph-> ihl)) * 4);
fprintf (archivo de registro, “| -Tipo de servicio:% ​​d \ n”, (unsigned int) iph-> tos);
fprintf (archivo de registro, “| -IP Longitud total:% d Bytes (Tamaño del paquete) \ n”, ntohs (iph-> tot_len));
fprintf (archivo de registro, “| -Identificación:% d \ n”, ntohs (iph-> id));
// fprintf (archivo de registro, “| -Cero reservado campo:% d \ n”, (unsigned int) iphdr-> ip_reserved_zero);
// fprintf (archivo de registro, “| -Dont Fragment Field:% d \ n”, (unsigned int) iphdr-> ip_dont_fragment);
// fprintf (archivo de registro, “| -Más campo de fragmento:% d \ n”, (unsigned int) iphdr-> ip_more_fragment);
fprintf (archivo de registro, “| -TTL:% d \ n”, (unsigned int) iph-> ttl);
fprintf (archivo de registro, “| -Protocolo:% d \ n”, (unsigned int) iph-> protocol);
fprintf (archivo de registro, “| -Checksum:% d \ n”, ntohs (iph-> check));
fprintf (archivo de registro, “|-IP de origen:% s \ n”, inet_ntoa (source.sin_addr));
fprintf (archivo de registro, “| -Destination IP:% s \ n”, inet_ntoa (dest.sin_addr));
}

void print_tcp_packet (unsigned char * Buffer, int Size)
{
iphdrlen corto sin signo;
    
struct iphdr * iph = (struct iphdr *) Buffer;
iphdrlen = iph-> ihl * 4;
    
struct tcphdr * tcph = (struct tcphdr *) (Buffer + iphdrlen);
            
fprintf (archivo de registro, “\ n \ n *********************** Paquete TCP **************** *********\norte”);
        
print_ip_header (Buffer, Tamaño);
        
fprintf (archivo de registro, “\ n”);
fprintf (archivo de registro, “Encabezado TCP \ n”);
fprintf (archivo de registro, “| -Puerto de origen:% u \ n”, ntohs (tcph-> source));
fprintf (archivo de registro, “| -Destination Port:% u \ n”, ntohs (tcph-> dest));
fprintf (archivo de registro, “| -Número de secuencia:% u \ n”, ntohl (tcph-> seq));
fprintf (archivo de registro, “| -Número de reconocimiento:% u \ n”, ntohl (tcph-> ack_seq));
fprintf (archivo de registro, “| -Longitud del encabezado:% d DWORDS o% d BYTES \ n”, (unsigned int) tcph-> doff, (unsigned int) tcph-> doff * 4);
// fprintf (archivo de registro, “| -CWR Flag:% d \ n”, (unsigned int) tcph-> cwr);
// fprintf (archivo de registro, “| -ECN Flag:% d \ n”, (unsigned int) tcph-> ece);
fprintf (archivo de registro, “| -Indicador urgente:% d \ n”, (unsigned int) tcph-> urg);
fprintf (archivo de registro, “| -Indicador de reconocimiento:% d \ n”, (unsigned int) tcph-> ack);
fprintf (archivo de registro, “| -Push Flag:% d \ n”, (unsigned int) tcph-> psh);
fprintf (archivo de registro, “| -Reset Flag:% d \ n”, (unsigned int) tcph-> rst);
fprintf (archivo de registro, “| -Synchronise Flag:% d \ n”, (unsigned int) tcph-> syn);
fprintf (archivo de registro, “| -Finish Flag:% d \ n”, (unsigned int) tcph-> fin);
fprintf (archivo de registro, “| -Window:% d \ n”, ntohs (tcph-> window));
fprintf (archivo de registro, “| -Checksum:% d \ n”, ntohs (tcph-> check));
fprintf (archivo de registro, “| – Puntero urgente:% d \ n”, tcph-> urg_ptr);
fprintf (archivo de registro, “\ n”);
fprintf (archivo de registro, “Dump de datos”);
fprintf (archivo de registro, “\ n”);
        
fprintf (archivo de registro, “Encabezado IP \ n”);
PrintData (Buffer, iphdrlen);
        
fprintf (archivo de registro, “Encabezado TCP \ n”);
PrintData (Buffer + iphdrlen, tcph-> doff * 4);
        
fprintf (archivo de registro, “Carga de datos \ n”);
PrintData (Buffer + iphdrlen + tcph-> doff * 4, (Tamaño – tcph-> doff * 4-iph-> ihl * 4));
                        
fprintf (archivo de registro, “\ n ########################################################## ################ “);
}

void print_udp_packet (unsigned char * Buffer, int Size)
{
    
iphdrlen corto sin signo;
    
struct iphdr * iph = (struct iphdr *) Buffer;
iphdrlen = iph-> ihl * 4;
    
struct udphdr * udph = (struct udphdr *) (Buffer + iphdrlen);
    
fprintf (archivo de registro, “\ n \ n *********************** Paquete UDP **************** *********\norte”);
    
print_ip_header (Buffer, Tamaño);
    
fprintf (archivo de registro, “\ nUDP Header \ n”);
fprintf (archivo de registro, “| -Puerto de origen:% d \ n”, ntohs (udph-> source));
fprintf (archivo de registro, “| -Destination Port:% d \ n”, ntohs (udph-> dest));
fprintf (archivo de registro, “| -UDP Longitud:% d \ n”, ntohs (udph-> len));
fprintf (archivo de registro, “| -UDP Checksum:% d \ n”, ntohs (udph-> check));
    
fprintf (archivo de registro, “\ n”);
fprintf (archivo de registro, “Encabezado IP \ n”);
PrintData (Buffer, iphdrlen);
        
fprintf (archivo de registro, “Encabezado UDP \ n”);
PrintData (Buffer + iphdrlen, tamaño de udph);
        
fprintf (archivo de registro, “Carga de datos \ n”);
PrintData (Buffer + iphdrlen + sizeof udph, (Size – sizeof udph – iph-> ihl * 4));
    
fprintf (archivo de registro, “\ n ########################################################## ################ “);
}

void print_icmp_packet (unsigned char * Buffer, int Size)
{
iphdrlen corto sin signo;
    
struct iphdr * iph = (struct iphdr *) Buffer;
iphdrlen = iph-> ihl * 4;
    
struct icmphdr * icmph = (struct icmphdr *) (Buffer + iphdrlen);
            
fprintf (archivo de registro, “\ n \ n *********************** Paquete ICMP **************** *********\norte”);
    
print_ip_header (Buffer, Tamaño);
            
fprintf (archivo de registro, “\ n”);
        
fprintf (archivo de registro, “Encabezado ICMP \ n”);
fprintf (archivo de registro, “| -Type:% d”, (unsigned int) (icmph-> type));
            
if ((unsigned int) (icmph-> type) == 11)
fprintf (archivo de registro, “(TTL caducado) \ n”);
más if ((unsigned int) (icmph-> type) == ICMP_ECHOREPLY)
fprintf (archivo de registro, “(Respuesta de eco ICMP) \ n”);
fprintf (archivo de registro, “| -Code:% d \ n”, (unsigned int) (icmph-> code));
fprintf (archivo de registro, “| -Checksum:% d \ n”, ntohs (icmph-> checksum));
// fprintf (archivo de registro, “| -ID:% d \ n”, ntohs (icmph-> id));
// fprintf (archivo de registro, “| -Secuencia:% d \ n”, ntohs (icmph-> secuencia));
fprintf (archivo de registro, “\ n”);

fprintf (archivo de registro, “Encabezado IP \ n”);
PrintData (Buffer, iphdrlen);
        
fprintf (archivo de registro, “Encabezado UDP \ n”);
PrintData (Buffer + iphdrlen, tamaño de icmph);
        
fprintf (archivo de registro, “Carga de datos \ n”);
PrintData (Buffer + iphdrlen + sizeof icmph, (Size – sizeof icmph – iph-> ihl * 4));
    
fprintf (archivo de registro, “\ n ########################################################## ################ “);
}

Datos de impresión vacíos (datos de char * sin signo, tamaño int)
{
    
para (i = 0; i <Tamaño; i ++)
{
if (i! = 0 && i% 16 == 0) // si se completa una línea de impresión hexadecimal …
{
fprintf (archivo de registro, “”);
para (j = i-16; j <i; j ++)
{
if (datos [j]> = 32 && datos [j] <= 128)
fprintf (logfile, “% c”, (unsigned char) data [j]); // si es un número o alfabeto
                
else fprintf (archivo de registro, “.”); // de lo contrario imprime un punto
}
fprintf (archivo de registro, “\ n”);
}
        
if (i% 16 == 0) fprintf (archivo de registro, “”);
fprintf (archivo de registro, “% 02X”, (unsigned int) data [i]);
                
if (i == Size-1) // imprime los últimos espacios
{
para (j = 0; j <15-i% 16; j ++) fprintf (archivo de registro, ""); // espacios adicionales
            
fprintf (archivo de registro, “”);
            
para (j = ii% 16; j <= i; j ++)
{
if (datos [j]> = 32 && datos [j] <= 128) fprintf (logfile, "% c", (unsigned char) data [j]);
else fprintf (archivo de registro, “.”);
}
fprintf (archivo de registro, “\ n”);
}
}
}

Compile y ejecute este programa de socket
$ gcc sniffer.c && sudo ./a.out
El programa debe ejecutarse como usuario root o privilegios de superusuario. por ejemplo, sudo ./a.out en ubuntu
El programa crea sockets sin procesar que requieren acceso a la raíz.
El resultado en el archivo de registro es algo como esto:

*********************** Paquete TCP *************************

Encabezado IP
| -IP Versión: 4
| -IP Longitud del encabezado: 5 DWORDS o 20 Bytes
| -Tipo de servicio: 32
| -IP Longitud total: 137 bytes (tamaño del paquete)
| -Identificación: 35640
| -TTL: 51
| -Protocolo: 6
| -Checksum: 54397
| – IP de origen: 174.143.119.91
| -Destino IP: 192.168.1.6

Encabezado TCP
| -Puerto de origen: 6667
| -Puerto de destino: 38265
| -Número de secuencia: 1237278529
| -Número de reconocimiento: 65363511
| -Longitud del encabezado: 5 DWORDS o 20 BYTES
| -Indicador urgente: 0
| -Bandera de reconocimiento: 1
| -Presione la bandera: 1
| -Restablecer bandera: 0
| -Synchronise Flag: 0
| -Bandera de acabado: 0
| -Ventana: 9648
| -Checksum: 46727
| – Puntero urgente: 0

Descarga de datos
Encabezado IP
45 20 00 89 8B 38 40 00 33 06 D4 7D AE 8F 77 5B E… [correo electrónico protegido] } .. w [
C0 A8 01 06….
Encabezado TCP
1A 0B 95 79 49 BF 5F 41 03 E5 5E 37 50 18 25 B0… yI._A .. ^ 7P.%.
B6 87 00 00….
Carga de datos
3A 6B 61 74 65 60 21 7E 6B 61 74 65 40 75 6E 61: kate` [protegido por correo electrónico]
66 66 69 6C 69 61 74 65 64 2F 6B 61 74 65 2F 78 filiado / kate / x
2D 30 30 30 30 30 30 31 20 50 52 49 56 4D 53 47 -0000001 PRIVMSG
20 23 23 63 20 3A 69 20 6E 65 65 64 20 65 78 61 ## c: necesito exa
63 74 6C 79 20 74 68 65 20 72 69 67 68 74 20 6E ctly la derecha n
75 6D 62 65 72 20 6F 66 20 73 6F 63 6B 73 21 0D ¡número de calcetines !.
0A.

################################################## #########

*********************** Paquete TCP *************************

Encabezado IP
| -IP Versión: 4
| -IP Longitud del encabezado: 5 DWORDS o 20 Bytes
| -Tipo de servicio: 32
| -IP Longitud total: 186 bytes (tamaño del paquete)
| -Identificación: 56556
| -TTL: 48
| -Protocolo: 6
| -Checksum: 22899
| – IP de origen: 74.125.71.147
| -Destino IP: 192.168.1.6

Encabezado TCP
| -Puerto de origen: 80
| -Puerto de destino: 49374
| -Número de secuencia: 3136045905
| -Número de reconocimiento: 2488580377
| -Longitud del encabezado: 5 DWORDS o 20 BYTES
| -Indicador urgente: 0
| -Bandera de reconocimiento: 1
| -Presione la bandera: 1
| -Restablecer bandera: 0
| -Synchronise Flag: 0
| -Bandera de acabado: 0
| -Ventana: 44765
| -Checksum: 15078
| – Puntero urgente: 0

Descarga de datos
Encabezado IP
45 20 00 BA DC EC 00 00 30 06 59 73 4A 7D 47 93 E …… 0.YsJ} G.
C0 A8 01 06….
Encabezado TCP
00 50 C0 DE BA EC 43 51 94 54 B9 19 50 18 AE DD .P… .CQ.T..P…
3A E6 00 00:…
Carga de datos
48 54 54 50 2F 31 2E 31 20 33 30 34 20 4E 6F 74 HTTP / 1.1 304 No
20 4D 6F 64 69 66 69 65 64 0D 0A 58 2D 43 6F 6E Modificado..X-Con
74 65 6E 74 2D 54 79 70 65 2D 4F 70 74 69 6F 6E Opción tipo tienda
73 3A 20 6E 6F 73 6E 69 66 66 0D 0A 44 61 74 65 s: nosniff..Fecha
3A 20 54 68 75 2C 20 30 31 20 44 65 63 20 32 30: jue, 01 dic 20
31 31 20 31 33 3A 31 36 3A 34 30 20 47 4D 54 0D 11 13:16:40 GMT.
0A 53 65 72 76 65 72 3A 20 73 66 66 65 0D 0A 58. Servidor: sffe..X
2D 58 53 53 2D 50 72 6F 74 65 63 74 69 6F 6E 3A -XSS-Protección:
20 31 3B 20 6D 6F 64 65 3D 62 6C 6F 63 6B 0D 0A 1; modo = bloque ..
0D 0A ..

################################################## #########

Nota
1. El sniffer anterior recoge solo los paquetes TCP, debido a la declaración:
sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
Para UDP e ICMP, la declaración debe ser:
sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_UDP);
sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
Puede sentirse tentado a pensar en hacer:
sock_raw = socket (AF_INET, SOCK_RAW, IPPROTO_IP);
pero esto no funcionará, ya que IPPROTO_IP es un protocolo ficticio, no real.
2. Este sniffer recoge solo los paquetes entrantes.
3. Proporciona marcos de IP a la aplicación, lo que significa que los encabezados Ethernet no están disponibles.
4. No es muy preciso ya que pierde algunos paquetes incluso en los entrantes.
5. libpcap también se puede usar para escribir sniffers.

Para referencias:

  • Programando con pcap
  • tutorial de captura de paquetes libpcap
  • Página en iitk.ac.in
  • Crea tu propio sniffer de paquetes en C
  • pcap
  • Captura de paquetes en su programa C, con libpcap – Código abierto para usted