Il se trouve qu'on avait des résultats un peu bluffant puisque ça parle pas forcément. Heureusement, quand on chopait la description de l'interface réseau, ça allait mieux.
Pour qu'on ne se prenne plus la tête avec tout ça, j'ai décidé d'encapsuler les fonctionnalités que je détaillerais dans une classe C++.
Donc, pour le moment, on a une fonction statique qui renvoie un double vecteur de chaînes de caractères avec la chaîne représentant l'interface, l'autre représentant la description.
On a aussi un constructeur qui prend en argument une interface réseau à ouvrir via CreateFile() et qui va récupérer sa description ainsi que d'autres infos comme son adresse IP, le masque de sous-réseau et la passerelle par défaut. Par contre, j'ai testé sur le Windows Vista de ma copine, ça marche pas. Putain de bordel de merde, quoi.
Bref, on va passer en revue ma classe que j'ai nommée "WinGPacket". Le nom est pourri mais je savais pas comment la baptiser. Win pour Windows, G pour la première lettre de mon pseudo, et "Packet" car ça manipule les paquets réseau. Bref, on s'en fout...
wingpacket.hpp :
#ifndef _WINGPACKET_HPP
#define _WINGPACKET_HPP
#include <iostream>
#include <cstdlib>
#include <vector>
#include <windows.h>
#include "string.hpp"
class Wingpacket {
public:
Wingpacket(char* = NULL);
~Wingpacket();
static std::vector< std::vector<String> > getAllInterfaces();
char* getDescription() const;
char* getIPAddress() const;
char* getSubnetMask() const;
char* getDefaultGateWay() const;
int sendPacket(char* packet, int size) const;
private:
String m_description;
String m_ipAddr, m_subnetMask, m_defaultGateWay;
HANDLE m_hDevice;
};
#endif
En quelques mots : on met des défines pour éviter de se faire couillonner si y'a des inclusions multiples. On a aussi les accesseurs aux principaux membres qui sont des objets non pas de type "string" mais de type "String", ce qui change tout. C'est une classe que j'ai refaite dans le cadre de mes études et je me suis dit que je pourrais la garder.
Mais ce qui reste le plus intéressant, c'est la fonction au prototype suivant :
int sendPacket(char* packet, int size) const;
Elle prend en argument un pointeur sur notre tableau de caractères qui correspond à notre packet en dur, ainsi que sa taille. Effectivement, on n'ira pas mesurer la taille du paquet avec strlen() puisqu'il y a des octets nuls dedans.
Et le plus impressionnant : son code. Vous êtes prêts ? :)
int Wingpacket::sendPacket(char* packet, int size) const {
DWORD dwBytesWritten;
return WriteFile(this->m_hDevice, packet, size, &dwBytesWritten, NULL );
}Eh oui, un simple WriteFile() suffit à balancer notre paquet sur le réseau. Quand on sniffe avec WireShark, on les voit tranquillement défiler. Notre handle correspond à une poignée sur notre interface réseau qui est ouverte dans le constructeur de la manière suivante :
String strDeviceName(deviceName);
if(strDeviceName.substr(0, 8) == "\\Device\\") {
String newName = "\\\\.\\Global\\NPF_";
newName += strDeviceName.substr(8);
String tmpPathKey;
// On essaie d'ouvrir le driver
this->m_hDevice = ::CreateFile(
newName.getStr(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
0
);
// ...
Je fais face à un problème : je préfixe la chaîne passée en paramètre formel à ma méthode qui correspond à l'interface réseau, de la forme { ... }. Visiblement, je préfixe cette chaîne de "\\\\.\\Global\\NPF_" et :
- J'aurais du mal à vous dire ce à quoi correspond "NPF_". Je peux juste vous dire qu'il existe un driver sous ce nom ;
- Parfois, il arrive qu'il faille que j'enlève ce préfixe, sans quoi l'envoi de paquets ne marche pas.
Eh oui, à cause de mon manque de patience et de compétences, j'ai pas tout trouvé ce que je cherchais dans les sources de winpcap. Donc il reste des points à éclaircir.
Ce qu'il faut véritablement retenir, c'est qu'il suffise que d'un WriteFile() pour balancer un paquet forgé entièrement à la main à travers le réseau. Allez, un petit exemple de code qui fonctionne chez moi mais, malheureusement, pas chez tout le monde et peut-être pas chez vous...
#include <iostream>
#include "string.hpp"
#include "wingpacket.hpp"
using namespace std;
int main()
{
vector< vector> Devices = Wingpacket::getAllInterfaces();
for(unsigned int i = 0, l = Devices.size(); i < l; i++) {
cout << i+1 << " : " << Devices[i][0] << " " << Devices[i][1] << std::endl;
}
unsigned int choice;
cout << "Select the interface to open : ";
cin >> choice;
while(choice < 1 || choice > Devices.size()) {
cout << "Try again: ";
cin >> choice;
}
Wingpacket myDevice(Devices[choice-1][0]);
cout << "Description is " << myDevice.getDescription() << "\n";
cout << "IP address is " << myDevice.getIPAddress() << "\n";
cout << "Subnet Mask is " << myDevice.getSubnetMask() << "\n";
cout << "DefaultGateWay is " << myDevice.getDefaultGateWay() << "\n";
for(int i = 0; i < 10; i++) {
myDevice.sendPacket(
"\x01\x23\x45\x67\x89\x2a" // Adresse MAC Destination (n'importe quoi)
"\xfe\xdc\xba\x98\x76\x54" // Adresse MAC Source (n'importe quoi)
// Couche réseau
"\x08\x06" // "Protocole ARP"
"\x00\x01" // Hardware type = Ethernet
"\x08\x00" // Protocol type = IP
"\x06" // Hardware size = 6 (une adresse MAC fait 6 octets)
"\x04" // Protocol size = 4 (une adresse ipv4 fait 4 octets)
"\x00\x02" // On envoie une réponse
"\x01\x23\x45\x67\x89\x2a" // Adesse MAC de l'envoyeur (n'importe quoi)
"\xc0\xa8\x01\x01" // Adresse IP de l'envoyeur (on fout n'importe quoi, ici, 192.168.1.1)
"\xfe\xdc\xba\x98\x76\x54" // Adresse MAC Destination (n'importe quoi)
"\xc0\xa8\x01\x05" // Adresse IP Destination (ici, moi : 192.168.1.5
, 42);
Sleep(1000);
}
return 0;
}
A l'exécution :
C:\[...]\WinGPacket\bin\Release>WinGPacket.exe
1 : \Device\{C8B3A563-AC0B-4871-9A3B-9EF6DB42EC87}
2 : \Device\{F9A79020-32A7-4134-BD12-C92351E25EC4}
3 : \Device\NdisWanIp
4 : \Device\{8EEB3BBC-655E-4E05-A2DD-92A5325081A6} Belkin Wireless G Plus MIMO USB Network Adapter
5 : \Device\{79ACFCBD-A8D5-40EE-B565-3AA5D78D34B5}
6 : \Device\{70467207-74FE-4F20-87EB-5F79C03C2D2A}
7 : \Device\NdisWanBh
8 : \Device\{F9415153-9262-4C55-B0A4-D9D3B9911CB7} Realtek PCIe FE Family Controller
Select the interface to open :
1 : \Device\{C8B3A563-AC0B-4871-9A3B-9EF6DB42EC87}
2 : \Device\{F9A79020-32A7-4134-BD12-C92351E25EC4}
3 : \Device\NdisWanIp
4 : \Device\{8EEB3BBC-655E-4E05-A2DD-92A5325081A6} Belkin Wireless G Plus MIMO USB Network Adapter
5 : \Device\{79ACFCBD-A8D5-40EE-B565-3AA5D78D34B5}
6 : \Device\{70467207-74FE-4F20-87EB-5F79C03C2D2A}
7 : \Device\NdisWanBh
8 : \Device\{F9415153-9262-4C55-B0A4-D9D3B9911CB7} Realtek PCIe FE Family Controller
Select the interface to open :
Un invite de saisie où je rentre le numéro d'une interface qui me parle, c'est-à-dire qui possède une description. Ici, ça sera "4" car je bosse avec ma clé USB Wifi.
Select the interface to open : 4
Description is Belkin Wireless G Plus MIMO USB Network Adapter
IP address is 192.168.1.3
Subnet Mask is 255.255.255.0
DefaultGateWay is 192.168.1.1
Description is Belkin Wireless G Plus MIMO USB Network Adapter
IP address is 192.168.1.3
Subnet Mask is 255.255.255.0
DefaultGateWay is 192.168.1.1
Super, il m'affiche la configuration de ma clé wifi, malheureusement chopée dans le registre et non à l'aide d' "IoControlCode". Bon, je suis sûr de rien, mais je suis persuadé qu'il soit possible de récupérer ce telles informations via un DeviceIoControl()...
Mais avant, je prends bien soin d'ouvrir WireShark et de capturer les paquets en mode "non promiscuous" (exceptionnellement pour cette interface car sinon ça ne fonctionne pas) et je mate ce qui se passe. Et là...

Cliquez sur l'image pour agrandir
On voit nos paquets passer, l'air de rien. Bon, heureusement, j'ai mis de la merde quant aux adresses MAC. Mais si on fout vraiment n'importe quoi, on peut bloquer entièrement le réseau ; dire à tout le monde que la passerelle par défaut se situe à une adresse MAC foireuse, ça fait mal et ça empêche tout le monde de passer. Effet garanti.
Conclusion
Envoyer nos propres paquets sur le réseau s'avère, avec le recul, vraiment simple. Ce qui est plus dur et ce sur quoi je ne me suis pas (encore ?) attardé : gérer des connexions TCP/IP avec ces paquets, par exemple. Et plus encore si affinité. Heureusement, il y a les rfc.
A retenir : WriteFile() permet l'envoi de paquets sur une interface réseau ouverte avec CreateFile().
Et comme promis, une archive contentant le programme de test, les sources et le fichier projet Code::Blocks : http://venom630.free.fr/geo/blog/pcap/WinGPacket.zip
Et, enfin, si par hasard vous avez des réponses à mes interrogations, je suis entièrement preneur car je compte vraiment faire quelque chose de concret avec tout ça. A vrai dire, faire un fork() de pcap m'intéresserait car j'aurais juste des .h et .cpp à me trimballer ; des fichiers sources dont je connais et maîtrise le contenu plutôt que des dll et plein d'autres dépendances...
Geo
PS : Ivan, je me suis mis aux chocapics.