mercredi 10 novembre 2010

Comprendre ce à quoi servent __cdecl et __stdcall en langage C, ainsi que leurs différences

Qu'on développe en C ou qu'on lise du code (pour le fun), on tombe souvent sur des signatures type :
int __cdecl foobar(const char* pt_name, int anotherArg);


Ou même encore :

void __stdcall barfoo(void* ptr, int arg);


Les prototypes sont choisis au hasard. On ne sait pas ce que font ces fonctions à l'intérieur et encore moins à quoi elles servent.

D'après le titre de l'article, vous comprendrez que nous nous intéresserons aux notations __cdecl et __stdcall.

Tout d'abord, il faut savoir que ça n'est pas un souci majeur de savoir si on doit mettre telle ou telle directive. En effet, cela change peu de choses. Si l'on ne met aucune de ces deux directives - et c'est souvent le cas quand on écrit du C, parce qu'on en n'a strictement rien à cirer - alors ce sera la directive __cdecl qui sera utilisée.

Allez, j'en viens aux faits... Explications !

Comme vous pouvez le voir, on utilise ces directives lors de la définition de fonctions. En effet, pour ceux qui ne savent pas forcément comment se passe l'appel d'une fonction - d'un point de vue binaire, donc quand on exécute des instructions "natives" successivement - voici, sans entrer dans des explications barbares, comment ça se passe :

La routine appelante, qui peut aussi bien être votre fonction main ou même une autre fonction, va déposer les arguments de la fonction à appeler sur la pile d'exécution. Les informaticiens emploient couramment la traduction anglaise de ce terme : "execution stack", voire stack puisqu'ils sont fainéants. Cette pile d'exécution est très utile, et sert au programme pour mémoriser des données en général : variables, adresses mémoire, et j'en passe. Prenons le code suivant :

#include <stdio.h>
#include <stdlib.h>

void __cdecl doStuff(int, int);
void __stdcall doAnotherStuff(int, int, int, int);

int main(int argc, char **argv) {
doStuff(1, 2);
doAnotherStuff(1, 2, 3, 4);
return EXIT_SUCCESS;
}

void __cdecl doStuff(int arg1, int arg2) {
printf("I'm doStuff.\n");
}

void __stdcall doAnotherStuff(int foo1, int foo2, int foo3, int foo4) {
printf("- And I'm doAnotherStuff.\n");
}


Lors de l'appel à doStuff, le code présent dans la fonction main() va simplement "empiler" - ou "déposer sur la pile d'exécution" - les arguments 2 et 1. La directive __cdecl définit l'ordre de passage des arguments de la fonction de droite à gauche. C'est-à-dire que nous allons déposer le nombre 2 sur la pile, et 1 ensuite. Au final, notre pile ressemblera à ceci :

00000001
00000002


La valeur 2 se retrouve à la base de notre pile (bien qu'en réalité, on ait d'autres valeurs en-dessous mais elles ne nous intéressent pas) car elle a été empilée en premier. 1 se retrouve au sommet car il a été empilé en second. Vous pourrez penser que c'est bizarre, mais il n'en est rien ; il est plus aisé, pour la fonction appelée, de retrouver le premier argument au sommet de la pile !

La directive __stdcall utilise aussi cette convention, elle a ça en commun avec __cedcl. De ce fait, avant d'appeler la fonction doAnotherStuff, la pile d'exécution sera :

Arg1 = 00000001
Arg2 = 00000002
Arg3 = 00000003
Arg4 = 00000004


Pour information, il existe aussi des conventions où l'on empile les arguments de gauche à droite. Mais nous ne nous y intéresserons pas.

Vous suivez jusqu'ici ? Alors continuons.

Ne tournons plus autour du pot et expliquons les choses telles qu'elles sont réellement :

  • Vous choisissez la directive __cdecl : une fois l'appel de la fonction déroulé, la pile sera intacte et ce sera logiquement à vous de la "nettoyer" ;

  • Vous choisissez la directive __stdcall : une fois l'appel de la fonction déroulé, la pile sera dépourvue des arguments que nous avons empilés.



