Вы находитесь на странице: 1из 55

RAW SOCKET Programming

Ing. Mauricio Mena Cortés


koitoer
27 de junio de 2009

1
Índice general

1. Prefacio 5
1.1. A quien lo lea: . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2. Conocimientos básicos 7
2.1. Requerimientos del sistema . . . . . . . . . . . . . . . . . . . 7
2.2. Modelo OSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.1. Capa 1: Fı́sica . . . . . . . . . . . . . . . . . . . . . . 8
2.2.2. Capa 2: Enlace . . . . . . . . . . . . . . . . . . . . . . 8
2.2.3. Capa 3: Red . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.4. Capa 4: Transporte . . . . . . . . . . . . . . . . . . . 12
2.2.5. Capa 5: Sesión . . . . . . . . . . . . . . . . . . . . . . 14
2.2.6. Capa 6: Presentación . . . . . . . . . . . . . . . . . . 14
2.2.7. Capa 7: Aplicación . . . . . . . . . . . . . . . . . . . . 14

3. RAW SOCKET ?? 16
3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2. Funciones importantes . . . . . . . . . . . . . . . . . . . . . . 17
3.2.1. socket . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.2. sendto . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.3. recvfrom . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.4. getsockopt && setsocketopt . . . . . . . . . . . . . . . 18
3.3. Algunas estrcuturas importantes . . . . . . . . . . . . . . . . 19
3.3.1. Estructura ifreq . . . . . . . . . . . . . . . . . . . . . . 19
3.4. Un ejemplo para empezar . . . . . . . . . . . . . . . . . . . . 21

4. Sniffer 28
4.1. SNIFFER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1.1. PROMISC MODE ?? . . . . . . . . . . . . . . . . . . 28

2
ÍNDICE GENERAL 3

4.2. ksniffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.1. Compilación y Ejecución . . . . . . . . . . . . . . . . 31
4.3. Sniffer mejorado. . . . . . . . . . . . . . . . . . . . . . . . . . 31

5. Implantar Funcionalidades 42
5.1. Modificaciones . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.1.1. Añadimos UDP . . . . . . . . . . . . . . . . . . . . . . 42
5.1.2. Añadimos ICMP . . . . . . . . . . . . . . . . . . . . . 43
5.1.3. Añadimos DHCP . . . . . . . . . . . . . . . . . . . . . 44
5.1.4. Añadimos ARP . . . . . . . . . . . . . . . . . . . . . . 51

6. Conclusión 54
6.1. Problemas principales . . . . . . . . . . . . . . . . . . . . . . 54
6.2. Tutoriales futuros . . . . . . . . . . . . . . . . . . . . . . . . 54
6.3. Ideas Finales . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.4. Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . 55
Índice de figuras

2.1. Ethernet (Header) . . . . . . . . . . . . . . . . . . . . . . . . 9


2.2. IP (Header) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. ICMP (Header) . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4. TCP (Header) . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5. UDP (Header) . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6. Modelo OSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.1. Ejecución captada por IPTraf del código bajado de internet y


como vemos no funciona . . . . . . . . . . . . . . . . . . . . . 21
3.2. Ejecución captada por Wireshark . . . . . . . . . . . . . . . . 27

4.1. Empaquetado en modelo OSI . . . . . . . . . . . . . . . . . . 32

5.1. Header ICMP . . . . . . . . . . . . . . . . . . . . . . . . . . . 44


5.2. Ejecucion del sniffer, Protocolo DHCP . . . . . . . . . . . . . 50
5.3. Ejecución del sniffer, Protocolo ARP . . . . . . . . . . . . . . 53

4
Parte 1

Prefacio

1.1. A quien lo lea:


En este tutorial trataré de explicar y plasmar mediante algunos ejemplos
propios y algunos otros corregidos que obtuve de la red, la forma y funcional-
idad que se puede tener al programar con RAW SOCKET, pero vamos, ni
siquiera he definido que es un RAW SOCKET, en este caso su traducción
a nuestro lenguaje es ”socket crudo”, mucha gente se podrá preguntar por
que la palabra crudo, esto se debe a que este tipo de sockets trabajan sin
estar ligados a un protocolo de comunicaciones, es decir, podemos darle el sa-
bor(flavor) que necesitemos, esto en programación lo podemos traducir como
darle la funcionalidad que nostros deseamos.
Estos sockets nos permiten el acceso a los protocolos de comunicaciones,
con la posibilidad de hacer uso o no de protocolos de capa 3 y/o 4, y por
lo tanto dándonos el acceso a los protocolos directamente y a la información
que recide en ellos. El uso de sockets de este tipo nos va a permitir la imple-
mentación de nuevos protocolos, y por qué no decirlo, la modificación de los
ya existentes.
Cabe mencionar que en esta ocasión, pondré en el tutorial la documentación
de algunas estructuras de datos y funciones que nos ayudarán a crear y enten-
der el uso de los sockets crudos, aún asi tampoco pretendo explicar ni entrar a
detalle en el modelo OSI, el cual nos servirá sólo como referencia para enten-
der un poco más del funcionamiento de los sockets, aunque sı́ le dedicaré una
sección a lo que considero más importante del modelo al trabajar con estos
sockets.

5
PARTE 1. PREFACIO 6

Bueno espero el tutorial sea claro, conciso y les sirva para empezar a
entender un poco más acerca de la programación de sockets crudos, empeze-
mos.
Parte 2

Conocimientos básicos

2.1. Requerimientos del sistema


Primero que nada todo este manual está orientado a sistemas linux - unix,
como el anterior tutorial. La distribución que se use no tendrá por qué im-
portar, ya que en realidad lo que se necesitará es tener instalado

1. GCC

Sı́, sólo necesitaremos el compilador de GNU, ya que en esta ocasión to-


das las definiciones y funciones se van a hallar en el propio sistema de linux,
normalmente en la carpeta /usr/include/net, /usr/include/netinet y /usr/in-
clude/linux, las cuales son cargadas al sistema en una instalación normal del
sistema.
También nos podremos auxiliar del archivo /etc/protocol, para el conocimien-
to de los protocolos y sus abreviaciones.

2.2. Modelo OSI


El modelo de capas OSI (Open System Interconection) es uno de los
pilares básicos para el desarollo de las redes actuales y es completamente
aplicable a redes de tipo TCP/IP, fue creado en 1984 por ISO, con el fin de
estandarizar el proceso de creación de nuevos tipos de redes de informática.

7
PARTE 2. CONOCIMIENTOS BÁSICOS 8

Lo que en realidad hace el modelo OSI es dividir, a modo de referencia,


cualquier sistema de red en capas diferenciadas entre sı́, para comprender de
forma más gráfica la funcionalidad de cada una de ellas.

2.2.1. Capa 1: Fı́sica


Esta capa es la encargada de mover los bits entre los diferentes dispos-
itivos, da las especificaciones eléctricas, mecánicas y los requerimientos con
respecto a voltajes, intensidades, cables, pines, modulaciones para una trans-
misión exitosa de la información, con lo que se tienen interfaces que pueden
estar activadas o desactivadas, esta capa nos provee de la topologı́a fı́sica de
la red, por lo que en este tutorial no haremos uso de ella.

2.2.2. Capa 2: Enlace


Después de la demodulación de la señal, esta capa es la encargada de
revisar las alteraciones que esta pudo haber sufrido en el medio, esta capa
trabaja con tramas, permite encontrar diversos errores en las mismas, se le
suele conocer como MAC (Media Access Control). En esta capa actúa el LLC
(Logic Link Control), y se trabaja con direcciones fı́sicas (MAC ADDRESS)
de las NIC (Network Interface Control), Switches y Conmutadores. El PDU
que se maneja es trama (frame), controla el framing, la notificación de errores,
la topologı́a de red y el principal estándar de esta capa es el IEEE-802.

ETHERNET

#include < i f e t h e r . h>

struct e t h h d r
{
unsigned char h d e s t [ ETH ALEN ] ;
/∗ d e s t i n a t i o n e t h addr ∗/
unsigned char h s o u r c e [ ETH ALEN ] ;
/∗ s o u r c e e t h e r addr ∗/
unsigned short h p r o t o ;
/∗ p a c k e t t y p e ID f i e l d ∗/
};
PARTE 2. CONOCIMIENTOS BÁSICOS 9

Figura 2.1: Ethernet (Header)

2.2.3. Capa 3: Red


Capa encargada de encaminar datos al destino, eligiendo la mejor ruta,
en esta capa trabajan los protocolos de ruteo. El PDU (Data Unit) que se
maneja es el paquete, y el principal protocolo que trabaja en esta capa es
IP (Internet protocol), aunque no debemos de olvidar que también trabajan
ICMP (Internet Common Message Protocol), ARP (Address Resolution Pro-
tocol), RARP (Reverse Address Resolution Protocol), PPTP (Point-to-point
Tunneling Protocol), entre otros.

IP

#include <i p . h>

struct i p h d r {
#i f d e f i n e d ( LITTLE ENDIAN BITFIELD )
u8 ihl :4 ,
version : 4 ;
# e l i f d e f i n e d ( BIG ENDIAN BITFIELD )
u8 version :4 ,
ihl :4;
#e l s e
#e r r o r ” P l e a s e f i x <asm/ b y t e o r d e r . h>”
#e n d i f
u8 tos ;
u16 tot len ;
u16 id ;
PARTE 2. CONOCIMIENTOS BÁSICOS 10

Figura 2.2: IP (Header)

u16 frag off ;


u8 ttl ;
u8 protocol ;
u16 check ;
u32 saddr ;
u32 daddr ;
/∗The o p t i o n s s t a r t h e r e . ∗/
};

ICMP

Figura 2.3: ICMP (Header)


PARTE 2. CONOCIMIENTOS BÁSICOS 11

struct icmphdr
{
u i n t 8 t type ;
/∗ message t y p e ∗/
u i n t 8 t code ;
/∗ t y p e sub−code ∗/
u i n t 1 6 t checksum ;
union
{
struct
{
u int16 t id ;
u i n t 1 6 t sequence ;
} echo ;
/∗ echo datagram ∗/
u int32 t gateway ;
/∗ gateway a d d r e s s ∗/
struct
{
u int16 t unused ;
u i n t 1 6 t mtu ;
} frag ;
/∗ p a t h mtu d i s c o v e r y ∗/
} un ;
};

#define ICMP ECHOREPLY 0


/∗ Echo Reply ∗/
#define ICMP DEST UNREACH 3
/∗ D e s t i n a t i o n U n r e a c h a b l e ∗/
#define ICMP SOURCE QUENCH 4
/∗ Source Quench ∗/
#define ICMP REDIRECT 5
/∗ R e d i r e c t ( change r o u t e ) ∗/
#define ICMP ECHO 8
/∗ Echo R e q u e s t ∗/
#define ICMP TIME EXCEEDED 11
/∗ Time Exceeded ∗/
#define ICMP PARAMETERPROB 12
/∗ Parameter Problem ∗/
#define ICMP TIMESTAMP 13
/∗ Timestamp R e q u e s t ∗/
#define ICMP TIMESTAMPREPLY 14
/∗ Timestamp Reply ∗/
PARTE 2. CONOCIMIENTOS BÁSICOS 12

