Ahora verás como está construido el código, los puntos tratados en este simple código son:
- Crear La cabecera Ethernet.
- Crear La cabecera ARP.
- Transpasar ambos a una sola cadena.
- Enviar los datos por la red.
Según como viste las definiciones en los puntos ARP Poison 1 y 2, ahora serán utilizadas dichas estructuras para asignarles los campos que queramos para que sean enviados por la red, primero echale un vistazo al código y luego iré comentando linea a linea.
#include "Crossover/Crossover.h"
using namespace Crossover::Framework::Net::Protocols;
using namespace Crossover::Framework::Net;
using namespace std;
#define HL 6 // Hardware len
#define PL 4 // Protocol len
int main(){
try
{
//Creando Cabecera ETHERNET
struct Ethernet tEthernet;
memset(&tEthernet,0,sizeof(tEthernet));
memcpy(tEthernet.dst, "\xFF\xFF\xFF\xFF\xFF\xFF", HL);
memcpy(tEthernet.src, "\xAA\xAA\xAA\xAA\xAA\xAA", HL);
tEthernet.type = htons( ETHERNET_TYPE_ARP);
//Creando Cabecera de ARP
struct ARP tARPheader;
memset(&tARPheader, 0, sizeof(tARPheader));
//ETHERNET_TYPE_ETHER -> Ethernet.h
tARPheader.hrd = htons( ETHERNET_TYPE_ETHER);
//ETHERNET_TYPE_IP -> Ethernet.h
tARPheader.pro = htons( ETHERNET_TYPE_IP);
tARPheader.hln = HL;
tARPheader.pln = PL;
tARPheader.op = htons( ARP_OP_REQUEST);
//Asignando la ip de origen
unsigned char szDestSourceIP[4];
char szSourceIP[80];
strcpy( szSourceIP, "2.2.2.2");
Converter::hip2mip( szSourceIP , szDestSourceIP);
memcpy(tARPheader.sha, "\xAA\xAA\xAA\xAA\xAA\xAA", HL);
memcpy(tARPheader.spa, szDestSourceIP, PL);
//Asignando la ip de destino
unsigned char szDestTargetIP[4];
char szTargetIP[80];
strcpy( szTargetIP, "1.1.1.1");
Converter::hip2mip( szTargetIP, szDestTargetIP);
memcpy(tARPheader.tha, "\x00\x00\x00\x00\x00\x00", HL);
memcpy(tARPheader.tpa, szDestTargetIP, PL);
Wrapcap oWrapcap;
oWrapcap.open();
char *szBuff = (char *) malloc(1024);
memcpy(szBuff, (char*) &tEthernet, sizeof(tEthernet));
memcpy(&szBuff [ sizeof(tEthernet)], (char*) &tARPheader
, sizeof(tARPheader));
oWrapcap.send((u_char*) szBuff, sizeof(tEthernet) + sizeof(tARPheader));
// while(1){
// oWrapcap.send((u_char*) szBuff, sizeof(tEthernet) + sizeof(tARPheader));
// Sleep(1000);
// }
}
catch(Exception ex)
{
cout << ex.message() << " OS: " << ex.systemMessage() << endl;
}
return 0;
}
#include "Crossover/Crossover.h"
Esta es la referencia a todas las cabeceras necesarias para crear el código, que apuntan incluso al framework, que es un conjunto de clases, creadas para hacer más optimo y mantenible el código
NOTA: Si estas muy perdido, puedes descargarte los papers de iniciación a la programación en C++, creados para los foros de UNDERC0DE y continuados por el equipo de desarrollo Exseption.
using namespace Crossover::Framework::Net::Protocols;
using namespace Crossover::Framework::Net;
using namespace std;
Estos son los nombres de espacios, te fijastes que las estructuras Ethernet y ARP están contenidas en el espacio de nombre Crossover::Framework::Net::Protocols, en el espacio de nombre namespace Crossover::Framework::Net, está contenida la clase Wrapcap, que es la encargada de enviar el paquete, ya te comentaré de esta, el nombre de espacio std ya sabes para que es, ¿no sabes?, puedes apoyarte de los papers o puedes investigar más con tu buscador preferido.
La siguiente imagen es la estructura de directorios donde están organizadas las clases.
#define HL 6 // Hardware len
#define PL 4 // Protocol len
Estas lineas (macros) definen los largos para el campo de mac (Hardware Len) y el campo de ipv4 (Protocol Len)
//Creando Cabecera ETHERNET
struct Ethernet tEthernet;
memset(&tEthernet,0,sizeof(tEthernet));
memcpy(tEthernet.dst, "\xFF\xFF\xFF\xFF\xFF\xFF", HL);
memcpy(tEthernet.src, "\xAA\xAA\xAA\xAA\xAA\xAA", HL);
tEthernet.type = htons( ETHERNET_TYPE_ARP);
En las siguientes lineas se define la cabecera Ethernet, la estructura Ethernet está definida
en el archivo de cabecera "Ethernet.h", ubicado en el directorio Crossover/Framework/Net/Protocols
, el nombre de espacio en el cual se encuentra es la misma ruta de directorios donde se aloja.
En la linea "
memset(&tEthernet,0,sizeof(tEthernet));" se vacía la estructura a valores cero,
ya que por defecto en C/C++ los valores asignados son valores basura, aunque no debería afectar su
resultado si es que se asignaran todos los campos de la estructura. (Si no conoces el prototipo de memset
o sizeof, es hora de que te pongas a investigar gooleando o por otro que conozcas.
Luego en "memcpy(tEthernet.dst, "\xFF\xFF\xFF\xFF\xFF\xFF", HL);" se asigna la mac de destino, es preciso que te familiarices con esta notación, te servirá para interpretar los paquetes tanto con un sniffer como en la misma depuración.
hasta el momento llevamos la mac de destino el paquete quedaría de esta manera
FF FF FF FF FF FF 00 00 00 00 00 00 00 00
la cabecera ethernet solo esta compuesta de 14 bytes, los 6 primeros bytes representan la mac de destino
La siguiente linea "
memcpy(tEthernet.src, "\xAA\xAA\xAA\xAA\xAA\xAA", HL);" asignará los 6 próximos
bytes quedando asi:
FF FF FF FF FF FF AA AA AA AA AA AA 00 00
tEthernet.type = htons( ETHERNET_TYPE_ARP); esta linea asignará el código del siguiente protocolo,
para que el núcleo al desempaquetar sepa como obtener los campos del protocolo superior ARP,
o si no podría confundirse. htons( ) es una función definida en el archivo <netinet/in.h> para usuarios
linux, y en <Winsock2.h> para usuarios windows, el objetivo de esta función es dejar los bytes en el orden
de la red, solo invierte los bytes 13 y 14 cambiándolos de orden, si no se hiciese esto, los bytes irían
en un orden que no entendería luego el OS.
la constante ETHERNET_TYPE_ARP está definida en el archivo header "Ethernet.h" y su valor es el siguiente: #define ETHERNET_TYPE_ARP 0x0806
FF FF FF FF FF FF AA AA AA AA AA AA 08 06
Si no se utilizara htons (host to network short) los bytes irían como:
FF FF FF FF FF FF AA AA AA AA AA AA 06 08 (malo)
//Creando Cabecera de ARP
struct ARP tARPheader;
memset(&tARPheader, 0, sizeof(tARPheader));
//Ethernet.h
tARPheader.hrd = htons( ETHERNET_TYPE_ETHER);
//Ethernet.h
tARPheader.pro = htons( ETHERNET_TYPE_IP);
tARPheader.hln = HL;
tARPheader.pln = PL;
tARPheader.op = htons( ARP_OP_REQUEST);
//Asignando la ip de origen
unsigned char szDestSourceIP[4];
char szSourceIP[80];
strcpy( szSourceIP, "2.2.2.2");
Converter::hip2mip( szSourceIP , szDestSourceIP);
memcpy(tARPheader.sha, "\xAA\xAA\xAA\xAA\xAA\xAA", HL);
memcpy(tARPheader.spa, szDestSourceIP, PL);
//Asignando la ip de destino
unsigned char szDestTargetIP[4];
char szTargetIP[80];
strcpy( szTargetIP, "1.1.1.1");
Converter::hip2mip( szTargetIP, szDestTargetIP);
memcpy(tARPheader.tha, "\x00\x00\x00\x00\x00\x00", HL);
memcpy(tARPheader.tpa, szDestTargetIP, PL);
Luego comienza la creación de la cabecera
ARP, que es la parte fundamental de la célula, la
cabecera ARP esta definida en el archivo ARP.h.
memset(&tARPheader, 0, sizeof(tARPheader)); en esta línea se hace lo mismo que el anterior.
El paquete estaría quedando como sigue:
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
tARPheader.hrd = htons( ETHERNET_TYPE_ETHER); idem
El paquete por ahora estaría definido de la siguietne manera
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
tARPheader.pro = htons( ETHERNET_TYPE_IP);
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
tARPheader.hln = HL; header len.
tARPheader.pln = PL; protocol len
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 06 04 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
tARPheader.op = htons( ARP_OP_REQUEST); esta indica que será una solicitud.
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 06 04 00 01
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
//Asignando la ip de origen
unsigned char szDestSourceIP[4]; En esta linea se asignará la ip destino, solo se requieren
4 bytes para asignar la ipv4.
char szSourceIP[80]; Aquí es donde se almacenará la ip en formato decimal.
strcpy( szSourceIP, "2.2.2.2"); Se asigna "2.2.2.2" a szSourceIP
Converter::hip2mip( szSourceIP , szDestSourceIP); Esta función definida en el archivo de cabecera
"Converter.h" (ver imagen de arbol de direcctorios), traspasa la ip en formato legible (Numeración decimal)
al formato maquina, asignando la ip "2.2.2.2" solo en 4 bytes.
memcpy(tARPheader.sha, "\xAA\xAA\xAA\xAA\xAA\xAA", HL); Mac de origen, directamente a 6 bytes gracias a las secuencias de escape \x.
memcpy(tARPheader.spa, szDestSourceIP, PL); ip de origen
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 06 04 00 01
AA AA AA AA AA AA 02 02 02 02 00 00 00 00 00 00 00 00 00 00
//Asignando la ip de destino
unsigned char szDestTargetIP[4];
char szTargetIP[80];
strcpy( szTargetIP, "1.1.1.1");
Converter::hip2mip( szTargetIP, szDestTargetIP);
memcpy(tARPheader.tha, "\x00\x00\x00\x00\x00\x00", HL);
memcpy(tARPheader.tpa, szDestTargetIP, PL);
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 06 04 00 01
AA AA AA AA AA AA 02 02 02 02 00 00 00 00 00 00 01 01 01 01
Con esto ya se ha completado el paquete, ahora queda enviarlo por la red, sigamoscon el código.
La siguiente linea
"Wrapcap oWrapcap;" es la clase que envuelve la funcionalidad de la API pcap, pcap es un conjunto de funciones o interfaces que facilitan la manipulación de paquetes que viajan por la red, esta API es usada para captura de paquetes tanto como envío de paquetes, la ventaja de esta API es que es multiplataforma, se ha envuelto esta API para facilitar su lectura y simplificar el código en partes, además de separar la complejidad de tratamiento de esta en simples llamadas a métodos.
La linea
"oWrapcap.open();" se encarga de obtener un dispositivo por defecto para poder luego enviar o capturar paquetes por la red, en este caso se enviará un paquete de solicitud por la red como forma de prueba.
Las siguientes 3 lineas traspasarán las estructuras Ethernet y ARP a una secuencia de bytes del tipo char.
Se realiza una reserva de espacio de 1024 bytes.
char *szBuff = (char *) malloc(1024);
Luego a szBuff se le asigna todos los campos de la estructura tEthernet
memcpy(szBuff, (char*) &tEthernet, sizeof(tEthernet));
En este momento la cadena szBuff tendría los siguientes bytes:
FF FF FF FF FF FF AA AA AA AA AA AA 08 06
Después en la cadena szBuff se avanzan los bytes necesarios para llegar al último y uno más después de la cabecera tEthernet si no se avanza se remplazarían bytes de la cabecera tEthernet, de esta forma
szBuff[total de bytes de cabecera ethernet] avanzamos justo una posicion mas del termino de la cabecera y luego se le pasa la dirección de memoria con el operador
&, para que desde este punto se asigne la cabecera ARP.
memcpy(&szBuff [ sizeof(tEthernet)], (char*) &tARPheader , sizeof(tARPheader));
Ahora la cadena szBuff contiene:
FF FF FF FF FF FF AA AA AA AA AA AA 08 06 00 01 08 00 06 04 00 01
AA AA AA AA AA AA 02 02 02 02 00 00 00 00 00 00 01 01 01 01
Finalmente se envía el paquete por la red con el método send de la clase Wrapcap, que como primer parámetro recibe un tipo
u_char* (unsigned char *, declarado en pcap/pcap.h), y luego el total de bytes a transmitir que sería la suma de bytes de ambas cabeceras, en total se enviarían 14 bytes de cabecera ethernet y 28 para ARP, puedes luego optimizar el código para que la cadena szBuff no requiera tanto peso como 1024 bytes que no fue necesario utilizar.
oWrapcap.send((u_char*) szBuff, sizeof(tEthernet) + sizeof(tARPheader));
Puntos finales sobre el termino de esta entrada, luego publicaré el código completo para que puedas desentrañar su funcionamiento y entenderlo, también explicaré como probar su funcionamiento con un sniffer tshark en este caso, si te atrapa la ansiedad puedes probar el código inmediatamente con cualquier sniffer si es que te manejas con otro.
Un punto que se me escapaba, la parte del
try catch es una estructura de control de errores, si dentro del bloque try se produjera cualquier error, este saltaría al bloque catch, que dentro de los operadores ( ) esta la declaración de
Exception exception es una clase que contiene el detalle del error tanto de alguna clase como del mismo detalle del error del sistema operativo, también podrás ver su código y entenderlo.
Si un punto les parece difícil me comentan o me envían un mail, para explicarlo a mejor entender.
Aquí el link de descarga del codigo:
http://arp-poison.googlecode.com/files/Wrapcap_v1.tar
Por ahora esta solo funcionando para linux, aunque puede que funcione en windows, para compilarlo en linux solo tienen que poner make en la consola, es necesario instalar la libreria pcap, por lo tanto deberás instalar esta antes de compilar con el comando make.