Que ce soient pour ceux qui ne comprennent pas nécessairement le fonctionnement exact de la pile - parce que j'ai été bref - ou ceux qui savent très très bien de quoi je parle, une question commune peut se poser : "Mais quel intérêt ? Pourquoi choisir l'une ou l'autre puisque ça ne change rien d'apparent ?"

Tout d'abord, sachez que si vous utilisez __stdcall, assurez-vous - et on ne sait jamais - d'avoir une copie de vos arguments quelque part en mémoire. Logiquement, vous en avez. Mais une erreur est si vite arrivée ! Un avantage notable serait évidemment des raisons de réduction de taille de pile : en effet, si votre fonction accepte plusieurs arguments voire une structure en entrée seulement - donc copiée intégralement sur la pile - alors la directive __stdcall peut s'avérer intéressante puisque la pile est dégagée au retour à la routine appelante.

Mais __cdecl a ses avantages aussi : c'est de garder la pile telle qu'on l'a trouvée avant l'appel ; pratique pour ceux qui utilisent une fonction sans forcément l'avoir programmée - l'API Win32, par exemple - et qui veulent que les choses restent à leur place. Ben, tenez, j'ai parlé de l'API Win32. Elles sont toutes, je pense, conventionnées par __stdcall.

Peut-être dis-je des choses fausses à propos des avantages / inconvénients ; le fainéant que je suis n'a pas pris la peine de se renseigner davantage sur ces directives-là. Donc ça peut se discuter, éventuellement. Et les commentaires sont là pour ça (je ne les ai jamais soumis à approbation et ça a excellemment marché jusque là).

Référence : http://msdn.microsoft.com/en-us/library/984x0h58%28v=VS.80%29.aspx


Geo

dimanche 13 juin 2010

Comment supporter __construct() et __destruct() en PHP 4 ?

Ok, rien à voir avec la sécurité, le Reverse Code Engineering ou quoi, mais je m'en fous. C'est mon blog et j'ai bien prévenu que je pouvais publier des trucs sur les polly pockets et les figurines en castor.

Allez, plus sérieusement, j'aimerais partager une astuce - m'en souvenir aussi, en tout cas - de programmation orientée objet en php. Je signale que cette méthode n'est en rien issu d'une masturbation intellectuelle, j'ai trouvé ça dans le framework CakePHP.

Pour se situer dans le contexte



En php4, voici ce qu'on ferait pour déclarer notre définition de classe (le moule à gateau, quoi).

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function Foo($arg = "") {
$this->_param = $arg;
}
}



Notez que j'ai omis le "?>" à la fin ; en effet, ça permet d'éviter d'avoir des erreurs à la con genre "header already sent by "fichier.class.php" on line [...]. On est sûr de n'avoir que du PHP derrière, pas d'echo d'HTML ni rien.

Et donc, en PHP 5, on a ça :

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function __construct($arg = "") {
$this->_param = $arg;
}
}



On se sert de la méthode magique __construct(). Notez qu'on peut très bien utiliser aussi le constructeur "Foo()", mais les méthodes magiques n'ont pas été conçues pour rien !

Et pour preuve, en PHP 5, on peut faire ça :

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function __construct($arg = "") {
$this->_param = $arg;
}

/* Destructeur de la classe */
public function __destruct() {
echo "Bye bye!\n";
}
}



Généralement, on utilise les destructeurs lorsque notre objet est amené à allouer des ressources dynamiquement ; il faut donc les libérer.

Prenons un exemple con : un objet va encapsuler un fichier. Le constructeur va ouvrir ce fichier (ça sera transparent) et le destructeur le fermera sans que nous ayons à écrire :

fclose($handleSurNotreFichier);


Bref, revenons à nos moutons. On est passé à PHP 5 dans la plupart des cas, mais il arrive parfois qu'on ne dispose que d'une version 4 qui ne supporte pas __construct() et __destruct() comme on le souhaiterait. En effet, ces méthodes ne seraient pas magiques et donc pas appelées automatiquement et respectivement lors de la construction et la destruction d'une instance.

