Chrooter apache
Matériel
Ajout disque dur
Ajout carte
Audit des disques durs
Gestion des peripheriques
Disquette d'installation
Ajout d'un scanner
Astuces
Ajout d'une imprimante
Réseau
Configuration reseau
Dns
Serveur cvs
Proxy squid 
Installation serveur ftp
Installation qmail 
Installation serveur courrier sous debian
Outil TCP/IP 
Le serveur samba
Connexion a distance securisee
Client/serveur vnc
Configurer apache
Dyndns
Installer un LAMP sous mandriva
Sécurité
Chiffrer un fichier/dossier
Securiser son poste
Mur pare feu pas a pas
Authentification ht-access
Surveillance de serveur CACTI
Snort
Snort-inline
Securiser Apache avec mod_security
Filtrage squid/squidguard/dansguardian
Auditer son site web
Sécuriser son linux
Installer un Lamp avec ssl
Contrer les scans de ports
Traitement anti-spam
Installer/Utiliser tripwire
Faire des sauvegardes incrémentales
Rsync
Nessus
Divers
Elisa, le multimédia facile
Utilisation de lilo
Les commandes Linux
Le multi-tache
Le crontab
Exploration de la configuration
Quotas
Messagerie
Installer une application
Debugger ses applications
Le format RPM
Mise a jour du noyau
Qemu
Tour d'horizon des principaux p2p
Récupération du système
Bips d'un pc
Astuces windows
Table Ascii
Lamerland
Conversion de fichiers musicaux
Compiler ses rpms
Graver en ligne de commande
Graver un fichier avi pour un dvd de salon
Récupérer des fichiers effacés
Liens
hakin9
Secureroot.com
Hackerthreads.org
Defcon
Hackerlounge
Les derniers exploits
Tous les codes sources
Securite sous Linux
Les logiciels libres quotidiens
Ezine divers
Madchat
Textes divers
Archives
 
Traductions LG
Toutes les traductions
Traductions Phrack
Toutes les traductions

Il y a actuellement 7 visiteurs connectés sur le site !

Google

Pourquoi ça ne marche pas !?
Pourquoi ça ne marche pas !? Comment repérer et corriger les erreurs dans une application Linux.

 

Résumé
Introduction
Logs
Strace
gdb et les fichiers core
Conclusion

 

Resume:

Si on entend ici et là qu'il est particulièrement aisé de trouver et corriger des bugs dans les programmes écrits sous Linux, il n'en est pas moins difficile de trouver de la documentation sur la façon de s'y prendre. Dans cet article, vous allez en découvrir plus sur la façon de trouver et de réparer les erreurs sans devoir apprendre quoi que ce soit à propos de la manière dont une application fonctionne.

:

Introduction :

Du point de vue de l'utilisateur, il n'est pas facile de voir une différence entre un système à sources ouvertes et un système fermé tant que tout fonctionne sans problème comme prévu. Néanmoins, la situation change lorsque plus rien ne tourne et un jour ou l'autre, tout utilisateur sera face à cette situation où plus rien ne veut se passer comme prévu. Dans un système avec des sources fermées, vous avez habituellement deux options :
Rapporter l'erreur et payer pour la réparation
Réinstaller et espérer que cela fonctionne ensuite
Sous Linux, vous avez ces options également mais vous pouvez aussi vous lancer dans la recherche de la cause du problème. Un des obstacles les plus importants à cela est le fait que vous n'êtes pas l'auteur du programme défaillant et que vous n'avez absolument pas la moindre idée de la façon dont il fonctionne.
Malgré ces obstacles, il y a différentes choses que vous pouvez faire sans pour cela devoir lire tout le code et sans apprendre comment le programme fonctionne.

Logs:

La chose la plus évidente et la plus simple que vous puissiez faire est de regarder dans les fichiers qui se trouvent dans /var/log/... Ce que vous allez trouvez dans ces fichiers et le nom de ceux-ci est entièrement configurable. /var/log/messages est généralement le fichier sur lequel vous devriez vous pencher. Les grosses applications peuvent avoir des répertoires spécifiques pour leurs logs (/var/log/httpd/ /var/log/exim ...).
La plupart des distributions utilisent syslog comme system logger et son comportement est contrôlé par le fichier de configuration /etc/syslog.conf La syntaxe de ce fichier est commentée dans "man syslog.conf".
Le logging fonctionne comme si le créateur d'un programme pouvait ajouter une ligne syslog à son code. C'est comme un printf sauf que cela écrit dans le log du système. Dans ce rapport, vous spécifiez une priorité et un "facility" pour classifier le message :