Figura 2.4: TCP (Header)

#define ICMP INFO REQUEST 15


/∗ I n f o r m a t i o n R e q u e s t ∗/
#define ICMP INFO REPLY 16
/∗ I n f o r m a t i o n Reply ∗/
#define ICMP ADDRESS 17
/∗ Address Mask R e q u e s t ∗/
#define ICMP ADDRESSREPLY 18
/∗ Address Mask Reply ∗/
#define NR ICMP TYPES 18

2.2.4. Capa 4: Transporte


En esta capa, se tienen dos modos de conexión, orientado a conexión y
no orientado a conexión y principalmente hacemos uso de TCP y UDP, estos
protocolos se encargan de dar la fin de secuencia, segmentación y reensamble
de datos al stream de datos, esta es la capa que nos provee la conexión lógica
ente los host, puede o no proveer de circuitos virtuales a la par de corrección
de errores en la transmisión. En este caso el PDU de la capa 4 es el segmento.

TCP

struct t c p h d r {
u short th sport ;
/∗ s o u r c e p o r t ∗/
PARTE 2. CONOCIMIENTOS BÁSICOS 13

u short th dport ;
/∗ d e s t i n a t i o n p o r t ∗/
tcp seq th seq ;
/∗ s e q u e n c e number ∗/
tcp seq th ack ;
/∗ acknowledgement number ∗/
#i f BYTE ORDER == LITTLE ENDIAN
u char th x2 : 4 ,
/∗ ( unused ) ∗/
th off :4;
/∗ d a t a o f f s e t ∗/
#e n d i f
#i f BYTE ORDER == BIG ENDIAN
u char t h o f f : 4 ,
/∗ d a t a o f f s e t ∗/
th x2 : 4 ;
/∗ ( unused ) ∗/
#e n d i f
u char t h f l a g s ;
#d e f i n e TH FIN 0 x01
#d e f i n e TH SYN 0 x02
#d e f i n e TH RST 0 x04
#d e f i n e TH PUSH 0 x08
#d e f i n e TH ACK 0 x10
#d e f i n e TH URG 0 x20
u short th win ;
/∗ window ∗/
u s h o r t th sum ;
/∗ checksum ∗/
u short th urp ;
/∗ u r g e n t p o i n t e r ∗/
};
UDP

struct udphdr
{
u int16 t source ;
u int16 t dest ;
u int16 t len ;
u i n t 1 6 t check ;
};
#endif

#define SOL UDP 17


PARTE 2. CONOCIMIENTOS BÁSICOS 14

Figura 2.5: UDP (Header)

/∗ s o c k o p t l e v e l f o r UDP ∗/

2.2.5. Capa 5: Sesión


Encargada de la separación de datos de diferentes aplicaciones, asi como
del control de diálogo, configuraciones de administración, término de sesiones
entre la capa de presentación y maneja los conceptos de comunicaciones sim-
plex, halfduplex o fullduplex.

2.2.6. Capa 6: Presentación


Presenta los datos, se encarga del cifrado de los datos, además de ser la
encargada de presentar los formatos de codificación, maneja las operaciones
multimedia, la translación de los datos y formato de código, también se en-
carga de la compresión Entre los cuales están (ASCII, EBDIC)

2.2.7. Capa 7: Aplicación


Da una interfaz al usuario, encargado de funciones de archivos, mensaje,
servidores, bases de datos, para los cuales maneja diferentes patrones de co-
municaciones y recursos para la comunicaciones. Entre los principales están
SSH, FTP, HTTP.
PARTE 2. CONOCIMIENTOS BÁSICOS 15

Figura 2.6: Modelo OSI


Parte 3

RAW SOCKET

3.1. Introducción
Cuando nosotros trabajamos con rawsocket, perdemos y ganamos difer-
entes caracterı́sticas, entre las más improtantes destacan:
1. Los sockets raw son un nivel abstracto sobre la trama de red, de he-
cho son más abstractos que los sockets que ya conocemos que son
SOCK STREAM y SOCK DGRAM, al ser estos últimos un caso es-
pecı́fico de los sockets crudos.
2. Al usar sockets crudos, tenemos una pérdida de fiabilidad con respecto
al TCP, ya que no se incluye por defecto al usar socket crudo.
3. No hay puertos; sı́, increı́ble pero cierto, aunque muchas veces dijimos
que un socket = dirección IP + un puerto, ahora desmentimos en parte
eso, ya que en los sockets crudos es el kernel el encargado de pasar
la información de un cierto protocolo a todos los sockets que estén
escuchando el mismo protocolo, no hay conexiones de red virtuales
como tal, es decir, no hay puertos.
4. Comunicaciones sin estándar, ya que uno lo debe de generar, al escribir
un servidor tendremos que escribir el cliente, ya que no es un estándar
y no cualquier programa entenderá lo que nosotros estamos diciendo.
5. Si se hace uso de protocolos ya existentes, se tienen que rellenar los
campos (headers) manualmente, ya que el kernel no lo hace automática-
mente.

16
PARTE 3. RAW SOCKET ?? 17

6. Por último debemos de tener en cuenta que cualquier programa que


haga uso de los sockets crudos, se debe ejecutar con permisos de root.

3.2. Funciones importantes


En este mundo de los sockets y con respecto a la programación de los
sockets crudos tenemos ciertas funciones que nos ayudarán a crearlos y us-
arlos, por lo que pongo un listado de las que considero mas importantes y
algunos detalles o parámetros más usados.

3.2.1. socket
#include <s y s / t y p e s . h>
#include <s y s / s o c k e t . h>

int s o c k e t ( int domain , int type , int p r o t o c o l ) ;

Donde el dominio que se usará es el PF INET, el tipo de socket es


SOCK RAW, y protocol es el protocolo a usar con este socket que se está cre-
ando. Una lista de los protocolos es la siguiente:
enum(
IPPROTO IP = 0 , /∗TCP dummy p r o t o c o l ∗/
IPPROTO ICMP = 0 , /∗ICMP p r o t o c o l ∗/
IPPROTO TCP = 0 , /∗TCP p r o t o c o l ∗/
IPPROTO UDP = 0 , /∗UDP p r o t o c o l ∗/
IPPROTO IPV6 = 0 , /∗ IPv6 p r o t o c o l ∗/
IPPROTO RAW = 0 , /∗RAW p r o t o c o l ∗/
);

3.2.2. sendto
#include <s y s / t y p e s . h>
#include <s y s / s o c k e t . h>

int s e n d t o ( int s , const void ∗msg , int l e n , unsigned int f l a g s ,


const struct s o c k a d d r ∗ to , int t o l e n ) ;

Como primer parámetro se indica el descriptor del socket. El segundo


parámetro es un puntero a los datos que se van a enviar. El tercer parámetro
indica el número de bytes que se van a enviar. En el cuarto parámetro van
las banderas que pueden ser :
PARTE 3. RAW SOCKET ?? 18

1. 0, send funciona igual que write.

2. MSG OOB, indica envı́o de datos urgentes.

3. MSG DONROUTE, hace que se ignoren los mecanismos de enrutamien-


to.

Para el cuarto parámetro se tiene una estructura de tipo sockaddr, que se


completa con la dirección y puerto del destino del mensaje Por último en el
quinto parámetro se envı́a el tamaño de la estrcutura sockaddr

3.2.3. recvfrom
#include <s y s / t y p e s . h>
#include <s y s / s o c k e t . h>

int r e c v f r o m ( int s , void ∗ buf , int l e n , unsigned int f l a g s ,


struct s o c k a d d r ∗ from , int f r o m l e n ) ;

Como primer parámetro se indica el descriptor del socket. El segundo


parámetro es un punteo a donde se van almacenar los datos recibidos. El
tercer parámetro indica el tamano de este buffer. En el cuarto parámetro van
las banderas que pueden ser :
1. MSG PEEK, permite acceso a los datos sin eliminarlos del buffer.

2. MSG OOB, indica recepción de datos urgentes.

3. MSG WAITALL, hace que la operacion de lectura se bloquee hasta el


llenado total del buffer

Para el quinto parámetro se tiene una estrcutura de tipo sockaddr, que nos
permite tener acceso a la información de quien envio el paquete En el sexto
y ultimo parámetro se envia el tamaño de la estructura sockaddr

3.2.4. getsockopt && setsocketopt


Funciones importantes que nos permiten modificar el comportamiento de
un socket.
PARTE 3. RAW SOCKET ?? 19

#include <s y s / t y p e s . h>


#include <s y s / s o c k e t . h>

int g e t s o c k o p t ( int s , int l e v e l , int opname , void ∗ o p t v a l , int


optlen ) ;
int s e t s o c k o p t ( int s , int l e v e l , int opname , const void ∗ o p t v a l ,
int o p t l e n ) ;

Como primer parámetro se indica el descriptor del socket. El segundo


parámetro indica la capa o el nivel de protocolo en la que se hará el cambio
El tercer parámetro indica la opción que queremos modificar En el cuarto
parámetro se indica el valor con el que se desea modificar. Para el quinto
parámetro se tiene el tamaño de la variable referenciada a modificar.

IPPROTO_IP - IP_OPTIONS
IPPROTO_IP - IP_HDRINCL

IPPROTO_TCP - TCP_MAXSEG
IPPROTO_TCP - TCP_NODELAY

SOL_SOCKET - SO_BROADCAST /Envı́o de broadcast


SOL_SOCKET - SO_DEBUG
SOL_SOCKET - SO_DONTROUTE
SOL_SOCKET - SO_ERROR
SOL_SOCKET - SO_KEEPALIVE /Mantener viva la conexión
SOL_SOCKET - SO_LINGER /Envı́o al desconexión
SOL_SOCKET - SO_REUSEADDR / Reuso de dirección
SOL_SOCKET - SO_TYPE / Obtención del tipo de socket
SOL_SOCKET - BINDTODEVICE /Enlazar socket con dispositvo

3.3. Algunas estrcuturas importantes


3.3.1. Estructura ifreq
#include <s y s / i o c t l . h>
#include <n e t / i f . h>

struct i f r e q {
char i f r n a m e [ IFNAMSIZ ] ; /∗ I n t e r f a c e name ∗/
union {
PARTE 3. RAW SOCKET ?? 20

struct sockaddrifr addr ;


struct sockaddrifr dstaddr ;
struct sockaddrifr broadaddr ;
struct sockaddrifr netmask ;
struct sockaddrifr hwaddr ;
short ifr flags ;
int ifr ifindex ;
int ifr metric ;
int ifr mtu ;
struct ifmapifr map ;
char i f r s l a v e [ IFNAMSIZ ] ;
char i f r n e w n a m e [ IFNAMSIZ ] ;
char ∗ ifr data ;
};
};