Voici comment CakePHP procède : tout d'abord, il définit une class "Mère", je dirais même "Doyenne" donc toutes les autres classes hériteront. Et là, vous comprendrez vite l'intention :

<?php

class Mother {

public function Mother() {
/* On récupère les arguments passés à la fonction
via http://fr.php.net/func_get_args */
$arguments = func_get_args();

/* On vérifie si la méthode __destruct() existe
via http://fr2.php.net/method_exists */
if(method_exists($this, "__destruct")) {
/* Si c'est le cas, on l'assimile en fonction
"callback" de "shutdown, c'est-à-dire qu'elle
sera appelée en fin de vie de l'instance
(http://fr2.php.net/register_shutdown_function)
*/
register_shutdown_function( array(&$this, "__destruct") );
}
/* On appelle notre méthode __construct() avec les arguments passés
avec http://fr2.php.net/call_user_func_array */
call_user_func_array(array(&$this, "__construct"), $arguments);
}

/* Et là, on a notre constructeur qui sera surchargé par les classes
filles */
public function __construct() {
/* Le code de votre choix */
}
}


Désormais, qu'on soit en PHP 4 ou PHP 5, on pourra faire :

<?php

class MaClasse extends Mother {

/* Handle sur un fichier */
private $_handle = null;

/* Constructeur */
public function __construct() {
/* Ouverture d'un fichier */
$this->_handle = @fopen("fichier", "w");
}

/* On peut imaginer des méthodes pour écrire, tout effacer, etc... */

/* Destructeur */
public function __destruct() {
fclose($this->_handle);
}
}


Si ça se trouve j'étais le seul à pas connaître ce tour de passe-passe (& puis tant mieux) mais si y'en a à qui ça peut servir, ben tant mieux. J'ai aucun mérite sur la chose, mais je me dis que partager cette petite astuce qui se retrouve au milieu d'un bazar pointu - c'est comme auditer le code de phpBB, quoi - pourrait servir...

Geo

dimanche 6 juin 2010

Une bonne raison d'utiliser intval()

Me frappez pas. J'ai lâché l'idée de reverser winpcap car au final tout tourne autour d'un driver fait par eux-mêmes. Peut-être que quand j'aurai retrouvé l'envie, je m'y mettrai. Mais là, j'ai la tête à parler de PHP car ça faisait longtemps.

Sur le blog de mon camarade pp^, on peut voir un article fort intéressant sur le contournement de la fonction in_array(). Je ne vais pas m'attarder là-dessus, je vous redirige juste sur son article : http://ppisho.me/hack-web/in_array-probleme-avec-des-nombres/.

J'ai aussi trouvé un problème avec des nombres, hors de la fonction in_array(). M'enfin, c'était pas dur à trouver non plus.

Supposons que vous ayez des droits d'écriture sur $foo :


$foo = 3;
if($foo >= 2) {
echo "Rulez!";
} else {
echo "No...";
}


Évidemment, le code affiche Rulez. Seulement voilà, vous vous en douterez :


$foo = " 3 foobar";
if($foo >= 2) {
echo "Rulez!";
} else {
echo "No...";
}


Ce code affiche aussi "Rulez"... Et pour cause ! PHP, c'est bien, mais c'est pas un langage fortement typé comme le C ou le Java. Tout est chaînes de caractères, en PHP. Imaginez qu'à la place de "foobar" après notre "3", on injecte du code SQL, Javascript, ... Et j'en passe. Bah ça fait bobo. Notez aussi que j'ai mis un espace avant le 3 ; pourvu que ça soit un caractère blanc, donc tabulation, retour à la ligne, etc, en fait.

Bon, ok, c'est pas une découverte révolutionnaire et tout le monde sait comment éviter ça.

Avec intval(), notre variable aura toujours un nombre entier mais la condition sera quand même vérifiée :

$foo = intval(" 3 foobar");
if($foo >= 2) {
echo "Rulez! (\$foo = ".$foo.")\n";
} else {
echo ":(";
}


Résultat :

Rulez! ($foo = 3)