#include
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
facility classe le type d'application envoyant le message.
priority détermine l'importance du message. Les valeurs possibles dans leur ordre d'importance sont :
LOG_EMERG
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG

Avec cette interface en C, toute application écrite en C peut écrire sur le log du système. Les autres langages ont des APIs similaires. Même les scripts shell peuvent écrire dans le log avec la commande :
logger -p err "ce texte va vers /var/log/messages"
Une configuration standard de syslog (fichier /etc/syslog.conf) devrait avoir parmi les autres une ligne ressemblant à celle-ci :
# Log anything (except mail) of level info or higher.
# Don't log private authentication messages.
*.info;mail.none;authpriv.none /var/log/messages

Le "*.info" va logger tout ce qui a un niveau de priorité LOG_INFO ou supérieur. Pour avoir plus de renseignements dans /var/log/messages, vous pouvez changer cela en "*.debug" et relancer syslog (/etc/init.d/syslog restart).
La procédure pour "debuger" une application serait donc comme suit. lancer tail -f /var/log/messages et ensuite lancer l'application défaillante dans un autre shell. Peut-être aurez vous quelques pistes pour savoir ce qui ne va pas.
Si la première étape est insuffisante, éditez /etc/syslog.conf et changer *.info en *.debug. Lancez "/etc/init.d/syslog restart" et répéter l'étape 1).
Le problème de cette méthode est qu'elle dépend entièrement de ce que le développeur a mis dans son code. Si il/elle n'a pas ajouté de rapport syslog aux points cruciaux, vous ne verrez alors rien du tout. En d'autres mots, vous ne serez en mesure de voir que les problèmes que le développeur avait présentis.

Strace

Une application sous Linux peur exécuter 3 types de fonctions :
les fonctions se trouvant quelque part dans son propre code
les fonctions de bibliothèques
les appels système
Les fonctions de bibliothèques sont similaires aux fonctions propres des applications sauf que celles-ci sont fournies dans un autre paquetage. Les appels système sont les fonctions où le programme communique avec le noyau. Les programmes ont besoin de communiquer avec le noyau si ils ont besoin d'accéder aux matériel de l'ordinateur. C'est le cas pour l'affichage à l'écran, la lecture d'un fichier sur un disque, la lecture de l'encodage au clavier, l'envoi d'un message sur le réseau, etc...
Ces appels système peuvent être interceptés et vous pouvez dès lors suivre la communication entre l'application et le noyau.
Un problème courant est celui où une application ne fonctionne pas comme prévu car elle ne peut pas trouver un fichier de configuration ou n'a pas les permissions suffisantes pour écrire dans un répertoire. Ces problèmes peuvent facilement être détectés avec strace. L'appel système relevant s'appelle dans ce cas-ci "open".
Vous utilisez strace comme ceci :
strace votre_application

Voici un exemple :
# strace /usr/sbin/uucico
execve("/usr/sbin/uucico", ["/usr/sbin/uucico", "-S", "uucpssh", "-X", "11"],
[/* 36 vars */]) = 0
uname({sys="Linux", node="brain", ...}) = 0
brk(0) = 0x8085e34
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40014000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
etc............................

Que voyez-vous ? Attardons notre regard par exemple sur les lignes suivantes :
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3

