MTProd > Dev4all > Articles > Programmation > Réseau > Programmation Winsock avancée avec les systèmes NT > 5 Forger ses propres paquets
Rechercher13 Personnes en-ligne
Programmation Winsock avancée avec les systèmes NT

5  Forger ses propres paquets



5.1  Paquets ICMP

Maintenant que nous sachons à quoi ressemble la structure d'un paquet ICMP ou encore celle du protocol IP, nous pouvons nous permettre de forger notre propre paquet grâce à un programme en C. Je vais donc ici vous montrer un simple exemple qui permettra d'envoyer une requète ICMP echo request à une machine présente sur le réseau.
Tout d'abord nous devons initialisé l'API Winsock dans sa version 2 contrairement à précedemment, pour se faire, rien de complexe, nous avons juste à remplacer les paramètres passés à la macro MAKEWORD.

Ce qui donnera : MAKEWORD(2, 2)

Ensuite nous devons comme d'habitude créer notre socket mais cette fois ci nous n'allons pas juste nous contenter d'un socket stream mais plutôt d'un raw socket. Pour créer celui ci nous allons appeler une fonction de l'API Winsock 2, cette fonction se nomme WSASocket().
Etudions de suite ensemble son prototype :

prototype :

SOCKET WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags);


Comme vous pouvez le constater cette fonction nous renvois un descripteur sur le socket créé, le premier paramètre correspond à la famille du socket, le second correspond au type du socket, le troisième permet de connaitre le protocol utilisé pour ainsi savoir à quelle famille d'adresse celui-ci correspond, le quatrième définie une structure de type LPWSAPROTOCOL_INFO permettant de spécifier les caractéristiques du socket créé, le sixième paramètre est réservé à Windows, nous ne en n'occupons pas, quand au dernier paramètre il indique les attributs de notre socket. En cas d'échec cette fonction renvoie une erreur du type INVALID_SOCKET, si vous voulez obtenir précisemment le code de sortie vous pouvez utiliser la fonction WSAGetLastError().

Maintenant passons à un exemple :

SOCKET socket;
socket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0,0);
if (socket == INVALID_SOCKET)
{
    fprintf(stderr, "erreur lors de la création du raw socket : %d\n", WSAGetLastError());
    WSACleanup();
    exit(-1);
}


Comme vous pouvez observer ici nous passons en second paramètre SOCK_RAW ce qui permet de créer un socket de type raw, ensuite nous passons au paramètre suivant la valeur IPPROTO_RAW, celle ci permet de spécifier que nous utilisons le protocol IP mais toujours en mode raw.

Après avoir initialisé Winsock, créé notre raw socket, il nous reste encore une chose à faire, spécifier les bonnes options pour notre socket, pour ceci une fonction éxiste, toujours appartenant à l'API Winsock, cette fonction ne nomme : setsockopt();
étudions la de suite avant de l'utiliser.

prototype :

int setsockopt(SOCKET s, int level, int optname, const char FAR * optval, int optlen);


Nous passons directement au deuxième argument qui représente sur quel niveau vont ètre définie les options (SOL_SOCKET, IPPROTO_TCP, et IPPROTO_IP), les paramètres optval et optlen sont utilisés pour déterminer les options pour setsockopt, optlen doit contenir la taille du buffer pointé par optval, si aucune option n'est fournie ou renvoyée, optval peut être NULL. En cas de succès cette fonction renvois 0, dans le cas contraire celle-ci renvoie SOCKET_ERROR.
Maintenant servons nous de cette fonction pour définir les bonnes options à notre socket :

int optval = 1;

if (setsockopt(socket, IPPROTO_IP, 2, (char *)&optval, sizeof(optval)) == SOCKET_ERROR)
{
fprintf(stderr, "erreur lors de l'appel à setsockopt : %d\n", WSAGetLastError());
WSACleanup();
exit(-1);
}


A partir d'ici, notre socket est pret à être utilisé, donc maintenant nous allons devoir nous occupé de la création de notre paquet, pour ceci nous allons commencer par remplir notre structure IP avec les options voulues pour ensuite faire de même notre structure ICMP. Donc commençons avec notre en tête IP :