struct i f c o n f {
int i f c l e n ; /∗ s i z e o f b u f f e r ∗/
union {
char ∗ i f c b u f ; /∗ b u f f e r a d d r e s s ∗/
struct i f r e q ∗ i f c r e q ; /∗ a r r a y o f s t r u c t u r e s ∗/
};
};

Para más información recomiendo esta página


http://linux.about.com/library/cmd/blcmdl7 netdevice.htm
subsectionEstructura sockaddr ll
struct s o c k a d d r l l {
unsigned short s l l f a m i l y ;
/∗ Always AF PACKET ∗/
unsigned short s l l p r o t o c o l ;
/∗ P h y s i c a l l a y e r p r o t o c o l ∗/
int sll ifindex ;
/∗ I n t e r f a c e number ∗/
unsigned short s l l h a t y p e ;
/∗ Header t y p e ∗/
unsigned char sll pkttype ;
/∗ P a ck e t t y p e ∗/
unsigned char sll halen ;
/∗ Length o f a d d r e s s ∗/
unsigned char sll addr [8]; /∗ P h y s i c a l l a y e r a d d r e s s ∗/
};
PARTE 3. RAW SOCKET ?? 21

Figura 3.1: Ejecución captada por IPTraf del código bajado de internet y
como vemos no funciona

3.4. Un ejemplo para empezar


Cuando empecé en este tema de los sockets crudos, me encontré con un
ejemplo el cual considero el dı́a de hoy es muy bueno para poder explicar e
iniciarse con los raw sockets, seguramente personas que hayan estado en con-
tacto con este tema lo han de ver visto en internet, donde muchas personas
ya se consideran hackers XD al correr este ejemplo, pero les tengo una mala
noticia, el ejemplo como tal tiene fallas y si buscan realizar un ataque SYN
FLOOD como mencionan se puede hacer, si tienen un router enfrente fal-
lará lamentablemente, pero en este documento he corregido los errores (TCP
bogus header at least 20 length, Checksum offload, TCP header error offset),
pero vamos lo incluyo, ya que lo considero totalmente útil para el inicio de
este tema.
PARTE 3. RAW SOCKET ?? 22

Lamentablemente al correrlo tendrán que hacer el checksum manualmente


del TCP header (el cual consta de la suma del pseudoheader TCP + header
TCP + datos) al menos que se ocupen los mismos datos que yo manejo en
el ejemplo, ya que si cambian la IP origen o destino se tendrá que volver
a realizar; no incluyo una función que lo realice automaáticamente, ya que
como tal no lo considero necesario para fines de este manual.

#include <s t d i o . h>


#include <s t r i n g . h>
#include<l i n u x / i f e t h e r . h>
/∗ L i b r e rı́ a de l o s s o c k e t s ∗/
#include <s y s / s o c k e t . h>
#include <n e t i n e t / i n . h>
/∗ Para o b t e n e r d a t o s de capa 3 ∗/
#include <n e t i n e t / i p . h>
/∗ Para o b t e n e r d a t o s de capa 4 ∗/
/∗ Usaremos l a e s t r u c t u r a BSD para TCP hdr ∗/
#define FAVOR BSD
#include <n e t i n e t / t c p . h>
#include <u n i s t d . h>

/∗ Puerto a l c u a l s e va a e n v i a r e l p a q u e t e ∗/
#define P 22

int i =0;

/∗ Recordemos que a l no t e n e r e l s t a c k TCP/IP ∗/


/∗ implementado tendremos que h a c e r e l checksum ∗/
/∗ manualmente con ayuda de e s t a f u n c i ó n ∗/
unsigned short csum ( unsigned short ∗ buf , int nwords ) {
unsigned long sum ;
f o r ( sum = 0 ; nwords > 0 ; nwords−−)
sum += ∗ buf++;
sum = ( sum >> 1 6 ) + ( sum & 0 x f f f f ) ;
sum += ( sum >> 1 6 ) ;
return ˜sum ;
}

int main ( int argc , char ∗∗ argv ) {


int one = 1 ;
const int ∗ v a l = &one ;
p r i n t f ( ” I n i c i a m o s e n vı́ o de p a q u e t e s \n” ) ;
/∗ Creamos e l s o c k e t crudo , e l c u a l implementará TCP/IP ∗/
PARTE 3. RAW SOCKET ?? 23

int s = s o c k e t ( PF INET , SOCK RAW, IPPROTO TCP) ;

/∗ Creamos un b u f f e r que c o n t e n d r á l a s c a b e c e r a s , IP y TCP ∗/


/∗ además d e l p a y l o a d ∗/
char datagram [ 4 0 9 6 ] ;

/∗En e s t e c a s o iremos d e l p r o t o c o l o más b a j o a l más a l t o


por l o que empezaremos con IP , a l c r e a r una e s t u c t u r a i p ∗/
struct i p ∗ i p h = ( struct i p ∗ ) datagram ;

/∗ Usaremos una e s t u c t u r a de s o c k e t para e l e n vı́ o de p a q u e t e s


∗/
struct s o c k a d d r i n s i n ;

/∗ Usaremos l a f a m i l i a INET a l t e n e r que s a l i r por l a r e d ∗/


s i n . s i n f a m i l y = AF INET ;

/∗ Se e s c o g e un p u e r t o d e s t i n o a l que s e e n v i a r á n l o s p a q u e t e s
∗/
s i n . s i n p o r t = h t o n s (P) ;

/∗ Escogemos l a IP d e s t i n o de n u e s t r o s p a q u e t e s ∗/
sin . sin addr . s addr = inet addr (” 132.248.59.1 ”) ;

/∗Ponemos e l b u f f e r con l o s h e a d e r s en c e r o ∗/
memset ( datagram , 0 , 4 0 9 6 ) ;

/∗ Hasta e s t a p a r t e podrán p e n s a r que no hemos hecho nada


diferente
ya que s e usa una e s t r u c t u r a s o c k a d d r i n para e l e g i r destino y
todo
p a r e c e s e r l o mismo , p ero ahora tendremos e l c o n t r o l de l o s
campos
de l a s c a b e c e r a s IP y TCP, para e n v i a r l o s por l a r e d ∗/
/∗ Por l o que empezamos a r e l l e n a r l o s manualmente ∗/

/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ HEADER IP ∗/
iph−>i p h l = 5 ;
/∗ Se e s c o g e l a v e r s i ó n de IPv4 ∗/
iph−>i p v = 4 ;
/∗ Se e s c o g e e l Type o f S e r v i c e ∗/
iph−>i p t o s = 0 ;
/∗ Se da l a l o n g i t u d de l a c a b e c e r a IP ∗/
iph−>i p l e n = s i z e o f ( struct i p ) + s i z e o f ( struct t c p h d r ) ;
PARTE 3. RAW SOCKET ?? 24

p r i n t f ( ” Esta e s l a l o n g i t u d i p l e n %d \n” , iph−>i p l e n ) ;


/∗ I d e n t i f i c a d o r d e l p a q u e t e ∗/
iph−>i p i d = h t o n l ( 5 4 3 2 1 ) ;
/∗ Se da e l o f f s e t d e l p a q u e t e ∗/
iph−>i p o f f = 0 ;
/∗ Ponemos e l t i m e t o l i v e ∗/
iph−> i p t t l = 2 5 5 ;
/∗ Se e s o c g e e l p r o t o c o l o a u s a r ∗/
iph−>i p p = 6 ; // 6 a l s e r TCP / e t c / p r o t o c o l
/∗ Momentáneamente pones e l checksum a c e r o ∗/
iph−>ip sum = 0 ;
/∗ Se e s c o g e l a i p o r i g e n , sı́ c l a r o pondremos l a que queramos
XD ∗/
/∗ Pero s i queremos s a l i r por l a red , e l p r o x y ISP no
p e r m i t i r á c u a l q u i e r d i r e c c i ó n en e s t e campo ∗/
iph−>i p s r c . s a d d r = i n e t a d d r ( ” 1 9 2 . 1 6 8 . 1 . 7 0 ” ) ;
/∗ Se d e f i n e l a i p d e s t i n o mediante l a e s t r u c t u r a d e f i n i d a
a n t e r i o r m e n t e ∗/
iph−>i p d s t . s a d d r = s i n . s i n a d d r . s a d d r ;

/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ HEADER TCP ∗/
/∗ Ahora crearemos l a e s t r u t u r a TCP ∗/
struct t c p h d r ∗ tcph = ( struct t c p h d r ∗ ) ( datagram + iph−>
i p h l ∗4 ) ;
p r i n t f ( ” Esta e s l a l o n g i t u d s t r u c t i p %d \n” , s i z e o f ( ∗ i p h ) ) ;
p r i n t f ( ” Esta e s l a l o n g i t u d s t r u c t t c p h d r %d \n” , s i z e o f ( ∗
tcph ) ) ;
/∗ Se da e l p u e r t o o r i g e n d e l p a q u e t e ∗/
tcph−>t h s p o r t = h t o n s ( 4 5 5 2 1 ) ;
/∗ Se da e l p u e r t o d e s t i n o d e l p a q u e t e ∗/
tcph−>t h d p o r t = h t o n s (P) ;
/∗ Se da e l número de s e c u e n c i a d e l p a q u e t e ∗/
tcph−>t h s e q = random ( ) ;
/∗ Número de l a s e c u e n c i a ACK ∗/
tcph−>t h a c k = 0 x00000000 ;
tcph−>t h x 2 = 0 ;
/∗ Se da e l o f f s e t de l a c a b e c e r a TCP ( 5 ∗ 4 ) ∗/
tcph−>t h o f f = 5 ;
/∗ Ponemos para l a p e t i c i ó n de una c o n e x i ó n ∗/
tcph−>t h f l a g s = TH SYN ;
/∗ Indicamos e l tamaño de l a v e n t a n a ∗/
tcph−>t h w i n = 0 x 0 1 8 f ;
/∗ S i ponemos e l checksum TCP a c e r o aunque d i g a n que
e l s t a c k l o r e l l e n a en l o p e r s o n a l no me f u n c i o n ó , por
PARTE 3. RAW SOCKET ?? 25

l o que t u v e que h a c e r e l checksum ∗/


checksum c o r r e c t o ∗/
tcph−>th sum = 0 x 6 0 f a ;
tcph−>t h u r p = 0 ;

/∗ Se da e l checksum d e l p a q u e t e y s e i n c l u y e en e l campo ∗/
iph−>ip sum = csum ( ( unsigned short ∗ ) datagram , iph−>i p l e n
>> 1 ) ;
p r i n t f ( ”Checksum i p %x \n” , iph−>ip sum ) ;

/∗ Al u s a r IP HDRINCL l e decimos que estamos


i n c l u y e n d o e l h e a d e r IP en e l p a q u e t e ∗/
i f ( s e t s o c k o p t ( s , IPPROTO IP , IP HDRINCL , val , s i z e o f ( one )
) < 0)
p r i n t f ( ” E r r o r : No s e m o d i f i c ó e l s o c k e t \n” ) ;
while ( i !=10) {
i ++;
i f ( s e n d t o ( s , datagram , ( iph−>i p l e n ) , 0 , ( struct s o c k a d d r ∗ )
&s i n , s i z e o f ( s i n ) ) < 0 )
p r i n t f ( ” Paquete no s e ha podido e n v i a r \n” ) ;
else
p r i n t f ( ” Paquete e n v i a d o \n” ) ;
}

return 0 ;
}

