TetaTricks/chap/debug.tex

149 lines
4.4 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\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.
% ==============================================================
% ==============================================================