unsigned short packet_size, ip_version, ip_len;    /* taille de notre paquet, IP version, longueur */
struct iphdr *ip; /* notre structure IP */
....
/* taille de notre paquet */
packet_size = sizeof(struct iphdr) + sizeof(struct icmphdr);
/* on alloue un espace mémoire pour notre structure IP */
ip = (struct iphdr *)malloc(sizeof(struct iphdr));
/* On l'initialise à 0 */
memset(ip, 0x0, sizeof(struct iphdr));

/* longueur de l'en tête IP */
ip_len = sizeof(struct iphdr) / sizeof(unsigned long);
/* IP version */
ip_version = 4;
/* on remplie la structure IP */
ip->verlen = (ip_version << 4) | ip_len;
ip->tos = 0;
ip->tot_len = htons (sizeof (struct iphdr) + sizeof (struct icmphdr));
ip->id = 1;
ip->offset = 0;
ip->ttl = 255;
ip->protocol = IPPROTO_ICMP;
ip->saddr = inet_addr(argv[1]); /* adresse IP source */
ip->daddr = inet_addr(argv[2]); /* adresse IP de destination */
ip->checksum = 0; /* on initialise le champ checksum de notre structure IP à 0 avant l'appel de la fonction calculant le checksum */
ip->checksum = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); /* calcul du checksum avec la fonction */
                                 /* la fonction in_cksum se trouve dans les codes cités dans cet article */


Maintenant que notre structure IP est correctement initialisée, passons à la structure ICMP.

struct icmphdr *icmp;
/* on alloue un espace mémoire pour notre structure ICMP */
icmp = (struct icmphdr *)malloc(sizeof(struct icmphdr));
/* On l'initialise à 0 */
memset(icmp, 0x0, sizeof(struct icmphdr));

/* on la remplie */
icmp->type = 8; /* type ICMP echo request */
icmp->code = 0;
icmp->id = (ushort)GetCurrentProcessId();
icmp->seq = (ushort)GetCurrentProcessId();
icmp->checksum = 0;
icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));


Voila, nos deux structures sont remplies avec les options voulues, maintenant nous allons créer une image de notre paquet dans un buffer, un tableau de char plus précisement, ce tableau aura comme taille, la taille de notre paquet, c'est à dire, la taille de notre structure IP, plus la taille de notre structure ICMP. Voyons plus précisemment ce qui cela donne niveau code.

char *ptr = NULL, packet[32]; /* notre paquet, 32 = sizeof(struct icmphdr) + sizeof(struct iphdr) */

ZeroMemory(packet, sizeof(packet));
ptr = packet;
memcpy(ptr, ip, sizeof(struct iphdr));
ptr += sizeof(struct iphdr);
memcpy(ptr, icmp, sizeof(struct icmphdr));
ptr += sizeof(struct icmphdr);


Enfin, nous voila avec notre paquet prêt à être envoyé à destination, mais pour ceci, il nous reste encore une chose à découvrir, une nouvelle fonction plus précisemment, cette fonction se nomme sendto(). Etudions comment celle ci se présente.

Prototype :

int sendto(socket s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen);


Cette fonction va nous permettre d'envoyer un paquet à une adresse spécifié par la structure to de type sockaddr. Elle prend comme premier paramètre le socket émetteur, comme second un pointeur sur les données à envoyer, le troisième paramètre doit contenir la taille des données émises. Le quatrième paramètre n'est pas très important passons directement au cinquième qui correspond à un pointeur sur une structure de type sockaddr contenant l'adresse de destination, quand au dernier paramètre il spécifie simplement la taille de l'adresse pointée par la structure to.

observons cette exemple :

struct sockaddr_in sin; /* notre structure sockaddr_in qui contiendra l'adresse de destination */

/* on rempli notre structure sockaddr_in */
sin.sin_family = AF_INET;
sin.sin_addr.s_un.s_addr = inet_addr("127.0.0.1"); // Adresse de destination

/* hop, nous envoyons notre paquet à destination */
if(sendto(socket, packet, sizeof(packet), 0x0, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) == SOCKET_ERROR)    
{
    fprintf(stderr, "Erreur lors de l'appel de la fonction sendto. Code erreur : %d\n", WSAGetLastError());    
    free(ip);
    free(icmp);
    WSACleanup();
}