root@koitoerdp:~/Tutorial raw-sockets# gcc injection.c -o injection


root@koitoerdp:~/Tutorial raw-sockets# ./injection
Iniciamos envı́o de paquetes
Esta es la longitud ip_len 40
Esta es la longitud struct ip 20
Esta es la longitud struct tcphdr 20
Checksum ip 1294
Paquete enviado
Paquete enviado
Paquete enviado
Paquete enviado
Paquete enviado
PARTE 3. RAW SOCKET ?? 26

Paquete enviado
Paquete enviado
Paquete enviado
Paquete enviado
Paquete enviado
root@koitoerdp:~/Tutorial raw-sockets#

En este caso vemos que al lanzar la ejecución se mandan paquetes TCP


SYNC, para el inicio del HANDSHAKE, al puerto 22 de una máquina remota,
la cual nos reponde con un SYNC+ACK, por las diez veces que mandamos
ese paquete, pero si nos fijamos en la imagén del sniffer, nuestra máquina la
192.168.1.70, envı́a un RST, que es un Reset Connection al no haber proceso
alguno que se encarge de los paquetes recién recibidos, ya que como tal sólo
mandamos los diez SYNC, nunca esperamos respuesta.
Como vemos, es un programa que nos permite poner directamente los
campos de los headers, TCP e IP, con lo que corroboramos que podemos
tener el control de los protocolos ya existentes, en este caso debemos de
tener en cuenta que si quisieramos atacar algun host, cambiando nuestra IP,
para que no nos detecten, muchas veces nuestro ISP, no permitirá que este
tipo de peticiones salgan a internet, debido a que el mismo reconoce que no
son propias de su configuración, aunque si estudiamos un poco el tipo de
direcciones IP, que maneja esto si será posible.
PARTE 3. RAW SOCKET ?? 27

Figura 3.2: Ejecución captada por Wireshark


Parte 4

Un sniffer para comenzar

Después de programar un poco, tenemos listo un sniffer, que nos permite


analizar la información contenida en ciertos protocolos, en este caso si se
desea el análisis de otro protocolo, simplemente tendremos que realizar una
inclusión del desglose del protocolo en el lugar correcto como lo hare mas
adelante.

4.1. SNIFFER
Primero que nada,¿qué es un sniffer?, bueno es un programa de captura
de tramas, que se usa con diversos fines, como pueden ser el análisis de
protocolos, la gestión de las redes, fines maliciosos XD, análisis de fallos,
detección de posibles ataques o intrusos, o como simple medidor de tráfico.

4.1.1. PROMISC MODE ??


Es una configuración de la tarjeta de red, la cual nos permite pasar a nivel
de aplicación todos los paquetes recibidos sean o no para nosotros, por lo que
podremos escuchar todo el tráfico que llegue a nuestra tarjeta, que pudiese
ser mı́nimo si hablamos de una computadora o host, pero puede llegar a ser
ciertamente alto si activamos este modo en un gateway de salida de una LAN,
o en medio de una red WI-FI. Para activar este modo de la tarjeta debemos
de tener permisos de administrador.

root@koitoerdp:~# ifconfig eth0 promisc


root@koitoerdp:~# ifconfig

28
PARTE 4. SNIFFER 29

eth0 Link encap:Ethernet HWaddr 00:40:f4:a5:8c:5a


inet addr:192.168.1.70 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::240:f4ff:fea5:8c5a/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:2511 errors:0 dropped:0 overruns:0 frame:0
TX packets:1328 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:238408 (238.4 KB) TX bytes:218862 (218.8 KB)
Interrupt:19 Base address:0xc400

Como se aprecia la palabra PROMISC aparece marcando la interfaz XD.


Para quitarla del modo promiscuo, sólo la reiniciamos,para esto debemos ten-
er cuidado ya que si estamos conectados remotamente al reiniciar la interfaz,
nos desconectará.
Para la detección de tarjetas en modo promiscuo dentro de nuestra red,
tenemos varias opciones, que se dividen en dos tipos, el primero checar man-
ualmente cada tarjeta de red para ver si está en modo promiscuo, suena difı́cil
verdad, bueno entonces podemos crear un demonio el cual nos permita saber
cuando una tarjeta cambie a modo promiscuo, esto simplemente lo haremos
leyendo el /var/log/message, donde queda huella cuando una interfaz hace
uso de este modo

May 2 17:02:00 koitoerdp kernel: [17157.272110]


device lo entered promiscuous mode
May 2 17:02:04 koitoerdp kernel: [17161.113065]
device lo left promiscuous mode
May 2 17:11:16 koitoerdp kernel: [17712.772099]
device eth0 entered promiscuous mode
May 2 17:11:56 koitoerdp kernel: [17752.564435]
device eth0 left promiscuous mode

Pero vamos si lo borra, lo cual es difı́cil al menos que se tenga la contraseña


de root, pues sólo nos queda hacer uso de diversas técnicas para encontrar
tarjetas en modo promiscuo, que pueden ser por medio del envı́o de paquetes
ARP o el uso de antisniffers como Antisnif o Sentinel. Para esto también
encontré en internet, un buen programa que se llama neped.c, que se encarga
de la identificación de un host con su tarjeta en modo promiscuo a través
del simple envı́o de paquetes ARP a máquinas inexistentes. Considero que
PARTE 4. SNIFFER 30

echarle un vistazo al código puede ser en este momento un tanto dı́ficil, pero
al final de este tutorial puede ser bastante didáctico, para ver y corroborar
lo aprendido.

4.2. ksniffer
Muy bien, es hora de programar un sniffer sencillo, que capture el tràfico
TCP que pasa por nuestra interfaz, entonces empecemos.
#include <s t d i o . h>
#include < f c n t l . h>
#include <s y s / s o c k e t . h>
#include <r e s o l v . h>
#include <netdb . h>
#include <n e t i n e t / i n . h>
#include <n e t i n e t / t c p . h>
#include <n e t i n e t / i p . h>