Ca sera tout.

Geo

samedi 20 mars 2010

Analyse de winpcap - Petite rectification

Salut,

Je profite d'un billet bref pour vous signaler que je faisais un peu fausse piste. En effet, mon code n'est pas du tout portable. Et là, encore, je ne vous parle pas de linux qui refuserait aimablement de compiler, mais je parle du fait que je passe par un driver sans m'en rendre compte : npf.sys. Si je n'ai pas ce driver, je peux ouvrir mes interfaces réseau, certes, mais ça ne sert à rien d'écrire dedans.

Mes excuses, et voici le driver pour ceux qui veulent : http://venom630.free.fr/geo/blog/pcap/npf.sys

En ce qui concerne "WinGPacket" - ma classe perso pour forger des paquets - elle continue d'être bricolée. Ca reste une bidouille perso et non un projet sérieux, mais pour ceux qui veulent les sources :
http://venom630.free.fr/?mod=cpp&submod=wingpacket

Ah, oui, vous allez atterrir sur un portfolio que je me suis amusé à faire car je me faisais chier. Ca me permet d'organiser plus proprement mon boulot que mon dépôt.

Vous verrez que WinGPacket utilise Registry et String, deux classes que j'ai aussi codées. La première regroupe des fonctions statiques pour manipuler le registre sans se casser la tête, et la classe String, ... hum, vous voyez. :)

C'est tout ce que j'ai à dire pour cette fois. Dans la part III, j'essaierai de vous expliquer comment il est possible de "sniffer" le trafic réseau à coups de DeviceIoControl() (pour foutre la carte réseau en mode promiscuous, par exemple) et ReadFile() (vous vous en doutiez, n'est-ce pas ?).

A bientôt !

Geo

mardi 9 mars 2010

Fonctionnalités intéressantes dans notepad++

Ce petit poste plus ou moins inutile pour me rappeler de quelques fonctionnalités intéressantes que j'ai trouvé au pif sur notepad++. Vous savez, quand on fait une séquence de touches sans faire exprès. Ca peut mener à des résultats rigolos.

On connait bien, pour la plupart, le raccourci Ctrl+G (G de "Go") :

Invite de notepad++ : Aller à un endroit précis


Le développeur de cet outil est très amusant !

Y'a aussi Ctrl + Q - Q étant la première lettre de "Quote". En fonction du langage que vous avez choisi pour la coloration syntaxique, sélectionnez votre bloc de code et appuyez sur ctrl + Q pour y passer en commentaires.


Invite de notepad++ : Commenter du code (cliquez pour agrandir)


Et enfin, à l'instant, je viens de taper sur ctrl + espace, et là... WOOT !


Invite de notepad++ : quelle fonction utiliser ? (cliquez pour agrandir)


On a une liste - peut-être exhaustive ? - de fonctions php à utiliser. Et c'est pas qu'en PHP qu'on nous propose ça. Je connaissais pas, c'est marrant.

Bon, ok, cet article sert un peu à rien, c'est juste pour archiver une connerie dont j'aimerais me resservir et ne pas oublier.

Cya.

Geo

dimanche 7 mars 2010

Analyse de winpcap - Partie II

On continue sur la lancée. Lors de mon article précédent, je montrais comment il était possible d'énumérer les interfaces réseau disponible sur ma bécane. ( http://geo0w.blogspot.com/2010/03/analyse-de-winpcap-partie-i.html ).

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 :


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


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.

mercredi 3 mars 2010

Analyse de winpcap - Partie I

Hello les gens,

Lors de mon dernier poste traitant de la lib winpcap et de ses fonctionnalités, j'avais parlé, en conclusion, d'analyser son fonctionnement. Effectivement, j'étais resté perplexe et me posais une question.

En effet, il faut savoir que Microsoft restreint l'usage des "Raw Socket", où nous sommes en mesure de forger nos propres paquets. On m'a conseillé d'abandonner, et je me suis rendu compte que j'avais bien fait de désobéir ! >:)