Nous avons terminé en ce qui concerne le forging de paquet ICMP, ceci, bien sûr, n'est qu'un exemple d'application, vous pouvez ensuite réaliser avec ceci un ICMP paquet forgeur ( icmprpk.c (ce fichier se trouve dans l'archive de cet article) ) ou un même un smurfeur (mais c'est mal ). Vous pouvez trouver le code complet démontrant toute cette section ici : icmp.c (ce fichier se trouve dans l'archive de cet article)




5.2  Paquets TCP

Après avoir joué avec le protocol ICMP, attaquons un peu le TCP, en forgeant nos propres datagrammes TCP nous allons pouvoir réaliser de multiples choses, comme envoyer une multitude de paquets TCP avec le flag SYN activé à une machine distante ce qui va créer un effet de surcharge au niveau des connexions de l'hote distante, cette attaque se nomme le "syn flooding", nous allons aussi pouvoir spoofer des connexions TCP avec un peu plus d'expérience (problème de connaissance du numéro de séquence, sur certain OS ).
Maintenant passons à la pratique. Nous connaissons toutes les fonctions nécessaires pour envoyer notre propre paquet TCP, la seule chose innovante ici va être de devoir remplir une structure de type TCP_HDR pour ensuite calculer le checksum de notre paquet grâce à une pseudo en tête permettant ceci. Voyons comment réaliser cela.
Je ne vais pas reparler des détails sur l'initialisation de Winsock, etc... Vous avez dejà vu ça dans la section précédente, passons directement aux choses intérressantes. Donc ici, à la place de déclarer une structure de type ICMP_HDR nous allons la remplacer pas une de type TCP_HDR :

struct tcphdr *tcp; /* notre structure TCP */
/* maintenant passons à la structure TCP */
/* on alloue de la mémoire pour celle ci */
tcp = (struct tcphdr *)malloc(sizeof(struct tcphdr));
memset(tcp, 0x0, sizeof(struct tcphdr));

/* on la remplie */
tcp->sport = htons(1500);    /* port source */
tcp->dport = htons(80) ;    /* port de destination */
tcp->seqnum = htonl(1337);    /* numéro de séquence */
tcp->acknum = htonl(1337); /* ack number */
tcp->dataoffset = (5) << 4; /* décalage */
tcp->flags = 0x02; /* 0x02 = flag syn */
tcp->window = htons(1337); /* fenètre */
tcp->checksum = 0;        /* checksum */
tcp->urgpointer = 0;        /* options */


Maintenant que notre structure TCP est remplie avec les options voulues nous allons devoir calculer le checksum pour notre paquet. pour cela nous allons devoir utiliser une pseudo structure, voyons de suite à quoi ressemble cette structure :

struct pseudohdr
{
    unsigned long saddr;
    unsigned long daddr;
    char useless;
    unsigned char protocol;
    unsigned short length;
    struct tcphdr TCP;
};


Nous connaissons maintenant l'allure de notre pseudo structure, alors nous pouvons la remplir pour ensuite calculer notre checksum, et c'est ce que nous allons faire de suite.

struct pseudohdr *psdheader; /* pseudo header */

/* allocation mémoire pour notre pseudo header */
psdheader = (struct pseudohdr *)malloc(sizeof(struct pseudohdr));
memset(psdheader, 0x0, sizeof(struct pseudohdr));

/* on remplie notre structure */
psdheader->saddr = inet_addr(argv[1]); /* adresse source */
psdheader->daddr = inet_addr(argv[3]); /* adresse de destination */
psdheader->useless = 0;         /* null */
psdheader->protocol = IPPROTO_TCP;     /* protocol */
psdheader->length = htons(sizeof(struct tcphdr)); /* longueur de notre structure TCP_HDR */
psdheader->tcp = *tcp;         /* structure TCP_HDR */

/* Maintenant nous calculons le checksum pour notre paquet */
tcp->checksum = in_cksum((unsigned short *)psdheader, sizeof(struct pseudohdr));


