\section{Introduction}

  \subsection{Qu'est ce que le traçage de code?}

   Le traçage de code se rapproche beaucoup de l'ingénierie inversée
(reverse engineering). C'est une technique permettant de déterminée
l'utilité et le fonctionnement d'un programme. Le programme est alors
assimilée à une boîte noire dont on essaye de trouver qu'elle est la
fonction qui permet à partir d'une certaine entrée, d'obtenir la
sortie constatée.\\

  Pour ce faire on utilise des 'rétrotéchniques' qui nous permettent
de passer d'un code compréhensible par la machine, mais
pas par l'homme, à un code lisible par l'homme mais plus par la
machine. Il s'agit de décompiler ou de désassembler par exemple.\\

Le traçage de code peut être utiliser pour un grand nombre de raisons:\\
\begin{itemize}
  \item Analyser et comprendre un code inconnu (pour déterminer un
  algorithme par exemple),
  \item Récupérer du code source perdu (on ne possède plus qu'un
  éxecutable),
  \item Déterminer l'éxistance de virus ou de code parasite dans un
  programme,
  \item Déterminer des failles de sécurité, ou des erreurs (boggues),
  \item Déterminer les protections logicielles,
  \item Comprendre le fonctionnement d'un logiciel insuffisamment
  documenté, pour, par exemple, faire un logiciel qui intéropère avec
  le logiciel tracé.\\
\end{itemize}
Ceux-ci ne sont que des exemples d'applications, on peut en trouver
bien d'autres. Leur nombre fait du traçage de code une pratique très
courante.\\

  Pourtant le traçage de code pose des problèmes legaux. Les logiciels
sont soumis aux droits d'auteurs qui considèrent l'oeuvre logicielle
comme une oeuvre de l'esprit; les éléments de forme et la structure de
l'oeuvre originale sont protégeables. Plus particulièrement en France,
le droit d'auteur ainsi que le droit pénal et la loi Godfrain donnent
une qualification pénale au fait de pénétrer dans un système, élément
de la propriété d'autrui. Par ces lois les programmes sont protégés
des techniques de traçage de code. Une exception est faite toutefois
si le but est d'assuré l'interopérabilité, c'est-à-dire l'articulation
des logiciels les uns avec les autres. Pour le cas de la décompilation
en France on pourra se repporter à \cite{Bas02}, avec un exemple
d'application \cite{Dus00} et sur l'ingénierie inverse dans le monde
\cite{Cif95}).

  En résumé les téchniques de traçage de code ne doivent être