Le programme essaye de lire /etc/ld.so.preload et échoue. Ensuite, il continue et lit /etc/ld.so.cache. Ici, il réussit et obtient un file descriptor 3. Maintenant, l'erreur de lecture de /etc/ld.so.preload peut ne pas être un problème du tout car le programme peut juste essayer de lire ceci et l'utiliser si possible. En d'autre mots, ce n'est pas obligatoirement un problème si un programme échoue lors de la lecture d'un fichier. Tout dépend de la façon dont le programme a été élaboré. Jetons un coup d'oeil à tous les appels open dans les résultats de strace :
open("/usr/conf/uucp/config", O_RDONLY)= -1 ENOENT (No such file or directory)
open("/etc/nsswitch.conf", O_RDONLY) = 3
open("/etc/ld.so.cache", O_RDONLY) = 3
open("/lib/libnss_compat.so.2", O_RDONLY) = 3
open("/etc/passwd", O_RDONLY) = 3
open("/usr/conf/uucp/sys", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/var/log/uucp/Debug", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0600) = 3
open("/var/log/uucp/Log", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0644) = 4
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3

Le programme essaye maintenant de lire /usr/conf/uucp/config. Oh! C'est étrange, j'ai le fichier de configuration dans /etc/uucp/config ! ... et donc il n'y a pas de ligne où le programme essaye d'ouvrir /etc/uucp/config. Voilà l'erreur. Le programme a donc manifestement été configuré au moment de la compilation avec une localisation erronée du fichier de configuration.
Comme vous pouvez voir, strace peut être très utile. Le problème est que cela nécessite un peu d'expérience de programmation en C pour comprendre l'ensemble des résultats de strace mais normalement, vous ne devriez pas avoir besoin d'en arriver là.


 

gdb et les fichiers core:

Parfois, il arrive qu'un programme se termine avec ce message empreint de dépit : "Segmentation fault (core dumped)". Celà signifie que le programme a essayé (suite à une erreur de programmation) d'écrire au delà de la zone de mémoire qui lui était allouée. C'est la cas spécialement quand un programme écrit ne serait-ce que quelques octets de trop, ce quii peut survenir en de rares occasions. C'est dû à la mémoire qui est allouée en morceaux et il arrive parfois qu'accidentellement, il n'y ait plus de place suffisante pour les octets supplémentaires.
Lorsque ce "Segmentation fault" apparaît, un fichier core est créé dans le répertoire de travail actuel du programme (normalement, votre répertoire personnel). Ce fichier core est une copie du contenu de la mémoire au moment où l'erreur est survenue. Certains shells fournissent des possibilités de contrôler lorsque les fichiers core sont écrits. Sous bash, par exemple, le comportement par défaut est de ne pas écrire du tout de fichier core. Pour activer les fichiers core, vous devez utiliser cette commande :

# ulimit -c unlimited
# ./lshref -i index.html,index.htm test.html
Segmentation fault (core dumped)
Exit 139
Le fichier core peut maintenant être utilisé avec le débugeur gdb pour trouver ce qui n'a pas fonctionné. Avant de démarrer gdb, vous pouvez vérifier que vous cherchez bien dans le bon fichier core :
# file core.16897
core.16897: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style,
from 'lshref'

OK, lshref est le programme qui a crashé, donc chargeons le dans gdb. Pour appeler gdb en vue de l'utiliser avec le fichier core, vous ne devez pas uniquement spécifier le nom du fichier core mais aussi le nom de l'exécutable qui va avec le fichier core.
# gdb ./lshref core.23061 GNU gdb Linux (5.2.1-4)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
Core was generated by `./lshref -i index.html,index.htm test.html'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x40095e9d in strcpy () from /lib/libc.so.6
(gdb)

Ainsi, nous voyons que le programme crashe lorsqu'il essaye de faire un strcpy. Le problème est que cela peut se situer à beaucoup d'endroits dans le code, partout où strcpy est utilisé.
En général, il y a maintenant 2 possibilités pour trouver exactement dans quelle partie du code cela ne fonctionne plus. Recompiler le code avec l'information pour le débugage (gcc option -g) Réaliser une analyse de la pile dans gdb Le problème dans notre cas est que strcpy est une fonction de bibliothèque et que même si nous recompilions absolument tout le code (en incluant le libc) cela nous dirait toujours que cela échoue à une certaine ligne de la bibliothèque C.
Ce dont nous avons besoin est une analyse de la pile trace qui va nous dire quelle fonction a été appelée avant que strcpy ne soit exécuté. La commande pour réaliser une analyse de la pile dans gdb est "backtrace". Cela ne va cependant pas fonctionner uniquement avec le fichier core. Nous devons relancer la commande dans gdb (reproduire l'erreur) :
gdb ./lshref core.23061
GNU gdb Linux (5.2.1-4)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
Core was generated by `./lshref -i index.html,index.htm test.html'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x40095e9d in strcpy () from /lib/libc.so.6
(gdb) backtrace
#0 0x40095e9d in strcpy () from /lib/libc.so.6
Cannot access memory at address 0xbfffeb38
(gdb) run ./lshref -i index.html,index.htm test.html
Starting program: /home/guido/lshref ./lshref -i index.html,index.htm test.html

Program received signal SIGSEGV, Segmentation fault.
0x40095e9d in strcpy () from /lib/libc.so.6
(gdb) backtrace
#0 0x40095e9d in strcpy () from /lib/libc.so.6
#1 0x08048d09 in string_to_list ()
#2 0x080494c8 in main ()
#3 0x400374ed in __libc_start_main () from /lib/libc.so.6
(gdb)

Maintenant, nous pouvons voir que la fonction main() a appelé string_to_list() et de string_to_list strcpy() est appelé. Nous allons nous tourner vers string_to_list() et regarder le code :
char **string_to_list(char *string){
char *dat;
char *chptr;
char **array;
int i=0;
dat=(char *)malloc(strlen(string))+5000;
array=(char **)malloc(sizeof(char *)*51);
strcpy(dat,string);

Cette ligne malloc semble contenir une faute de positionnement typographique. Probablement, cela aurait dû être : dat=(char *)malloc(strlen(string)+5000);

Nous modifions cela, recompilons et ... hourra ... cela fonctionne.
Regardons un deuxième exemple où l'erreur n'est pas détectée dans une librairie mais dans le code de l'application. Dans un tel cas, l'application peut être compilée avec le drapeau "gcc -g" et gdb sera capable de montrer la ligne exacte où l'erreur a été détectée.

Voici un exemple simple.
#include
#include
int add(int *p,int a,int b)
{
*p=a+b;
return(*p);
}
int main(void)
{
int i;
int *p = 0; /* a null pointer */
printf("result is %d\n", add(p,2,3));
return(0);
}

Nous le compilons :
gcc -Wall -g -o exmp exmp.c
Lancons le ...
# ./exmp
Segmentation fault (core dumped)
Exit 139
gdb exmp core.5302
GNU gdb Linux (5.2.1-4)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
Core was generated by `./exmp'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x08048334 in add (p=Cannot access memory at address 0xbfffe020
) at exmp.c:6
6 *p=a+b;

gdb nous dit que l'erreur a été détectée à la ligne 6 et que le pointeur "p" pointe vers une mémoire qui ne peut pas être atteinte.
Nous regardons le code ci-dessus et c'est bien-sûr un bête exemple où p est un pointeur nul et vous ne pouvez pas conserver une donnée dans un pointeur nul. Facile à réparer ...


 

Conclusion:

Nous avons vu des cas où vous pouvez vraiment trouver la cause de l'erreur sans connaître grand chose sur les méandres du fonctionnement d'un programme.
Je n'ai pas abordé le cas des erreurs fonctionnelles, par ex. un bouton dans une GUI qui est dans une mauvaise position mais qui fonctionne quand même. Dans ces cas, vous devrez en apprendre plus sur le fonctionnement interne du programme. Cela prend généralement plus de temps et donc, il n'y a pas de recette préfabriquée sur la méthode à appliquer.
Cependant, les techniques simples pour trouver les erreurs peuvent être quand même appliquées dans la plupart des cas.
Bonne chasse !

Sources de l'article


 

Blog
Le blog
La radio
La Radio
Boutique
La boutique du Geek
Php/Mysql
Formulaire en php
Administrer un serveur Mysql
Session en php
Gerer ses bases mysql
Les bases php
Securiser ses scripts PHP
Controler ses programmes avec RATS
Convertir une base sql en utf8
Astuces php
Le fichier php.ini
Programmation
Python rapide
Tutorial Python
Tutorial Perl
Tutorial Perl complet
Tutoriel ruby
Tutoriel C
Introduction à gawk
Filtres et utilitaires
Find
Programmation Shell
Ecriture de scripts bash
Expressions regulieres
Vi
Introduction a Javascript
Compiler avec gcc
Astuces en Bash
Cracking
Tutoriel Assembleur
Guide du cracking pour débutant
Assembleur
Manual Unpacking
Techniques de Protection
Différentes failles Web
Arp spoofing dans un réseau switché
Les intrusions
Les attaques externes
Defacage
Defacage complet
Buffer overflow
Netcat
Injection sql
Injection sql(suite)
John the Ripper
Spoofer un email
Utiliser google
La faille system
Usurper une identité
Le rooting
Shellcode sous Unix
La faille race condition
La faille xss
La faille xss (2)
Attaques sur un routeur
P2P
Azureus pas-a-pas
News
Lire les news de Linux-pour-lesnuls.com au format RSS
Distros
Gestion des paquets debian
101 commandes debian
Jeu
Webtarot
Graphisme
Effet neon dans GIMP
Effet vapeur dans GIMP
Cours fonctionnalités de GIMP
Redimentionner une image avec GIMP
Redimentionner une photo pour en faire un cadre avec gimp
Morphing avec gimp
Détourer avec gimp
Réduire le poids d'une image avec gimp
Caricature avec gimp
Humour
Ensemble
Divers