int main ( ) {
/∗ Creamos e l s o c k e t crudo , e l c u a l s ó l o va a
c a p t u r a r e l t r á f i c o de p a q u e t e s TCP, como l o
i n d i c a l a o p c i ó n IPPROTO TCP ∗/
int f d = s o c k e t ( PF INET , SOCK RAW, IPPROTO TCP) ;
/∗ Será n u e s t r o b u f f e r de c a p t u r a ∗/
char b u f f e r [ 8 1 9 2 ] ;
int i =0 ;
int b y t e s = 0 ;
/∗ Entramos a un c i c l o i n f i n i t o que nos p e r m i t e
i r c a p t u r a n d o y mostrando e l t r á f i c o , en e s t e
c a s o usamos read , ya que estamos t r a b a j a n d o con
e l p r o t o c o l o TCP ∗/
while ( ( b y t e s = r e a d ( fd , b u f f e r , 8 1 9 2 ) ) > 0 ) {
/∗ Una v e z c a p t u r a d o l o mostramos a im pr es ión , e s t o l o
hacemos s e ñ a l a n d o donde i n i c i a e s t a i n f o r m a c i ó n , l o c u a l
e x p l i c a r é un poco más a d e l a n t e ∗/
p r i n t f ( ” Paquete c a p t u r a d o %s \n” , b u f f e r+s i z e o f ( struct
i p h d r )+s i z e o f ( struct t c p h d r ) ) ;
p r i n t f ( ” Contenido : \n” ) ;
p r i n t f ( ” Hexadecimal \n” ) ;
f o r ( i = 0 ; i < b y t e s ; i ++)
p r i n t f ( ” %x” , b u f f e r [ i ] ) ;
p r i n t f ( ” \ n E n t e n d i b l e \n” ) ;
f o r ( i = 0 ; i < b y t e s ; i ++)
p r i n t f ( ” %c ” , b u f f e r [ i ] ) ;
PARTE 4. SNIFFER 31

p r i n t f ( ”\ n S i g u i e n t e
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− \n” ) ;
}

4.2.1. Compilación y Ejecución


gcc Sniffer.c -o SnifferSimple
./SnifferSimple

Paquete capturado
Contenido :
Hexadecimal
450028ffffffb616400ffffff806ffffffc0ffffffddffffffc0
ffffffa8145ffffffc0ffffffa81468ffffffb3016ffffffc3d6
e2affffff831dffffffbbffffffa65010fffffffa4fffffffb8f
fffffe300
Entendible
[CARACTERES OMITIDOS]
Siguiente --------------------------------------

Si como lo podemos apreciar ya tenemos nuestro gran sniffer, pero me


dirán que no se entiende nada jajaja, pues claro sólo estamos capturando los
paquetes jamás los ordenamos y obtenemos los datos de ellos, asi que esa
será nuestra siguiente tarea, lo que cabe resaltar en este ejemplo es el uso de
las estructuras que previamente habiamos platicado.

4.3. Sniffer mejorado.


Algo que tenemos que recordar, que es tan simple, pero a la vez es lo
más importante al momento de obtener la información útil en un sniffer, es
la forma en la que se está recibiendo y enviando la información, recordemos
que tenemos capas del modelo OSI, por lo que tenemos en la red un paquete,
que por decirlo de alguna manera esta empaquetado, esto se ve como se
muestra en la siguiente figura
Entonces como vemos en la figura lo primero que llega siempre es el
Header de la capa de enlace, despues viene el NH (Network header) header
PARTE 4. SNIFFER 32

Figura 4.1: Empaquetado en modelo OSI

de red, después el TH (transport header) header de la capa de transporte,


después los header de sesión, presentación y por último vienen los datos que
contiene. Y si es ası́ como llega, entonces será la forma que nosotros usaremos
para ir leyendo y ordenando los datos, es importante tener en cuenta que
recibiremos con recvfrom(), función que previamente vimos que nos permite
almacenar en un buffer, por lo que la información almacenada en ese buffer,
será rescatable a través de un apuntador.
Ahora para decirle al apuntador de donde a donde llega la información
de cierto header, lo que ocuparemos serán las estructuras antes vistas, de los
diferentes headers.
Haciendo una analogı́a, tenemos un buffer de tamaño X, la estructura
que describe el header de la capa de enlace supongamos que son Y bytes,
obviamente si el paquete recibido es válido X tiene que ser mayor que Y,
entonces diremos que los Y primeros bytes de X corresponden a la capa de
enlace. Después sabemos que en la capa de red el protocolo usado tiene un
header de P bytes, ¿dónde terminarı́a este header de la capa de red? , bueno
pues la respuesta es sencilla, si el header de red es de P bytes, primero que
nada empezariı́a en (Y + 1) bytes a partir del inicio del paquete, y por lo
PARTE 4. SNIFFER 33

tanto tendrı́a su fin en ( Y + P ) bytes. En este caso ¿cómo sabemos cuánto


valdrı́a X, Y, P y demás ?, pues con ayuda de la función sizeof() sobre las
estructuras que sabemos que se manejan en las diferentes capas del modelo
OSI.
Recordemos que en un arreglo de caracteres por ejemplo char mybuffer[1000],
el inicio se puede indicar con mybuffer, o &mybuffer[0], con lo que apuntamos
a la primera posición de todo el buffer, entonces si queremos movernos para
captar el siguiente header o la información lo hacemos de la siguiente forma:
buffer + sizeof( struct ethhdr) +sizeof( struct iphdr) + sizeof(struct tcph-
dr)
Tenemos que en este caso primero apuntamos al inicio del paquete después
movemos el apuntador n lugares dependiendo de la suma del header ethernet
+ header ip + header tcp, por lo cual estaremos al inicio de la información que
era nuestro objetivo, obviamente si leyeron bien, no podemos decir del todo
que es la información como tal, ya que siguen diferentes headers y después
ahora si va la información.
Entonces sabiendo esto sólo les recordaré una cosa, nosotros cuando creamos
un socket crudo podemos definirle que protocolo usaremos, si le ponemos IP-
PROTO TCP, no nos llegará el header ethernet como en el primer ejemplo
paso, pero tampoco nos llegará información UDP, ARP, ICMP, RARP y
demás, entonces recordemos lo que mencione antes, que el kernel se encarga
de subir la información a los sockets que esten escuchando cierto tipo de pro-
tocolo, asi que si estamos escuchando solo tráfico TCP pues no recibiremos
mas que eso, pero no se preocupen si queremos estudiar todos los protocolos
y todo el tráfico sólo pondremos ETH P ALL, y lo haremos XD , pero re-
cuerden que si usamos esta constante recibiremos TODO !!!! el tráfico, por
lo que nuestro sniffer capturará mucha información,y difı́cilmente podremos
encontrar o ver lo que necesitamos, por lo que el uso de esta constante lo
dejo a su criterio. Aunque cabe mencionar que más adelante en este tutorial,
se hará uso de la constante para que les enseñe la forma de recibir protocolos
de capa 3 y 4.
Muy bien, creo que ha sido una buena introducción, asi que vamos al
código.

/∗ Programa s n i f f e r 1 . c ∗/
/∗ Compilado en Linux k o i t o e r s v 2.6.27 −14 − g e n e r i c i 6 8 6 GNU/ Linux
∗/
/∗ #1 SMP Wed Apr 15 1 8 : 5 9 : 1 6 UTC 2009 ∗/
PARTE 4. SNIFFER 34

#include<s t d i o . h>
#include<s t r i n g . h>
#include<s t d l i b . h>
/∗ L i b r e rı́ a de l o s s o c k e t s ∗/
#include<s y s / s o c k e t . h>
#include<f e a t u r e s . h>
#include<l i n u x / i f p a c k e t . h>
#include<e r r n o . h>
/∗ L i b r e rı́ a de l o s s o c k e t s ∗/
#include<s y s / i o c t l . h>
/∗ L i b r e rı́ a de c o n t r o l de i n t e r f a c e s ∗/
#include<n e t / i f . h>
/∗ L i b r e rı́ a de l o s p a q u e t e s e t h e r n e t ∗/
#include<l i n u x / i f e t h e r . h>
/∗ L i b r e rı́ a de l o s p a q u e t e s i p ∗/
#include<l i n u x / i p . h>
/∗ L i b r e rı́ a de l o s p a q u e t e s t c p ∗/
#include<l i n u x / t c p . h>
/∗ L i b r e rı́ a de d e f i n i c i o n e s de p r o t o c o l o s ∗/
#include<n e t i n e t / i n . h>
/∗ L i b r e rı́ a para d e c o d i f i c a r u n s i g n e d char ∗/
#include<c t y p e . h>

void h e a d e r ( ) ;
int obtenerData ( unsigned char ∗ , int ) ;
int obtenerHTCP ( unsigned char ∗ , int ) ;
void obtenerHIP ( unsigned char ∗ , int ) ;
void o b t e n e r H E t h e r n e t ( unsigned char ∗ , int ) ;
int s o c k e t i f ( char ∗ , int , int ) ;
void impresionHex ( unsigned char ∗ , int ) ;
void impresionASCII ( unsigned char ∗ , int ) ;
void impresionFormatoIP ( unsigned char ∗ , int ) ;

int main ( int argc , char ∗∗ argv ) {


/∗ Nuestro f u t u r o r a w s o c k e t ∗/
int r s o c k ;
int l e n ;
/∗ Número de p a q u e t e s a r e c i b i r ∗/
int p a q u e t e s ;
/∗ Bandera para l a i m p r e s i ó n o no de d a t o s ∗/
int v a l i d a c i o n =0;
int p a c k e t = 0 ;
/∗ B u f f e r de l e c t u r a de l a i n f o r m a c i ó n r e c i b i d a ∗/
PARTE 4. SNIFFER 35

unsigned char p b u f f e r [ 2 0 4 8 ] ;
/∗ E s t r u c t u r a que l i g a r á e l s o c k e t con l a i n t e r f a z ∗/
struct s o c k a d d r l l p i n f o ;
/∗ Definimos tamano d e l b u f f e r ∗/
int p s i z e = s i z e o f ( p i n f o ) ;
i f ( a r g c !=3) {
p r i n t f ( ”Uso %s <i n t e r f a z > <n o p a q u t e s > \n” , argv [ 0 ] ) ;
e x i t ( −1) ;
}
/∗ Se c r e a un s o c k e t crudo para p r o t o c o l o IP ∗/
r s o c k = c r e a S o c k e t ( ETH P IP ) ;
/∗ Se i n i c i a e l p r o c e s o ∗/
header ( ) ;
/∗ Se une e l s o c k e t a un i n t e r f a z de l a máquina ∗/
s o c k e t i f ( argv [ 1 ] , r s o c k , ETH P IP ) ;
/∗ Decimos e l número de p a q u e t e s a c a p t u r a r ∗/
p a q u e t e s = a t o i ( argv [ 2 ] ) ;
/∗ Empezamos l a c a p t u r a de n número de p a q u e t e s ∗/
while ( paquetes −−){
/∗ Recibimos p a q u e t e s a t r a v é s d e l s o c k e t crudo d e l
p r o t o c o l o IP ∗/
/∗ Y l o s pasamos l o s d a t o s a l b u f f e r , además de su
i n f o r m a c i ó n a l a e s t r u c t u r a p i n f o ∗/
i f ( ( l e n = r e c v f r o m ( r s o c k , p b u f f e r , 2 0 4 8 , 0 , ( struct
s o c k a d d r ∗ )&p i n f o , &p s i z e ) ) == −1){
p e r r o r ( ”Ha habido un e r r o r en l a c a p t u r a ” ) ;
e x i t ( −1) ;
} else {
p r i n t f ( ”−−−−−−−− Paquete Capturado No . %d −−−−−−−−−−−− \n”
, p a c k e t++) ;
/∗ Una v e z c a p t u r a d o obtendremos l a i n f o r m a c i o n ∗/
/∗ Obtenemos e l h e a d e r e t h e r n e t capa 2 ∗/
obtenerHEthernet ( p b u f f e r , len ) ;
/∗ Obtenemos e l h e a d e r i p capa 3 ∗/
obtenerHIP ( p b u f f e r , l e n ) ;
/∗ Obtenemos e l h e a d e r t c p capa 4 ∗/
v a l i d a c i o n = obtenerHTCP ( p b u f f e r , l e n ) ;
i f ( v a l i d a c i o n == 1 )
obtenerData ( p b u f f e r , l e n ) ;
p r i n t f ( ”−−−−−−−− END Paquete Capturado No . %d −−−−−−−−− \n
\n” , p a q u e t e s ) ;
} // f i n d e l e l s e
bzero ( p buffer , sizeof ( p b u f f e r ) ) ;
} // f i n d e l w h i l e
PARTE 4. SNIFFER 36

return 0 ;
}

int c r e a S o c k e t ( int p r o t o c o l ) {
int r s o c k e t ;
i f ( ( r s o c k e t = s o c k e t (PF PACKET, SOCK RAW, h t o n s ( p r o t o c o l ) ) )==
−1){
p e r r o r ( ” E r r o r a l c r e a r e l s o c k e t crudo ” ) ;
e x i t ( −1) ;
}
return r s o c k e t ;
}

void h e a d e r ( ) {
p r i n t f ( ” S n i f f e r ! \n” ) ;
p r i n t f ( ”−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−\n” ) ;
}

int s o c k e t i f ( char ∗ i n t e r f a z , int r s o c k , int p r o t o c o l ) {


/∗ Ahora en v e z de l i g a r e l s o c k e t con un p u e r t o ∗/
/∗ La l i g a r e m o s con una i n t e r f a z ∗/
struct s o c k a d d r l l s l l ;
struct i f r e q i f r ;
b z e r o (& s l l , s i z e o f ( s l l ) ) ;
/∗ Limpiamos l a e s t r u c t u r a de d a t o s de l a i n t e r f a z ∗/
b z e r o (& i f r , s i z e o f ( i f r ) ) ;
strcpy ( i f r . ifr name , i n t e r f a z ) ;
/∗ Cargamos d a t o s de l a i n t e r f a z a l a e s t r u c t u r a i f r ∗/
i f ( ( i o c t l ( r s o c k , SIOCGIFINDEX , & i f r ) ) == −1){
p r i n t f ( ”No s e puede o b t e n e r l a i n t e r f a z ! \ n” ) ;
e x i t ( −1) ;
}
/∗ I m p r e s i ó n de p r o p i e d a d e s de l a i n t e r f a z ∗/
/∗ Obtenemos l a s f l a g s de l a i n t e r f a z ∗/
i f ( ( i o c t l ( r s o c k , SIOCGIFFLAGS,& i f r ) ) < 0 ) {
p e r r o r ( ” E r r o r : Al o b t e n e r b a n d e r a s de l a
interfaz ”) ;
}

int f l a g s =0;
flags = ifr . ifr flags ;
PARTE 4. SNIFFER 37

/∗ Checamos s i l a i n t e r f a z e s t á a c t i v a ∗/
i f ( ( f l a g s & IFF UP ) != 0 )
p r i n t f ( ” D i s p o s i t i v o a r r i b a [UP] \n” ) ;
else {
p r i n t f ( ” El d i s p o s i t i v o no e x i s t e no e s t á a r r i b a \n ” ) ;
e x i t ( −1) ;
}

/∗ Checamos s i l a i n t e r f a z e s t á en modo promisuo ∗/


i f ( ( f l a g s & IFF PROMISC) != 0 )
p r i n t f ( ” D i s p o s i t i v o en modo Promiscuo \n” ) ;

p r i n t f ( ”La i n t e r f a z de c a p t u r a e s %s \n” , i n t e r f a z ) ;

/∗ Obtenemos l a d i r e c c i o n MAC ∗/
i f ( ( i o c t l ( r s o c k ,SIOCGIFHWADDR,& i f r ) ) < 0 ) {
p e r r o r ( ” E r r o r : Al o b t e n e r MAC a d d r e s s ” ) ;
e x i t ( −1) ;
}
p r i n t f ( ”La MAC de l a i n t e r f a z e s : ” ) ;
impresionHex ( i f r . i f r h w a d d r . s a d a t a , 6 ) ;

/∗ Obtenemos l a d i r e c c i o n IP ∗/
i f ( ( i o c t l ( r s o c k , SIOCGIFADDR,& i f r ) ) < 0 ) {
p e r r o r ( ” E r r o r : Al o b t e n e r d i r e c c i ó n IP ” ) ;
e x i t ( −1) ;
}
p r i n t f ( ”La D i r e c c i ó n IP de l a i n t e r f a z e s : ” ) ;
impresionFormatoIP (& i f r . i f r a d d r . s a d a t a [ 2 ] , 4 ) ;

/∗ Obtenemos l a máscara de r e d ∗/
i f ( ( i o c t l ( r s o c k , SIOCGIFNETMASK,& i f r ) ) < 0 ) {
p e r r o r ( ” E r r o r : Al o b t e n e r l a máscara de r e d ” ) ;
e x i t ( −1) ;
}
p r i n t f ( ”La Máscara de r e d de l a i n t e r f a z e s : ” ) ;
impresionFormatoIP (& i f r . i f r n e t m a s k . s a d a t a [ 2 ] , 4 ) ;

/∗ Obtenemos l a d i r e c c i ó n de b r o a d c a s t ∗/
i f ( ( i o c t l ( r s o c k ,SIOCGIFBRDADDR,& i f r ) ) < 0 ) {
p e r r o r ( ” E r r o r : Al o b t e n e r l a d i r e c c i ó n de
broadcast ” ) ;
e x i t ( −1) ;
}
p r i n t f ( ”La d i r e c c i ó n de b r o a d c a s t de l a i n t e r f a z e s : ”
PARTE 4. SNIFFER 38

);
impresionFormatoIP (& i f r . i f r b r o a d a d d r . s a d a t a [ 2 ] , 4 ) ;

/∗ Obtenemos e l ı́ n d i c e de l a i n t e r f a z ∗/
i f ( ( i o c t l ( r s o c k , SIOCGIFINDEX , & i f r ) ) == −1){
p r i n t f ( ”No s e puede o b t e n e r l a i n t e r f a z ! \ n” ) ;
e x i t ( −1) ;
}

/∗ Siempre AF PACKET ∗/
s l l . s l l f a m i l y = AF PACKET;
/∗ Definimos e l ı́ n d i c e a l c u a l s e l i g a r á e l s o c k e t ∗/
sll . sll ifindex = ifr . ifr ifindex ;
/∗ Definimos e l p r o t o c o l o que s e manejará ∗/
s l l . s l l p r o t o c o l = htons ( p r o t o c o l ) ;
/∗ Ligamos e l s o c k e t a l a i n t e r f a z ∗/
i f ( ( bind ( r s o c k , ( struct s o c k a d d r ∗ )&s l l , s i z e o f ( s l l ) ) )== −1){
p e r r o r ( ” E r r o r a l u n i r e l s o c k e t con l a i n t e r f a z \n” ) ;
e x i t ( −1) ;
}

p r i n t f ( ” \n−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−” ) ;
return 1 ;
}

void impresionHex ( unsigned char ∗p , int num) {


while (num−−){
p r i n t f ( ” %.2X ” , ∗p ) ;
p++;
}
p r i n t f ( ” \n” ) ;
}

void impresionASCII ( unsigned char ∗p , int num) {


while (num−−){
p r i n t f ( ” %c ” , t o a s c i i ( ∗ p ) ) ;
p++;
}
p r i n t f ( ” \n” ) ;
}

void impresionFormatoIP ( unsigned char ∗p , int num) {


while (num−−){
p r i n t f ( ” %d” , ∗p ) ;
PARTE 4. SNIFFER 39

i f (num!=0)
printf (” . ”) ;
p++;
}
p r i n t f ( ” \n” ) ;
}

void o b t e n e r H E t h e r n e t ( unsigned char ∗ packet , int l e n ) {


/∗ Creamos una e s t r u c t u r a de t i p o e t h h d r ∗/
/∗ para v a c i a r l o s d a t o s y c a s t e a r l o s ∗/
struct e t h h d r ∗ h e t h e r n e t ;
/∗ Hacemos e l ordenamiento s i e m p r e y cuando tengamos
s u f i c i e n t e s b y t e s ∗/
i f ( l e n > s i z e o f ( struct e t h h d r ) ) {
h e t h e r n e t = ( struct e t h h d r ∗ ) p a c k e t ;
/∗ Ahora obtenemos l o s d a t o s i m p o r t a n t e s que s e rı́ a n ∗/
/∗ MAC ORIGEN,MAC DESTINO y PROTOCOLO DE RED∗/
p r i n t f ( ” P r o t o c o l o de r e d : ” ) ;
impresionHex ( ( void ∗ )&h e t h e r n e t −>h p r o t o , 2 ) ;
p r i n t f ( ”MAC D e s t i n o : ” ) ;
impresionHex ( h e t h e r n e t −>h d e s t , 6 ) ;
p r i n t f ( ”MAC Origen : ” ) ;
impresionHex ( h e t h e r n e t −>h s o u r c e , 6 ) ;
} else
p r i n t f ( ”HETHERNET : Paquete de tamaño i n s u f i c i e n t e \n” ) ;

void obtenerHIP ( unsigned char ∗ packet , int l e n ) {


struct e t h h d r ∗ h e t h e r n e t ;
struct i p h d r ∗ h i p ;

h e t h e r n e t = ( struct e t h h d r ∗ ) p a c k e t ;
/∗ Confirmamos que s e t r a t a da un p a q u e t e IP ∗/
/∗ r e v i s a n d o e l campo h p r o t o de l a e s t r u c t u r a ∗/
/∗ e t h h d r ∗/

i f ( n t o h s ( h e t h e r n e t −>h p r o t o ) == ETH P IP ) {
/∗ Confirmamos l a e x i s t e n c i a de s u f i c i e n t e s b y t e s ∗/
i f ( l e n >= ( s i z e o f ( struct e t h h d r ) + s i z e o f ( struct i p h d r ) ) ) {
h i p = ( struct i p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) ) ;
p r i n t f ( ” IP D e s t i n o : %s \n” , ( char ∗ ) i n e t n t o a ( h i p −>daddr ) )
;
PARTE 4. SNIFFER 40

p r i n t f ( ” IP Origen : %s \n” , ( char ∗ ) i n e t n t o a ( h i p −>s a d d r ) )


;
} else
p r i n t f ( ”HIP : Paquete de tamaño i n s u f i c i e n t e \n” ) ;
} else
p r i n t f ( ” Este no e s un paquete IP \n ” ) ;
}

int obtenerHTCP ( unsigned char ∗ packet , int l e n ) {


struct e t h h d r ∗ h e t h e r n e t ;
struct i p h d r ∗ h i p ;
struct t c p h d r ∗ h t c p ;

/∗ V e r i f i c a m o s que haya s u f i c i e n t e s b y t e s para c a s t e a r l o s


h e a d e r s ∗/
i f ( l e n >= ( s i z e o f ( struct e t h h d r ) + s i z e o f ( struct i p h d r ) +
s i z e o f ( struct t c p h d r ) ) ) {
h e t h e r n e t = ( struct e t h h d r ∗ ) p a c k e t ;
/∗ V e r i f i c a m o s s e t r a t e de un p a q u e t e IP ∗/
i f ( n t o h s ( h e t h e r n e t −>h p r o t o ) == ETH P IP ) {
h i p = ( struct i p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) ) ;
/∗ V e r i f i c a m o s s e t r a t a d e l p r o t o c o l o TCP ∗/
i f ( h i p −>p r o t o c o l == IPPROTO TCP) {
/∗ Apuntamos a l a c a b e c e r a t c p ∗/
h t c p = ( struct t c p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r )
+ h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h tc p −>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h tc p −>d e s t ) ) ;
return 1 ;
} else
p r i n t f ( ”No e s un paquete TCP\n” ) ;
} else
p r i n t f ( ”No e s un paquete IP \n” ) ;
} else
p r i n t f ( ”HTCP : Paquete de tamaño i n s u f i c i e n t e \n” ) ;
return 0 ;
}