utilisées que sur des logiciels dont on est l'auteur, sur des
logiciels dont l'accés aux sources nous est autorisé, ou alors à des
fins de création d'un logiciel voulant échanger des données avec un
logiciel trop peu documenté.

  \subsection{Etat de l'art}

  Le traçage de code devient de plus en plus indispensable, et ce
retrouve à tous les niveaux. Les développeurs de microprocesseurs ont
bien compris cela, et sur les processeurs actuels, tel que le PowerPC
ou le Pentium d'Intel,
on trouve un certain nombre de fonctions facilitant le developpement
de logiciels. Ils intègrent des capacitées étendues de traçage de code
en temps réel, des broches supplementaires permettent de suivre les
changements d'adresses, d'autres options incluent le contrôle du
processeur (point d'arrêt, démarrage, etc...), des accès aux registres
et à la mémoire ainsi qu'un outil de mise au point. L'avantage d'une
telle méthode est que l'on peut travailler sur n'importe quel
executable en temps réel sans besoin de prétraitement, et surtout il
est utilisable sur des architectures multipipelines dont l'assembleur
et l'enchaînement des instruction est souvent dur à
comprendre. Pour plus d'informations sur le PowerPC voir \cite{IBM})
et \cite{Intel} pour le Pentium. Ces fonctions sont éxploitées par des
logiciels comme les debogueurs, par exemple GDB qui utilise des points
d'arrets hardware, ou par des interfaces hardware tel Nexus. Elle ne
sont donc jamais utilisées diretement par un développeur lambda.\\

  Ce qui nous amène à parler d'autres outils de traçage de code que
sont les débogueurs. Le but d'un débogueur est de permettre de suivre
ce qui se passe `` à l'interieur `` d'un programme de façon dynamiqe,
c'est à dire quand il s'éxecute, ou de savoir ce qu'il faisait lors
d'un arrêt volontaire ou involontaire. Pour une utilisation optimale
d'un débogueur l'éxecutable doit être compilé en mode ``debug''. Ceci
permet d'ajouter des informations tels que des symboles (noms de
variables, de fonctions, ...) et une correspondance entre la ligne
assembleur et le code source. Pour plus de renseignement sur sa
structure interne voir \cite{GDB}. Ainsi lors de son utilisation
interactive, le debogueur pourra faire le lien entre le code machine
et le code source. Les informations sont donc facilement exploitable
par le développeur. Les débogueur les plus connus sont GDB
\footnote{\url{http://sources.redhat.com/gdb/}} et SoftIce
\footnote{\url{http://www.compuware.com}}.\\

  Mais bien souvent nous n'avons pas les sources donc
l'utilisation d'un déboggeur s'avère délicate. On peut alors se servir
d'outils comme des traceurs d'appels système comme la commande
'strace' ou d'un bibliothèque dynamique 'ltrace'. Ceux ci sont basés autour
de l'appel système 'ptrace' qui permet de contrôler l'éxecution d'un
processus depuis le processus parent. L'utilisation de ces commandes
reste très simple, il suffit de lancer la commande avec en paramètre
la commande que l'on veut éxecuter. Nous aurons alors une trace de
tous les appels système ou de bibliothèque externe. Il est à noter que
ceci n'est nullement intéractif comme avec un débuggeur. Sur la trace
des appels systèmes on obtiendra des informations de haut niveau comme
la valeur des paramètres, ainsi que la valeur de retour des
fonctions, dans un format faclement lisible par l'homme.

  Mais parfois on aimerait pouvoir faire des modifications à
l'éxecutable. On passe alors par  un désassembleur pour transformer le
code machine en héxadécimal en langage assembleur lisible par
l'homme. Le plus gros problème du désassemblage est déterminer les
parties qui sont du code et les parties qui sont des données, car sur
les machines actuelles, tout est représenté de la même manière. On
trouve donc une grande différence entre des désassembleurs qui se
contentent de traduire les instructions comme la commande `objdump -D`
où l'on ne distingue pas le code des données et un désassembleur
commercial comme IDAPro
\footnote{\url{http://www.datarescue.com/idabase/ida.htm}}. Celui-ci sépare
automatiquement les données du code et reconnaît les points d'entrée des
bibliothèques communément utilisées par les compilateurs.
Le déassemblage est une opération statique, c'est-à-dire qu'on
désassemble l'ensemble d'un éxecutable sans l'éxécuter, il faut alors
le lire pour en comprendre son fonctionnement. Ceci requiert une
bonne connaissance de l'assembleur et la lecture de nombreuses lignes
de code. Ceci est d'autant plus handicapant que l'assembleur dépend
du processeur. Par contre on peut alors modifier et réassembler le
code pour obtenir un nouvel éxecutable.\\

  En résumé, actuellement si on veut tracer le code d'un programme il
faut soit posseder les sources pour pouvoir utiliser un débogueur,
soit on se limite au traçage des appels système et bibliothéques
dynamiques, voir au pire aux instructions assembleur. De plus si on
veut pouvoir modifier l'éxecutable on doit passer par de l'assembleur
aussi. Cela n'est vraiment pas pratique car de moins en moins de
personnes maitrîsent l'assembleur. De plus il faut lire un grand nombre
de lignes donc cela requiert beaucoup de temps. 

\subsection{Qu'est ce qu'un décompilateur?}

  En analysant l'état actuel du traçage de code on s'appercoit qu'il
manque un outil qui permettrait de passer d'un éxecutable en langage
machine vers en langage de haut niveau, idéalement un langage connu
par l'utilisateur, avec un minimum de lignes. C'est ce que fait un
décompilateur. Son fonctionnement est très similaire à un déassembleur,
c'est-à-dire qu'il agit d'une façon statique sur un éxecutble, sauf
que le format de sortie diffère. 

%% FIXME: On peut rajouter une partie sur les difficultées

  Le décompilateur suis un processus similaire à cleui d'un
compilateur, mais en sens inverse. Les différentes étapes sont:
\begin{itemize}
  \item Décoder le fichier binaire,
  \item Décoder les instructions machines en langage assembleur, comme
  un désassembleur,
  \item Faire une analyse sémantique de base pour reconnaitre des
  types de bas niveau comme les \texttt{long}, et pour simplifier les
  instructions,
  \item Charger les informations dans une forme intermediaire qui sera
  utilisée par la suite indépendament du langage assembleur source,
  \item Analyser le flot de données pour enlever de la représentation
  intermédiaire les aspects les plus bas niveaux comme les registres et
  les références à la pile,
  \item Analyser le flot de contrôle pour reconnaître les boucles et
  les sauts conditionnels, ainsi que leur degré d'imbrication,
  \item Analyser le typage pour retrouver les types du langage haut
  niveau,
  \item Générer le code de haut niveau depuis le code intermediare.
\end{itemize}

%% FIXME: Dire si c'est réalisable

Dans cet article nous allons nous interesser exclusivement à la
décompilation. Dans un premier temps nous allons voir les différentes
formes intermédiaires existantes. Puis nous allons nous intéresser aux
téchniques à base de graphes pour l'analyse de flot de données et de
contrôles. Enfin nous allons voir comment appliquer l'inférence de
type pour la reconstruction de type. Nous allons conclure sur les
différents compilateurs existants.