Voila tout est prêt, nous pouvons envoyé notre paquet après avoir copié le contenu des structures dans un buffer de type char, c'est d'ailleurs ce que nous avons fait dans la section précédente pour notre paquet ICMP. Vous trouverez donc ici (ce fichier se trouve dans l'archive de cet article) la source du programme illustrant ce chapitre. Ce programme permet d'envoyer des paquets TCP avec le flag SYN activé avec pour possibilité de modifier l'adresse source de ce même paquet.




5.3  Paquets UDP

Forger ses propres paquets UDP est en soit très simple, il suffit de remplir la structure définissant l'en tête UDP au niveau réseau ce qui n'est pas très complexe, donc après lecture de cette partie vous serez en mesure d'envoyer des datagrammes UDP de tout type, et même en modifiant votre IP source, ce qui, avec le protocol UDP peut être très pratique, celui-ci ne requière aucune authentification au niveau des connexions, vous pourrez donc exploiter ceci.
Etudions cette mystérieuse structure :

typedef struct udphdr
{
    unsigned short srcport;
    unsigned short dstport;
    unsigned short length;
    unsigned short checksum;
} UDP_HDR;



  • unsigned short srcport : ce premier membre définie le port source.

  • unsigned short desport : ce second membre définie encore une fois le port mais cette fois ci celui de destination.

  • unsigned short length : ce membre va définir la longueur de notre en tête UDP.

  • unsigned short checksum : ce dernier membre représente le checksum de notre paquet, sachez qu'il n'est pas obligatoire de le calculer, vous pouvez simplement, l'initialisé à 0.



Voyons cette exemple d'initialisation pour notre structure de type UDP_HDR :

/* notre structure udphdr */
struct udphdr *udp;
        
/* initialisation mémoire */
udp = (struct udphdr *)malloc(sizeof(struct udphdr));
memset(udp, 0x0, sizeof(struct udphdr));

/* remplissage */
udp->srcport = htons(1500); /* port source */
udp->dstport = htons(80); /* port destination */
udp->length = htons (sizeof(struct udphdr)); /* longueur */
udp>checksum = 0; /* checksum */


Maintenant, vous en savez assez avec ceci et les exemples précédents pour envoyer vos propres datagrammes UDP.
Vous trouverez un exemple de code illustrant cette partie ici (ce fichier se trouve dans l'archive de cet article).





<<  4  Les différents protocolesSommaire6  Programmer un sniffer grâce aux raw sockets  >>

 Accés rapide

1  Winsock 2 et architecture
2  Les Sockets Streams
3  Les raw sockets
4  Les différents protocoles
5  Forger ses propres paquets

Paquets ICMP

Paquets TCP

Paquets UDP

6  Programmer un sniffer grâce aux raw sockets
7  Références
8  Remerciements
Voir le sommaire complet

 Liens utiles

  • Publier un article
  • Envoyer cette page
  • Ecrire à l'auteur

  •  Mini-Chat

    Thienou (00h11): salut
    Thienou (00h13): Oula mon inscription date de 11 ans je me sent vieux :)
    neowolf25 (17h59): MMF2 en "pay what you want" jusqu'à demain sur
    neowolf25 (17h59): https://www.hu
    mblebundle.com/
    weekly

    Miuka (21h15): Coin coin de 2014
    Miuka (21h15): Des gens qui ont migré sur le forum Clickteam ou ailleurs ?
    Strike (09h45): Salut les vieux !
    Hikarion (12h46): Salut les djeunz
    Hikarion (13h38): A qui profite le scandale ?
    Hikarion (13h44): le chat irc est toujours actif ?

    Votre message



     Archives

     Dev4all Newsletter

    Restez à jour avec la newsletter mensuelle !

    Votre e-mail


    1800 abonnés

     Recommander Dev4all

    Recommandez Dev4all à un ami. Cela fera grandir notre communauté !

    E-mails de vos amis




    [ Accueil | S'inscrire | Mon Dev4all | Communauté | Téléchargements | Articles | Forums | Chat ]

    [ A propos de Dev4all | Aide | La charte Dev4all | Contact ]

    © 2000-2018 MTProd. Tous droits réservés.
    L'utilisation de Dev4all implique l'acceptation et le respect de la charte Dev4all.