int obtenerData ( unsigned char ∗ packet , int l e n ) {


struct e t h h d r ∗ h e t h e r n e t ;
struct i p h d r ∗ h i p ;
PARTE 4. SNIFFER 41

struct t c p h d r ∗ h t c p ;
unsigned char ∗ data ;
int d a t a l e n ;

i f ( l e n > ( s i z e o f ( struct e t h h d r ) + s i z e o f ( struct i p h d r ) +


s i z e o f ( struct t c p h d r ) ) ) {
/∗ Apuntamos y casteamos a l h e a d e r i p ∗/
h i p = ( struct i p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) ) ;
/∗ Apuntamos a l i n i c i o de l a p a r t e de l o s d a t o s ∗/
data = ( p a c k e t + s i z e o f ( struct e t h h d r ) + s i z e o f ( struct i p h d r
) + s i z e o f ( struct t c p h d r ) ) ;
/∗La l o n g i t u d de d a t o s e s l a l o n g i t u d de t o d o e l p a q u e t e ip ,
menos e l
tamaño d e l h e a d e r i p y h e a d e r t c p ∗/
d a t a l e n = n t o h s ( h i p −>t o t l e n ) − h i p −>i h l ∗4 − s i z e o f (
struct t c p h d r ) ;
i f ( data len ){
p r i n t f ( ” Longitud de d a t o s : %d\n” , d a t a l e n ) ;
p r i n t f ( ” Datos : \n” ) ;
impresionHex ( data , d a t a l e n ) ;
p r i n t f ( ” \n” ) ;
return 1 ;
} else {
p r i n t f ( ”No hay d a t o s en l o s p a q u e t e s \n” ) ;
return 0 ;
}
} else {
p r i n t f ( ”DATOS : Paquete de tamaño i n s u f i c i e n t e \n” ) ;
return 0 ;
}
}

