Introduction
Lobfuscation vous permet de protéger votre code en le rendant totalement incompréhensible pour une personne ou un décompilateur.
Après avoir expliqué à quelle problématique répond lobfuscation, nous détaillerons les techniques standards quutilise un obfuscateur afin de protéger votre code puis nous verrons un exemple concret avec lobfuscateur .Net DotFuscator
Quest-ce que lobfuscation ?
L'obfuscation est une technique de masquage qui permet l'attribution transparente de nouveaux noms pour les symboles de vos assemblys dans le but de rendre incompréhensible (dun point de vue sémantique) le code qui pourrait être récupéré à laide dun décompilateur.
Lorsqu'elle est appliquée correctement, l'obfuscation permet d'accroître sensiblement la protection contre la décompilation, tout en laissant l'application intacte. Il est important de comprendre que lobfuscation en .NET se fait sur du code MSIL compilé (dll ou exécutable) et non pas sur le code source. Le code obfusqué est fonctionnellement identique au code classique et il va sexécuter dans le Common Langage Runtime (CLR) avec des résultats similaires.
Une obfuscation profonde crée une multitude de possibilités de décompilation, dont certaines peuvent générer une logique incorrecte lors de la recompilation.
Un obfuscateur évolué peut aussi permettre de faire échouer les décompilateurs.
Techniques dobfuscation
1) Attribution d'un nouveau nom aux identificateurs
L'attribution d'un nouveau nom est un moyen de rendre la sortie décompilée plus difficile à comprendre. Le remplacement des noms par des caractères non imprimables (ou des noms illégaux dans la langue cible) est inutile, car les décompilateurs présentent des options permettant de renommer de tels identificateurs.
a) La méthode Overload Induction
LOverload Induction est une technologie brevetée pour la méthode d'attribution d'un nouveau nom.
Tandis que la plupart des systèmes d'attribution d'un nouveau nom attribuent simplement un nouveau nom en remplacement de chaque ancien nom (cest-à-dire que getX() devient a() et getY() devient b()), la méthode Overload Induction impose une surcharge maximale. L'algorithme tente de renommer autant de méthodes que possible avec exactement le même nom. Il parvient ainsi plus lentement à des noms plus longs (tels que aa, aaa, etc.). Cela permet également de gagner de l'espace.
En plus de rendre le code difficilement compréhensible, l'attribution d'un nouveau nom présente un effet secondaire positif, à savoir la réduction de la taille du code. Cela permet également de gagner de l'espace en conservant des entrées de tas de chaînes (string heap). Le fait de tout remplacer par « a » signifie que « a » est stocké une seule fois et que chaque méthode ou champ renommé en « a » peut pointer sur cette entrée. La méthode Overload Induction renforce cet effet, car les identificateurs les plus courts sont réutilisés en permanence. Lorsque l'on sait que la méthode Overload Induction peut donner lieu à trois méthodes nommées a(), la compréhension du résultat décompilé sera pour le moins difficile.
L'algorithme Overload Induction breveté détermine toutes les collisions possibles de l'attribution d'un nouveau nom et ne provoque la surcharge des méthodes que lorsque cela peut être fait en toute sécurité.
b) La méthode Overload Induction améliorée
Une amélioration de l'«Overload Induction» consiste à utiliser le type de retour de la méthode comme critère afin de déterminer l'unicité de la méthode. Cette fonctionnalité vous permet un gain de 15% supplémentaire de redondance dans le renommage des méthodes. De plus, comme la surcharge sur les types retournés nest pas autorisée dans certains langages (comme C# et VB), cela entrave encore plus la décompilation.
2) Obfuscation du flux de contrôle
L'obfuscation du flux de contrôle traditionnelle introduit de fausses instructions conditionnelles et d'autres structures trompeuses afin de semer la confusion et, si possible, de faire échouer les décompilateurs.
Dotfuscator utilise l'obfuscation du flux de contrôle avancée :
Plutôt que d'ajouter des structures de code, il détruit les structures de code utilisées par les décompilateurs pour recréer le code source. Au final, le code est équivalent à l'original du point de vue sémantique, mais il ne contient aucun indice sur la façon dont le code d'origine était écrit. Même si des décompilateurs avancés sont utilisés, leur résultat consistera au mieux à deviner les structures.
3) Cryptage de chaînes utilisateur
Dans la mesure où le cryptage de chaînes engendre une légère dégradation de la vitesse d'exécution (pour le décryptage à la volée lors de l'utilisation de la chaîne), le cryptage nest à utiliser que pour les sections sensibles de votre code.
4) Réduction du code
En général, plus lapplication est petite et plus le temps de chargement et dexécution est rapide. De plus, pour les environnements embarqués, comme le compact framework .NET, les économies en taille sont cruciales.
Quest-ce que l'élagage («Pruning») ?
Les développeurs utilisent souvent des librairies et des types qui ont été écrits pour être réutilisés mais au final, une petite partie seulement du code est exploitée. L'élagage va servir à enlever tout ce code non utilisé et aura pour conséquence bénéfique de réduire la taille de lexécutable ce qui améliorera les performances au niveau des temps de réponse et de la mémoire.
L'analyse statique parcourt le code, en commençant par un ensemble de méthodes appelées « déclencheurs ». Il s'agit des points d'entrée de votre application. En général, toute méthode qui doit appeler des applications externes doit être définie en tant que déclencheur. Par exemple, dans une application autonome simple, la méthode « Main » doit être définie en tant que déclencheur.
Lorsque lobfuscateur parcourt le code de la méthode de chaque déclencheur, il note quels champs, méthodes et types sont utilisés. Il analyse ensuite de façon similaire toutes les méthodes appelées. Le processus se poursuit jusqu'à ce que toutes les méthodes appelées aient été analysées.
Une fois l'opération terminée, lobfuscateur peut déterminer un ensemble minimum de types et les membres nécessaires à l'exécution de l'application. Seuls ces types seront inclus dans l'assembly de sortie.
Exemple
Voici maintenant un exemple concret d'obfuscation d'une dll .Net grâce à l'outil Dotfuscator.
L'objectif final est d'obtenir une dll dont la majeure partie du code sera obfusqué, tout en laissant à disposition les méthodes principales susceptibles d'être appelées.
- Visualisation de lassembly dans Reflector avant l'obfuscation :
L'analyse de la dll au travers de Reflector permet de révéler la façon dont elle est construite ainsi que les classes qui la composent avec leurs les méthodes et propriétes associées.