Pour ceux qui veulent des infos sur les Raw Socket avec WinSock : http://msdn.microsoft.com/en-us/library/ms740548%28VS.85%29.aspx. Vous comprendrez plus ou moins que si vous voulez balancer un paquet ARP, vous pouvez allez vous faire foutre. Au moins, ça a le mérite d'être clair.

Mais, quand il s'agit de winpcap, on peut faire ce qu'on veut. Etrange, non ? Alors je me suis mis en tête d'analyser ça. La question : "Comment ça fonctionne ?". Question brève pour une réponse qui va se scinder en probablement plusieurs postes.

Dans ce premier poste, j'irai tout en douceur. On n'apprendra pas à balancer / sniffer des paquets, mais juste à récupérer la liste des interfaces réseau (c'est déjà bien pour commencer, non ?)

/!\ Les tests sont menés, ici, sous un windows XP SP3. Il se peut que ça marche sur la série XP, mais je ne garantie rien quant à Vista / Seven et encore moins sous les versions antérieures !

Les interfaces réseau



Sous Windows, on peut avoir une ou plusieurs interfaces réseau. Votre carte ethernet en est une, aussi bien que votre carte Wifi. J'ai même une clé USB Wifi reconnue en tant qu'interface réseau. Il se peut aussi que les machines virtuelles engendrent la création d'interfaces réseau exploitables ; je ne me suis pas penché sur ce genre de chose, mais certains reversers doivent avoir la réponse. ;)

Pour récupérer cette liste d'interfaces réseau, il faut aller chercher dans la base de registre à ce chemin : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}. Dans cette clé figurent des sous-clés ayant pour non 0000, puis 0001, puis 0002, ... A croire qu'on prévoit 10000 noms d'interfaces réseau.

Dans ces sous-clé se trouvent une clé nommée "Linkage", et, enfin, dans cette clé "Linkage" se trouve une valeur de type REG_MULTI_SZ - constante signifiant "plusieurs chaînes terminées par un octet nul", d'où le "SZ" - String Zero - qui correspond au chemin de notre interface réseau.

L'objectif du moment sera d'afficher chacune de ces interfaces. Grâce à l'API Win32 et à sa panoplie de fonctions RegMachiChouette(), ça doit pas être bien dur ! :)

Quelques fonctions utiles :
RegOpenKeyEx() : Permet d'ouvrir une clé existante : http://msdn.microsoft.com/en-us/library/ms724897(VS.85).aspx ;
RegQueryValueEx() : Permet de récupérer la valeur d'une clé. http://msdn.microsoft.com/en-us/library/ms724911(VS.85).aspx ;
CreateFile() : Permet de déterminer si une interface réseau est accessible (un bon indice pour la suite ! :]) : http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx.

Ok, on aura nos interfaces, mais il faudrait aussi la description de chacune d'elles.
Et bien j'ai tout de même trouvé, dans la clé SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards, des sous-clé ayant pour un un numéro sans les 0 devant, cette fois. Mais ce sont les mêmes numéros que nos interfaces réseau de HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}, donc il nous sera facile de faire le lien !

Maintenant, on se tait et on code.

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>

#define KEY_IF_RESEAUX "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define KEY_DESC_RESEAUX "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"

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

// Des handle de clefs
HKEY hKey, hCurrentKey, hDescKey;

// Variables
char* currentPathKey, *currentDescKey;

// Numéro de l'interface sous chaîne de caractères
char currentNumber[5];

// compteur de l'interface
int cpt = 0;

// Handle sur l'interface qui va nous permettre de savoir si on peut l'ouvrir ou pas
HANDLE hDevice;

// Variable dont on se sert pour connaître les octets retournés par une fonction
LONG dwBytesReturned;

// Tampons
char buffer[1024];
char devName[1024];
char desc[1024];