Como vermos tenemos funciones llamada obtener que nos permiten sacar
la información de las diferentes capas, aunque todo esto se pudo haber hecho
en una sola función creo que está más claro al separarlo en varias. Aho-
ra bien, hemos identificado algunos protocolos, y desglosado algunos de sus
campos, por lo que ahora, tendremos que aumentarle funcionalidad y modos
de captura, que será el tema de la siguiente parte.
Parte 5

Implantar Funcionalidades

5.1. Modificaciones
Como mencioné antes, ahora se le implementarán algunas funcionalidades
extras, para que quede más clara la forma de trabajo de un sniffer un poco
más completo; en este caso, debemos recordar que la documentación de cada
protocolo se haya en un RFC, el cual tendrémos que leer y entender si nece-
sitamos un análisis de un cierto protocolo especı́ficamente. En las siguientes
subsecciones sólo pongo la modificación del código que se hizo, no pongo to-
do el sniffer, ya que como tal ocupa muchas hojas, pero cada modificación
la pueden hallar en un programa sniffer#.c, que incluyo a descarga en mi
página.

5.1.1. Añadimos UDP


Ahora ya tenemos sniffer2.c que nos permite recibir solo tráfico TCP, por
lo que haremos una modificación para poder entender el protocolo UDP

i f ( h i p −>p r o t o c o l == IPPROTO TCP) {


p r i n t f ( ” P r o t o c o l o TCP \n” ) ;
h t c p = ( struct t c p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) +
h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h tc p −>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h tc p −>d e s t ) ) ;
return 1 ;
/∗ Se i n c l u y e n e s t a s lı́ n e a s , para c a p t u r a y d e s g l o s e de UDP
∗/

42
PARTE 5. IMPLANTAR FUNCIONALIDADES 43

} e l s e i f ( h i p −>p r o t o c o l == IPPROTO UDP) {


p r i n t f ( ” P r o t o c o l o UDP \n” ) ;
h udp = ( struct udphdr ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) +
h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h udp−>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h udp−>d e s t ) ) ;
return 1 ;
}

5.1.2. Añadimos ICMP


Bien ya tenemos el tráfico TCP-UDP, ahora en sniffer3.c haremos la mod-
ificación para captar ICMP y distinguir algunos tipos de mensajes, en este
punto conviene que veamos la siguiente imagen, para observar en qué parte
de la trama se haya la información correspondiente al protocolo ICMP.

i f ( h i p −>p r o t o c o l == IPPROTO TCP) {


p r i n t f ( ” P r o t o c o l o TCP \n” ) ;
h t c p = ( struct t c p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) +
h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h tc p −>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h tc p −>d e s t ) ) ;
return 1 ;
} e l s e i f ( h i p −>p r o t o c o l == IPPROTO UDP) {
p r i n t f ( ” P r o t o c o l o UDP \n” ) ;
h udp = ( struct udphdr ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) +
h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h udp−>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h udp−>d e s t ) ) ;
return 1 ;
} e l s e i f ( h i p −>p r o t o c o l == IPPROTO ICMP) {
p r i n t f ( ” P r o t o c o l o ICMP \n” ) ;
icmp = ( struct icmphdr ∗ ) ( p a c k e t + h i p −>i h l ∗ 4 ) ;
i f ( icmp−>type == ICMP INFO REQUEST )
p r i n t f ( ” ICMP p e t i c i ó n d e s d e %s \n” , ( char ∗ ) i n e t n t o a (
h i p −>s a d d r ) ) ;
e l s e i f ( icmp−>type == ICMP ECHO )
p r i n t f ( ” ICMP echo d e s d e %s \n” , ( char ∗ ) i n e t n t o a ( h i p −>
daddr ) ) ;
e l s e i f ( icmp−>type == ICMP ECHOREPLY )
p r i n t f ( ” ICMP e c h o r e p l y d e s d e %s \n” , ( char ∗ ) i n e t n t o a (
h i p −>daddr ) ) ;
e l s e i f ( icmp−>type == ICMP DEST UNREACH )
PARTE 5. IMPLANTAR FUNCIONALIDADES 44

Figura 5.1: Header ICMP

p r i n t f ( ” ICMP d e s t i n o i n a l c a n z a b l e d e s d e %s \n” , ( char ∗ )


i n e t n t o a ( h i p −>s a d d r ) ) ;
} else
p r i n t f ( ”No e s un paquete TCP\n” ) ;

5.1.3. Añadimos DHCP


Sigamos, démosle la funcionalidad de captar mensajes DHCP, basándonos
en la estructura que maneja bind9 (DHCP server), para la mensajerı́a de
DHCP. Por lo que la pongo antes de todo el código fuente, aunque sé que
serı́a mejor y convendrı́a ponerla en un archivo .h, para liberar un poco el
código fuente, no lo hago debido a que quiero tener todo en un mismo archivo.

/∗ Encabezados , e s t r u c t u r a s y v a r i a b l e s para e l manejo de DHCP


∗/
/∗ O b t e n i d o s d e l programa b i n d 9 ∗/

#define DHCP UDP OVERHEAD ( 2 0 + /∗ IP h e a d e r ∗/


\
8) /∗ UDP h e a d e r ∗/
#define DHCP SNAME LEN 64
#define DHCP FILE LEN 128
#define DHCP FIXED NON UDP 236
#define DHCP FIXED LEN (DHCP FIXED NON UDP +
DHCP UDP OVERHEAD)
/∗ E v e r y t h i n g
but options .
∗/
#define BOOTP MIN LEN 300

#define DHCP MTU MAX 1500


PARTE 5. IMPLANTAR FUNCIONALIDADES 45

#define DHCP MTU MIN 576

#define DHCP MAX OPTION LEN (DHCP MTU MAX − DHCP FIXED LEN)
#define DHCP MIN OPTION LEN (DHCP MTU MIN − DHCP FIXED LEN)

struct d h c p p a c k e t {
u i n t 8 t op ; /∗ 0 : Message opcode / t y p e ∗/
u i n t 8 t htype ; /∗ 1 : Hardware addr t y p e ( n e t /
i f t y p e s . h ) ∗/
u i n t 8 t hlen ; /∗ 2 : Hardware addr l e n g t h ∗/
u i n t 8 t hops ; /∗ 3 : Number o f r e l a y a g e n t hops
from c l i e n t ∗/
u i n t 3 2 t xid ; /∗ 4 : T r a n s a c t i o n ID ∗/
u int16 t secs ; /∗ 8 : Seconds s i n c e c l i e n t
s t a r t e d l o o k i n g ∗/
u int16 t flags ; /∗ 1 0 : F l a g b i t s ∗/
struct i n a d d r c i a d d r ; /∗ 1 2 : C l i e n t IP a d d r e s s ( i f
a l r e a d y i n use ) ∗/
struct i n a d d r y i a d d r ; /∗ 1 6 : C l i e n t IP a d d r e s s ∗/
struct i n a d d r s i a d d r ; /∗ 1 8 : IP a d d r e s s o f n e x t s e r v e r
t o t a l k t o ∗/
struct i n a d d r g i a d d r ; /∗ 2 0 : DHCP r e l a y a g e n t IP
a d d r e s s ∗/
unsigned char chaddr [ 1 6 ] ; /∗ 2 4 : C l i e n t hardware
a d d r e s s ∗/
char sname [DHCP SNAME LEN ] ; /∗ 4 0 : S e r v e r name ∗/
char f i l e [ DHCP FILE LEN ] ; /∗ 1 0 4 : Boot f i l e n a m e ∗/
unsigned char o p t i o n s [ DHCP MAX OPTION LEN ] ;
/∗ 2 1 2 : O p t i o n a l p a r a m e t e r s
( a c t u a l l e n g t h d e p e n d e n t on MTU) . ∗/
};

u i n t 3 2 t sequencedhcp = 0 x0000 ;
int dhcphase = 0 ;

/∗ Código f u e n t e de i m p l e m e n t a c i ó n de DHCP en n u e s t r o s n i f f e r ∗/

e l s e i f ( h i p −>p r o t o c o l == IPPROTO UDP) {


p r i n t f ( ” P r o t o c o l o UDP \n” ) ;
h udp = ( struct udphdr ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) +
h i p −>i h l ∗4 ) ;
p r i n t f ( ” S o u r c e Port : %d\n” , n t o h s ( h udp−>s o u r c e ) ) ;
p r i n t f ( ” Dest Port : %d\n” , n t o h s ( h udp−>d e s t ) ) ;
PARTE 5. IMPLANTAR FUNCIONALIDADES 46

i f ( n t o h s ( h udp−>d e s t ) == 67 | | n t o h s ( h udp−>s o u r c e ) == 67 ) {
p r i n t f ( ” Mensajes DHCP \n” ) ;
dhcp = ( struct d h c p p a c k e t ∗ ) ( p a c k e t + s i z e o f ( struct
e t h h d r ) + h i p −>i h l ∗4 + s i z e o f ( struct udphdr ) ) ;
p r i n t f ( ”TRANSACTION ID %x \n” , dhcp−>x i d ) ;
i f ( dhcp−>op == 1 && sequencedhcp == dhcp−>x i d )
p r i n t f ( ”DHCP REQUEST \n ” ) ;
e l s e i f ( dhcp−>op == 1 ) {
p r i n t f ( ”DHCP DISCOVER \n” ) ;
sequencedhcp = dhcp−>x i d ;
p r i n t f ( ” Este e s e l p r o c e s o %x \n” , dhcp−>x i d ) ;
} e l s e i f ( dhcp−>op == 2 && dhcphase !=0) {
p r i n t f ( ”DHCP ACK \n” ) ;
sequencedhcp = 0 x00000000 ;
dhcphase = 0 ;
} e l s e i f ( dhcp−>op == 2 ) {
p r i n t f ( ”DHCP OFFER \n” ) ;
dhcphase ++ ;
}
}
return 1 ;

Corriendo el programa sniffer3, y haciendo en otra terminal un dhclient,


que como sabemos hace peticiones para encontrar un servidor DHCP, esto
para que pueda enviarme parámetros, para la configuración de la red. Pode-
mos ver la existencia de un transaction ID, que es como un identificador de
proceso, que nos permite saber a qué ventana o proceso redirigir la salida de
la petición DHCP, además de permitir como tal tener la secuencia ligada a
un proceso, secuencia que consta de (DHCP DISCOVER, DHCP OFFER,
DHCP REQUEST, DHCP ACK).

root@koitoerdp:~/Tutorial raw-sockets# ./sniffer3 eth0 1000


Sniffer !
-----------------------------------------------
Dispositivo arriba [UP]
La interfaz de captura es eth0
La MAC de la interfaz es : 00 40 F4 A5 8C 5A
La Dirección IP de la interfaz es : 192.168.1.70
La Máscara de red de la interfaz es : 255.255.255.0
La dirección de broadcast de la interfaz es : 192.168.1.255

---------------------------- Paquete Capturado No. 0 ------------


PARTE 5. IMPLANTAR FUNCIONALIDADES 47