- Démarrer DotFuscator à partir de Visual Studio:

- Sélection de lassembly à obfusquer :

- Sélection des classes à obfusquer :

- Génération de lassembly obfusqué :

- Visualisation dans Reflector après lobfuscation :
Les classes de l'assembly sont maintenant totalement incompréhensibles et on peut voir que les seuls classes mises à disposition sont celles que l'ont n'a pas obfusquées.

Restriction
Le fait de protéger son code peut néanmoins amener à un autre extrême puisque vous pourriez arriver dans une situation où le code est trop protégé et empêche son exploitation dans certains cas particuliers.
Voici les inconvénients que lobfuscation peut entrainer et les solutions pour contourner ces problèmes.
- Réflexion et chargement dynamique de classes
Dotfuscator ne dispose d'aucun moyen de prévoir les noms de type qui seront saisis par l'utilisateur. Si lon veut invoquer un membre par réflexion, il faut connaître le nom de celui-ci. La solution consiste à exclure les noms de tous les types que lon veut être en mesure de charger. - Message derreur de la trace de la pile
A cause du renommage, les messages derreurs peuvent devenir difficilement compréhensibles.
Néanmoins, Dotfuscator peut créer un fichier qui va contenir la cartographie de ce qui a été renommé grâce à lobfuscation incrémentale afin de vous permettre de retrouver le nom réel des méthodes affichées dans le message et dans la trace de la pile.
Conclusion
En résumé, lobfuscation vous permet dobtenir des exécutables et des dlls protégés contre la décompilation tout en les rendant plus performants et plus optimisés, tout cela sans toucher à votre code source.
Quelques Liens
Article sur l'obfuscation sur la MSDN
Blog MSDN sur l'obfuscation
Site de Dotfuscator
Site sur Dotfuscator et l'obfuscation en français