149 lines
4.4 KiB
TeX
149 lines
4.4 KiB
TeX
\chapter{Debug}\index{Debug}
|
||
\label{chap:debug}
|
||
|
||
Quand plus rien ne marche, reste-il encore un espoir ?
|
||
Il existe bien entendu des outils \textsl{mainstream}
|
||
tels que le classique \texttt{gdb}\index{gdb}, mais il en existe
|
||
une foultitude d'autres, injustement méconnus.
|
||
Nous allons en voir quelques-uns.
|
||
|
||
% ==============================================================
|
||
|
||
\section{Gdb}\index{gdb}
|
||
|
||
|
||
GDB is a source-level debugger, capable of breaking programs at
|
||
any specific line, displaying variable values, and determining
|
||
where errors occurred. Currently, gdb supports C, C++, D,
|
||
Objective-C, Fortran, Java, OpenCL C, Pascal, assembly, Modula-2,
|
||
Go, and Ada. \textsc{A must-have for any serious programmer}.
|
||
|
||
\subsection{ddd}
|
||
|
||
DDD is a graphical front-end for GDB and other command-line debuggers.
|
||
Using DDD, you can see what is going on “inside” another program while
|
||
it executes—or what another program was doing at the moment it crashed.
|
||
|
||
\subsection{xxgdb}
|
||
|
||
xxgdb is a simple but powerful graphical interface to the GNU
|
||
debugger gdb. A more powerful (but slower and much bigger) interface
|
||
is available in the ddd package.
|
||
|
||
% ==============================================================
|
||
|
||
\section{Strace}\index{strace}
|
||
|
||
Strace permet de tracer les appels systèmes d'un processus.
|
||
Comme vous le savez tous, un appel système
|
||
(aka syscall\index{syscall})
|
||
est \textbf{le} moyen de communication qu'utilise un process
|
||
utilisateur pôur demander un service au noyau.
|
||
|
||
\lstinputlisting[language=C]{code/hello.c}
|
||
|
||
Un exemple canonique, n'est-il pas ? Ce bout de code affichant
|
||
quelque chose à l'écran, il doit bien y avoir un appel au noyau
|
||
qui traine par là pour écrire vers la sortie standard.
|
||
Nous allons donc le chercher%
|
||
\footnote{En trichant un peu, je l'avoue, je connais son nom.}
|
||
|
||
\begin{verbatim}
|
||
$ gcc hello.c
|
||
$ strace -o foo ./a.out
|
||
$ grep write foo
|
||
write(1, "Hello world.\n", 13) = 13
|
||
$
|
||
\end{verbatim}
|
||
|
||
On peut réaliser la même opération en utilisant le filtrage interne
|
||
de strace, ce qui évite le passage par un
|
||
fichier intermédiaire, mais le résultat est moins lisible,
|
||
puisque a.out écrit sur stdout, et strace sur stderr~:
|
||
|
||
\begin{verbatim}
|
||
$ strace -e write ./a.out
|
||
write(1, "Hello world.\n", 13Hello world.
|
||
) = 13
|
||
+++ exited with 0 +++
|
||
$
|
||
\end{verbatim}
|
||
|
||
|
||
% ==============================================================
|
||
|
||
\section{LD\_PRELOAD}\index{LD\_PRELOAD}
|
||
|
||
D'accord, aves \texttt{strace} nous pouvons voir passer les
|
||
appels systèmes, mais dans la vie d'un process, certaines
|
||
opérations ne sortent pas de la \texttt{libc}\index{libc}.
|
||
L'une d'entre elles, \texttt{getenv(3)}, va nous servir d'exemple.
|
||
|
||
\begin{verbatim}
|
||
NAME
|
||
getenv - get an environment variable
|
||
SYNOPSIS
|
||
#include <stdlib.h>
|
||
char *getenv(const char *name);
|
||
DESCRIPTION
|
||
The getenv() function searches the environment list to find the envi‐
|
||
ronment variable name, and returns a pointer to the corresponding value
|
||
string.
|
||
\end{verbatim}
|
||
|
||
Cette fonction est utilisée par un logiciel pour avoir accès à
|
||
son contexte extérieur, son environnement, dans lequel on
|
||
peut trouver (entre autres) la variable \texttt{\$LOGNAME} qui,
|
||
oh surprise, contient votre nom de login.
|
||
|
||
Et justement, vous avez un programme sous la main que vous suspecter
|
||
d'avoir un problème relationnel avec cette variable.
|
||
Il nius faut donc remplacer le getenv de la libc par notre propre
|
||
version qui va écouter et exfiltrer l'utilisation de cette
|
||
fonction.
|
||
|
||
\begin{lstlisting}[language=C]
|
||
/*
|
||
spy_getenv.so: spy_getenv.c Makefile
|
||
gcc -Wall -shared -fPIC $< -ldl -o $@
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#define __USE_GNU
|
||
#include <dlfcn.h>
|
||
typedef char * (*original_getenv)(const char *envname);
|
||
|
||
char *getenv(char *envname)
|
||
{
|
||
static char *arrow = "--getenv--> ";
|
||
static char *wtf = " --> WTF ?";
|
||
char *content;
|
||
|
||
original_getenv orig_getenv;
|
||
orig_getenv = (original_getenv)dlsym(RTLD_NEXT, "getenv");
|
||
|
||
write(STDERR_FILENO, arrow, strlen(arrow));
|
||
write(STDERR_FILENO, envname, strlen(envname));
|
||
content = orig_getenv(envname);
|
||
if (NULL != content) {
|
||
write(STDERR_FILENO, "=", 1);
|
||
write(STDERR_FILENO, content, strlen(content));
|
||
}
|
||
else {
|
||
write(STDERR_FILENO, wtf, strlen(wtf));
|
||
}
|
||
|
||
write(STDERR_FILENO, "\n", 1);
|
||
return content;
|
||
}
|
||
\end{lstlisting}
|
||
|
||
Simple et efficace.
|
||
|
||
% ==============================================================
|
||
|
||
|
||
% ==============================================================
|