Protocolo de red : 08 00
MAC Destino : FF FF FF FF FF FF
MAC Origen : 00 40 F4 A5 8C 5A
IP Destino: 255.255.255.255
IP Origen : 0.0.0.0
Protocolo UDP
Source Port: 68
Dest Port: 67
Mensajes DHCP
TRANSACTION ID 395dc602
DHCP DISCOVER
Este es el proceso 395dc602
Longitud de datos : 288
Datos :
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 F4 A5 8C 5A
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 63 82 53 63 35 01 01 37 07 01 1C 02 03 0F 06 0C FF 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00

-------- END Paquete Capturado No. 999 ---------

-------- Paquete Capturado No. 1 ------------


Protocolo de red : 08 00
MAC Destino : FF FF FF FF FF FF
MAC Origen : 00 23 51 07 09 B9
IP Destino: 255.255.255.255
IP Origen : 192.168.1.254
Protocolo UDP
PARTE 5. IMPLANTAR FUNCIONALIDADES 48

Source Port: 67
Dest Port: 68
Mensajes DHCP
TRANSACTION ID 395dc602
DHCP OFFER
Longitud de datos : 288
Datos :
00 00 00 00 C0 A8 01 46 C0 A8 01 FE 00 00 00 00 00 40 F4 A5 8C 5A
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 63 82 53 63 35 01 02 36 04 C0 A8 01 FE 33 04 00 01 51
80 3A 04 00 00 A8 C0 3B 04 00 01 27 50 06 04 C0 A8 01 FE 03 04 C0
A8 01 FE 01 04 FF FF FF 00 FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00

-------- END Paquete Capturado No. 998 ---------

-------- Paquete Capturado No. 2 ------------


Protocolo de red : 08 00
MAC Destino : FF FF FF FF FF FF
MAC Origen : 00 40 F4 A5 8C 5A
IP Destino: 255.255.255.255
IP Origen : 0.0.0.0
Protocolo UDP
Source Port: 68
Dest Port: 67
Mensajes DHCP
TRANSACTION ID 395dc602
DHCP REQUEST
Longitud de datos : 288
Datos :
PARTE 5. IMPLANTAR FUNCIONALIDADES 49

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 F4 A5 8C 5A
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 63 82 53 63 35 01 03 36 04 C0 A8 01 FE 32 04 C0 A8 01
46 37 07 01 1C 02 03 0F 06 0C FF 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00

-------- END Paquete Capturado No. 997 ---------

-------- Paquete Capturado No. 3 ------------


Protocolo de red : 08 00
MAC Destino : 00 40 F4 A5 8C 5A
MAC Origen : 00 23 51 07 09 B9
IP Destino: 192.168.1.70
IP Origen : 192.168.1.254
Protocolo UDP
Source Port: 67
Dest Port: 68
Mensajes DHCP
TRANSACTION ID 395dc602
DHCP ACK
Longitud de datos : 293
Datos :
00 00 00 00 C0 A8 01 46 C0 A8 01 FE 00 00 00 00 00 40 F4 A5 8C 5A
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
PARTE 5. IMPLANTAR FUNCIONALIDADES 50

Figura 5.2: Ejecucion del sniffer, Protocolo DHCP

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 63 82 53 63 35 01 05 36 04 C0 A8 01 FE 33 04 00 01 51
80 3A 04 00 00 A8 C0 3B 04 00 01 27 50 06 04 C0 A8 01 FE 03 04 C0
A8 01 FE 01 04 FF FF FF 00 0F 11 67 61 74 65 77 61 79 2E 32 77 69
72 65 2E 6E 65 74 FF

-------- END Paquete Capturado No. 996 ---------


PARTE 5. IMPLANTAR FUNCIONALIDADES 51

5.1.4. Añadimos ARP


Por último, como hacer para poder implementar algo que no sea tan
estándar o una modificación nuestra, hay estructuras muy complejas que
realmente podemos o no ocuparlas del todo como fue el caso de DHCP,
que sólo manejamos algunos campos para este ejemplo, pero que pasa si
no queremos respetar las estructuras estándar, pues simple y sencillamente
recordamos que sólo tenemos que apuntar al lugar correcto y además rescatar
u obtener los datos, con un tipo de dato igual con el cual fueron insertados,
para no tener problemas de .agarrar”más bytes o no .agarrar”todos los bytes.
Por lo que se plantea ahora el caso de ARP, al que le hice una modificación
para entender el tráfico ARP, basándonos en el principio que les comento de
sólo saber de que posición sacar la información y que cantidad de información
sacar.

/∗ D e f i n i c i o n e s de ARP ∗/
#define IPALEN 4 /∗ Length i n b y t e s o f an IP a d d r e s s ∗/
#define MAXHWALEN 5 /∗ Maximum l e n g t h o f a hardware a d d r e s s ∗/

enum arp hwtype {


ARP NETROM=0x0000 , /∗ Fake f o r NET/ROM ( n e v e r a c t u a l l y
s e n t ) ∗/
ARP ETHER, /∗ Assigned t o 10 m e g a b i t E t h e r n e t ∗/
ARP EETHER, /∗ Assigned t o e x p e r i m e n t a l E t h e r n e t ∗/
ARP AX25 , /∗ Assigned t o AX. 2 5 L e v e l 2 ∗/
ARP PRONET, /∗ Assigned t o PROnet t o k e n r i n g ∗/
ARP CHAOS, /∗ Assigned t o Chaosnet ∗/
ARP IEEE802 , /∗ Who u s e s t h i s ? ∗/
ARP ARCNET,
ARP APPLETALK
};

enum a r p o p c o d e {
ARP REQUEST=0x0001 ,
ARP REPLY,
REVARP REQUEST,
REVARP REPLY
};

struct arp {
enum arp hwtype hardware ; /∗ Hardware t y p e ∗/
u int16 t protocol ; /∗ P r o t o c o l t y p e ∗/
PARTE 5. IMPLANTAR FUNCIONALIDADES 52

u i n t 1 6 t hwalen ; /∗ Hardware a d d r e s s l e n g t h , b y t e s ∗/
enum a r p o p c o d e opcode ; /∗ ARP opcode ( r e q u e s t / r e p l y ) ∗/
u i n t 8 t shwaddr [MAXHWALEN] ; /∗ Sender hardware a d d r e s s f i e l d
∗/
};

/∗ Fin de d e f i n i c i o n e s de ARP ∗/

/∗ Código f u e n t e de i m p l e m e n t a c i ó n de ARP en n u e s t r o s n i f f e r ∗/

h e t h e r n e t = ( struct e t h h d r ∗ ) p a c k e t ;
i f ( n t o h s ( h e t h e r n e t −>h p r o t o ) == ETH P IP ) {
i f ( l e n >= ( s i z e o f ( struct e t h h d r ) + s i z e o f ( struct i p h d r ) ) ) {
h i p = ( struct i p h d r ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) ) ;
p r i n t f ( ” IP D e s t i n o : %s \n” , ( char ∗ ) i n e t n t o a ( h i p −>daddr ) )
;
p r i n t f ( ” IP Origen : %s \n” , ( char ∗ ) i n e t n t o a ( h i p −>s a d d r ) )
;
} else
p r i n t f ( ”HIP : Paquete de tamano i n s u f i c i e n t e \n” ) ;
} e l s e i f ( n t o h s ( h e t h e r n e t −>h p r o t o ) == ETH P ARP) {
h a r p = ( struct arp ∗ ) ( p a c k e t + s i z e o f ( struct e t h h d r ) ) ;
i f ( n t o h s ( h arp−>hardware ) == 0 x0001 )
p r i n t f ( ” \tARP ETHERNET \n” ) ;
p r i n t f ( ” \ t S e n d e r MAC ADDRESS : ” ) ;
impresionHex ( h arp−>shwaddr −4 ,6) ;
p r i n t f ( ” \ t T a r g e t MAC ADDRESS : ” ) ;
impresionHex ( h arp−>shwaddr +6 ,6) ;
p r i n t f ( ” \ t S e n d e r IP ADDRESS : ” ) ;
impresionFormatoIP ( h arp−>shwaddr +2 ,4) ;
p r i n t f ( ” \ t T a r g e t IP ADDRESS : ” ) ;
impresionFormatoIP ( h arp−>shwaddr +12 ,4) ;
} else {
p r i n t f ( ” Este no e s un paquete que s e t e n g a c o n o c i m i e n t o \n ”
);
p r i n t f ( ” P r o t o c o l o %d ” , n t o h s ( h e t h e r n e t −>h p r o t o ) ) ;
}

-------- Paquete Capturado No. 4 ------------


Protocolo de red : 08 06
MAC Destino : FF FF FF FF FF FF
MAC Origen : 00 23 51 07 09 B9
PARTE 5. IMPLANTAR FUNCIONALIDADES 53

Figura 5.3: Ejecución del sniffer, Protocolo ARP

ARP ETHERNET
Sender MAC ADDRESS :00 23 51 07 09 B9
Target MAC ADDRESS :FF FF FF FF FF FF
Sender IP ADDRESS :192.168.1.254
Target IP ADDRESS :192.168.1.64
No es un paquete IP
-------- END Paquete Capturado No. 995 ---------
Parte 6

Conclusión e ideas finales

6.1. Problemas principales


En este caso es indispensable saber el buen uso de estructuras, arreglos y
apuntadores, para entender perfectamente los programas, considero que en
la compilación y demás no existe ninguna problemática a mencionar.

6.2. Tutoriales futuros


Pues no se, por ahora no tengo idea de cual será el siguiente aunque
tengo temas interesante para realizar algún tutorial, podrı́a ser de JNI para
trabajar C con Java, Sockets Seguros en Java o Sockets de C comunicandose
con Sockets de Java, o implementaciones de sistemas, o un pequeño tuto de
ncurses para crear un chat con todo lo aprendido. Pero en fin ya veré qué se
me ocurre, por el momento me retiraré un poco hasta encontrar un buen
tema y arreglar cosas pendientes.

6.3. Ideas Finales


Se que hay varios sniffers y muy potentes, pero recordando, la idea princi-
pal de este tutorial era el cómo poder programar y usar los sockets crudos. A
alguien que desee realizar un sniffer mas complejo, le recomiendo las librerı́as
pcap y/o libnet que tienen muchas funciones que nos permiten brincarnos la

54
PARTE 6. CONCLUSIÓN 55

parte tediosa que vimos en este manual, y pasamos rápidamente a la imple-


mentación de aplicaciones con sockets crudos.
Espero también disculpen mis faltas de ortografı́a y mi redacción, si en
algún momento consideran que está mal. Cualquier aclaración, duda o co-
mentario me pueden contactar a este correo. ((koitoer.server@gmail.com))

Los códigos del tutorial están en esta página:


http://www.koitoer.com/tutoriales/socketraw.tar.gz

Este tutorial fue creado en LATEX.

6.4. Agradecimientos
Pues serı́an para las personas que me hicieron llegar sus comentarios sobre
el primer tutorial que hice y para mi marthita querida XD