// Première étape : ouverture de la clé en question
LONG Status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, KEY_IF_RESEAUX, 0, KEY_ALL_ACCESS, &hKey );
if(Status == ERROR_SUCCESS) {

// On la ferme puisqu'on n'en a plus besoin
RegCloseKey(hKey);

// Les sous-clé s'appellent toutes 0000, 0001, ... Donc il va falloir les parcourir.
// strlen("\\0000\\Linkage") + '\0' = 14

sprintf(currentNumber,"%04d", cpt);

// On prépare le chemin de la clé dans le registre
currentPathKey = (char*)malloc(strlen(KEY_IF_RESEAUX) + 14);
strcpy(currentPathKey, KEY_IF_RESEAUX);
strcat(currentPathKey, "\\");
strcat(currentPathKey, currentNumber);
strcat(currentPathKey, "\\Linkage");

// On ouvre cette clé
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, currentPathKey, 0, KEY_ALL_ACCESS, &hCurrentKey);

// Tant qu'on arrive à ouvrir la clé...
while(Status == ERROR_SUCCESS) {

// La valeur "Export" nous intéresse.
Status = RegQueryValueEx(hCurrentKey, "Export", NULL, NULL, (BYTE*)buffer, (DWORD*)&dwBytesReturned);

// Si on a réussit à lire la valeur, on l'affiche
if(Status == ERROR_SUCCESS) {
// On teste si l'interface est accessible
sprintf(devName, "\\\\.\\Global\\%s", &buffer[8]);
hDevice = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
if(hDevice != INVALID_HANDLE_VALUE) {

// On essaie de récupérer la description de cette carte
sprintf(currentNumber, "%d", atoi(currentNumber)+1);
//printf("%s %s\n", buffer, currentNumber);
// strlen("\\xxxx") + '\0' = 6
currentDescKey = (char*)malloc(strlen(KEY_DESC_RESEAUX) + 6);
strcpy(currentDescKey, KEY_DESC_RESEAUX);
strcat(currentDescKey, "\\");
strcat(currentDescKey, currentNumber);

// On ouvre la clef de la description
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, currentDescKey, 0, KEY_ALL_ACCESS, &hDescKey);
if(Status == ERROR_SUCCESS) {

// "Description" nous intéresse
Status = RegQueryValueEx(hDescKey, "Description", NULL, NULL, (BYTE*)desc, (DWORD*)&dwBytesReturned);
if(Status == ERROR_SUCCESS) {
printf("%s %s\n", buffer, desc);
RegCloseKey(hDescKey);
}
}
free(currentDescKey);
CloseHandle(hDevice);
}
}

ZeroMemory(currentPathKey, strlen(KEY_IF_RESEAUX) + 14);

// On prépare le chemin de la clé suivante
sprintf(currentNumber,"%04d", ++cpt);

strcpy(currentPathKey, KEY_IF_RESEAUX);
strcat(currentPathKey, "\\");
strcat(currentPathKey, currentNumber);
strcat(currentPathKey, "\\Linkage");

// On la relit.
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, currentPathKey, 0, KEY_ALL_ACCESS, &hCurrentKey);

}

// On libère tout le tralala & on se casse
free(currentPathKey);
RegCloseKey(hCurrentKey);

}

return EXIT_SUCCESS;
}

Pour la source clean, c'est par ici : http://venom630.free.fr/geo/blog/reverse_pcap/part1/getInterfacesReseau.html. Le gros cadeau, c'est qu'il faille simplement compiler un seul fichier ! Pas de lib à linker ni rien.

Le résultat que j'obtiens :

C:\Documents and Settings\Geoffrey\Mes documents\c>getInterfacesReseau.exe
\Device\{8EEB3BBC-655E-4E05-A2DD-92A5325081A6} Belkin Wireless G Plus MIMO USB Network Adapter
\Device\{F9415153-9262-4C55-B0A4-D9D3B9911CB7} Realtek PCIe FE Family Controller


Pour récupérer les adresses IPs, j'ai pas cherché plus loin. Mais ça ne nous gênera pas pour la suite.

Et j'ai pas testé sur d'autres postes, donc je ne vous garantie rien. Mais alors rien du tout.

Conclusion



Certes, l'article a été très court, mais c'était pour vous montrer un aperçu. Là, on saura quelles interfaces emmerder pour pouvoir balancer nos paquets. Et n'importe quel paquet, de surcroit.

Geo