Protection, optimisation et déploiement de code .NET grâce à XenoCode 2005

LEBRUN Thomas

Cet article vous expliquera comment protéger, optimiser et déployer vos applications .NET grâce à XenoCode 2005.

N'hésitez pas à commenter cet article : Commentez .

- Titre : Protection, optimisation et déploiement de code .NET grâce à XenoCode 2005

- Auteur : LEBRUN Thomas

- Parution : 10 janvier 2005

- Mise à jour : 1 mars 2020

- Licence : Copyright ® 2005 LEBRUN Thomas. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Pourquoi protéger et optimiser vos déploiements d'applications ?

- I-B. Qu'est-ce-que XenoCode ?

- II. XenoCode

- II-A. Description

- II-B. Utilisation

- II-B-1. Ajout d'assembly

- II-B-2. Protection d'assembly

- II-B-3. Optimisation d'assembly

- II-B-4. Sortie

- III. Résultats

- IV. Conclusion

- V. Mon avis

- VI. Vidéo

- VII. Téléchargements

- VIII. Liens

I - Introduction

I-A - Pourquoi protéger et optimiser vos déploiements d'applications ?

Actuellement, lorsque vous compilez une application .NET, le code généré est compilé dans un langage intermédiaire : le CIL (Common Intermediate Language). De plus, le code .NET est autodescriptif, c'est-à-dire que les exécutables .NET contiennent des métadonnées qui décrivent les types, méthodes, champs et événements utilisés dans les instructions IL. Bien que très pratique, cela implique que la plus grosse partie de l'information de la source originale du programme est préservée. L'utilisation de programmes appelés décompilateurs (tel que ILDASM, Intermediate Language Disassembler, ou Reflector) permet d'exploiter cette information et ainsi de reconstituer une grande partie du code source original, à partir de l'application .NET.

Concrètement, cela signifie que lorsque vous distribuez une de vos applications .NET, vous en fournissez, sans même le savoir ou le vouloir, le code source. Il est donc aisé de recopier tout ou une partie de votre code, à votre insu.

Pour pallier ce problème, vous pouvez utiliser l'obfuscation du code. Cette technique consiste à « brouiller » le code source lors de la compilation de votre application. Dès lors, l'utilisation de décompilateur ne permettra pas de révéler le code source.

L'utilisation d'obfuscateurs (les outils permettant l'obfuscation du code) ne garantit pas une sécurité totale de votre code : toutefois, cela vous permet une forte protection que vous n'avez pas en natif.

I-B - Qu'est-ce-que XenoCode ?

XenoCode est un obfuscateur qui possède également d'autres fonctionnalités intéressantes :

- compression des assembly de sortie ;

- élimination du code mort ;

- incorporation du Framework .NET ;

- réalisation d'assembly « Tout-en-un » : les projets multiassembly peuvent être réunis en une seule assembly, afin de faciliter le déploiement ;

- …

XenoCode vous permet donc de protéger, d'optimiser et de déployer vos applications .NET, tout cela très rapidement.

II - XenoCode

II-A - Description

Voici quelques aperçus d'écrans de l'application :

Écran principal :

Écran Protection :

Écran Optimisation :

Écran Sortie :

Comme vous pouvez le constater, l'interface de XenoCode est sobre et intuitive : la prise en main est donc immédiate, ce que nous allons voir dans la section suivante.

II-B - Utilisation

Utiliser XenoCode n'est pas compliqué en soi. Vous pouvez :

- soit charger une assembly dans XenoCode et lancer directement le travail : à ce moment-là, XenoCode utilisera les options par défaut ;

- soit, charger une assembly, puis modifier les options avant de lancer XenoCode.

Cet article ne vous détaillera que la deuxième technique, la première ne servant qu'à obfusquer l'assembly, sans rien faire d'autre (pas de compression, pas d'optimisation, etc.).

II-B-1 - Ajout d'assembly

La première chose à faire est donc d'ajouter la (ou les) assembly que vous voulez protéger et/ou optimiser.

Pour cela, vous pouvez soit cliquer sur le bouton « Ajouter », soit cliquer, dans le menu, sur « Fichier -> Ajouter une assembly ».

À ce moment-là, XenoCode rajoute, dans la fenêtre principale, la liste des assemblys que vous avez sélectionnées, comme le montre cet aperçu :

Si votre application possède plusieurs assemblys, vous devez toutes les ajouter au projet XenoCode, afin que les différents paramètres que vous avez choisis (cryptage, compression, etc.) soient propagés automatiquement à toutes les assemblys.

Un coup d'œil sur le panneau Statistiques vous renseigne sur quelques informations à propos de vos assemblys (nombre de classes, de méthodes, de propriétés, etc.).

XenoCode vous permet d'appliquer des réglages prédéfinis (au moyen de la ComboBox « Préréglages ») : ce sont des configurations chargées à l'avance et que vous n'avez plus qu'à appliquer afin de les utiliser. Elles couvrent la majeure partie des possibilités qu'un développeur pourrait désirer. Nous allons d'ailleurs vous donner un peu plus d'informations sur chacun de ces préréglages :

Configuration

Description détaillée

Agressif

C'est le mode recommandé pour la plupart des projets (c'est d'ailleurs le mode appliqué par défaut lors du chargement de XenoCode).
- Les symboles sont renommés.
- L'obfuscation du flux de contrôle est activée.
- Le cryptage des chaînes et l'élimination du code mort sont désactivés.

Conservateur

C'est le mode recommandé pour les applications ASP.NET.

Minimiser la taille

Ce mode est à utiliser dans le cas d'applications développées pour le Compact Framework.
- L'élimination du code mort et des métadonnées est activée.
- Les autres fonctions de protection/optimisation sont désactivées.

Métadonnées uniquement

Utiliser pour les applications en .NET Remoting, toutes les métadonnées sont laissées intactes.

Null

Préréglage à utiliser uniquement dans le cas où vous devez effectuer du débogage.

Un clic sur le bouton « Rechercher les dépendances » vous permet d'afficher la liste de toutes les dépendances de l'assembly que vous avez chargée.

Vous pouvez également ajouter une ou plusieurs de ces dépendances à votre projet XenoCode.
Une fonctionnalité intéressante qu'il convient de vous expliquer (et qui peut intéresser les développeurs souhaitant distribuer leurs applications en nombre limité) est la possibilité de personnaliser votre assembly, en incorporant des informations que vous pourrez rendre visibles dans votre application (dans une fenêtre « À propos » par exemple).

Vous pouvez par exemple, enregistrer un nom et un numéro de version, ou bien un numéro de série, etc. bref n'importe quelle chaîne de caractères qui vous convient.

Pour cela, remplissez le (ou les) champ de la partie « Identification du produit ». Ces champs sont cryptés au moyen de la clé que vous pouvez renseigner en cliquant sur « Options -> Sélectionner la phrase clé… ».

Pour pouvoir afficher ces chaînes de caractères dans votre application, c'est très simple : vous devez juste utiliser, dans une chaîne de caractères, un jeton/une constante du type #WATERMARKx# où x correspond au numéro de la chaine que vous voulez afficher. Par exemple :

String.Format("Numéro de série : {0}", #WATERMARK3#);

affichera « Numéro de série : » suivi du numéro de série que vous avez spécifié dans XenoCode.

Une fois votre liste d'assemblys chargée, il est temps de passer à l'onglet « Protection », afin de voir comment protéger au mieux votre code.

II-B-2 - Protection d'assembly

Cette page se compose de plusieurs parties, que nous allons analyser en essayant de partir de la plus basique en allant vers la plus importante.

La partie « Suppression du désassemblage » tout d'abord : c'est ici que vous spécifiez si vous voulez empêcher que ILDASM puisse décompiler votre assembly.

Vous avez également la possibilité d'interdire les autres outils de désassemblage : ainsi, des utilitaires tels que Reflector ne seront d'aucune utilité pour tenter de percer les secrets de votre code source.
Cela vous permet alors d'accroître la sécurité.

L'obfuscation du code de contrôle est la partie qui vous permet d'empêcher le « Reverse Engineering » (ou Ingénierie Inverse), en brouillant les chemins d'exécution du programme.

Pour votre culture personnelle, sachez que le « Reverse Engineering » est la technique de piratage la plus connue : elle consiste en la décompilation d'un programme afin d'essayer de traduire le code machine en code compréhensible par l'homme.

Vous avez donc la possibilité de spécifier le niveau d'obfuscation du code que vous voulez appliquer à votre assembly.

Plus le niveau est élevé, plus le Reverse Engineering est difficile à réaliser, mais plus la taille de votre assembly est importante.

Le cryptage de chaînes vous permet d'augmenter encore la sécurité de votre assembly, en cryptant les chaînes de caractères.

Pour utiliser cette fonction, il vous suffit de cliquer sur le bouton « Sélectionner les chaînes ». Une fenêtre contenant la liste de toutes les chaînes de caractères de votre assembly sera alors affichée.

Sélectionnez la (ou les) chaîne(s) que vous désirez crypter puis cliquez sur le bouton « OK ». Cette technique peut s'avérer d'autant plus pratique si, pour une raison ou une autre, vous êtes dans l'obligation de stocker, en dur dans votre code, des noms d'utilisateur et/ou mots de passe.

La dernière partie, le renommage du nom des symboles, est donc la partie la plus importante pour protéger votre code .NET.

Cette technique permet de brouiller le nom des classes, méthodes, propriétés, etc. contenus dans votre assembly.

Par défaut, XenoCode fait en sorte que tout ce qui compose votre assembly soit brouillé, mais vous pouvez changer cela en naviguant vers le symbole que vous ne voulez pas protéger, et en désélectionnant la case qui se trouve à côté.

Le bouton « Sélectionner par filtre » vous permet de ne choisir que le type de symboles que vous désirez.

En passant, noter la CheckBox « Afficher les signatures », qui vous permet, comme son nom l'indique, d'afficher les signatures des symboles (c'est-à-dire la valeur de retour plus le nom du symbole) plutôt que leur nom.

Si, dans votre assembly, vous utilisez des symboles référencés par leur nom via l'API System.Reflexion, vous ne devrez pas les brouiller, car sinon, des exceptions risquent de se produire à l'exécution de votre application, dues au fait que l'objet auquel on essaye d'accéder n'est plus accessible à partir de son nom d'origine. Pour résumer, vous ne pourrez plus accéder à l'objet que vous voulez atteindre, car celui-ci aura été brouillé.

Une fois que vous avez défini ce que vous vouliez protéger, et avec quel degré de protection, voyons comment vous pouvez faire en sorte d'optimiser votre assembly. Pour cela, passez dans l'onglet « Optimisation ».

II-B-3 - Optimisation d'assembly

Dans cette partie, vous verrez comment paramétrer XenoCode pour que celui-ci utilise au mieux ces options d'optimisation.

Tout d'abord, la réduction des métadonnées : elle permet de convertir l'information symbolique présente dans vos assemblys .NET en une représentation minimale. Par exemple, une classe nommée UneClasse, sera convertie en un seul caractère : « U », de la même façon que les propriétés, les champs, etc.

Cette conversion permet la réduction de la taille de votre assembly.

Le profilage de code, quant à lui, est un outil que je ne vous conseillerai que dans les phases de débogage. En effet, son rôle est de générer un fichier texte qui contient l'ensemble des entrées et sorties des méthodes de votre assembly. Très utile donc pour détecter les possibles fuites mémoire ou autres, cette fonctionnalité n'apporte pas de réelle optimisation à l'assembly final.

La compression de la sortie, quant à elle permet, comme son nom l'indique, de compresser les assemblys que vous générez. La compression n'est réellement efficace que sur des assemblys de grandes tailles, la différence étant minime sur les assemblys plus légères.

L'élimination du code mort est la partie qui vous permet de supprimer, de votre assembly, les méthodes et les métadonnées inutilisées.

Ce que l'on appelle le code mort est en fait du code qui n'est jamais appelé lors de l'exécution d'un programme. Enlever ce code permet donc de réduire la taille de votre assembly, sans en modifier son comportement, étant donné que ce code n'est jamais appelé : il ne « sert à rien ».

Pour utiliser cette fonctionnalité, vous devez spécifier à XenoCode un ensemble de points d'entrée (ce sont les méthodes à partir desquelles l'exécution peut commencer).

Pour cela, cliquez sur le bouton « Sélectionnez les points d'entrée » : à ce moment, une boîte de dialogue contenant l'ensemble des méthodes de l'assembly est affichée.

La plupart du temps, un clic sur le bouton « Auto » sélectionnera automatiquement les points d'entrée et il n'est pas nécessaire de modifier la liste. Mais si vous le désirez, vous pouvez ajouter vous-même une méthode et l'ajouter en tant que point d'entrée, en la sélectionnant et en cliquant sur le bouton « Ajouter ». Voici un aperçu typique de ce que vous obtenez :

Maintenant que vous avez vu comment ajouter des assemblys, comment les protéger puis enfin comment les optimiser, voyons comment les générer, en passant dans l'onglet « Sortie ».

II-B-4 - Sortie

XenoCode vous permet de générer deux types d'assemblys :

- les multiassemblys ;

- les assemblys Tout-en-un.

Dans le premier cas, il y a autant d'assemblys générées que d'assemblys que vous avez ajoutées au projet (si vous avez ajouté trois assemblys, il y aura trois assemblys de générées).

Le deuxième cas est plus particulier : il vous permet de ne générer qu'une seule assembly regroupant toutes les assemblys que vous avez ajoutées, au départ, au projet.

L'utilisation de la deuxième méthode vous permet d'accéder au paramètre d'édition de liens : si votre projet utilise des DLL fournies par un autre éditeur, il peut être intéressant pour vous de tout réunir dans une seule assembly (le déploiement en est ainsi facilité).

Mais pour cela, vous devez sélectionner quelle sera l'assembly « principale », c'est-à-dire celle à partir de laquelle les attributs globaux tels que l'icône de l'application, le nom du fichier, etc. doivent être hérités. C'est donc tout naturellement sous « Hériter des attributs de l'assembly » que vous allez indiquer le nom de cette assembly « mère ».

Vous avez également la possibilité d'incorporer, dans votre assembly, le framework .NET, ce qui permet de résoudre l'un des problèmes auquel la plupart des développeurs sont confrontés :« Comment faire fonctionner mon application .NET sur un poste où le framework .NET n'est pas installé ? » (Pour être exact, pour le moment, seules les bibliothèques sont incorporées, en attendant que l'incorporation du runtime soit parfaitement fiable).

Avec cette option, cette question ne se pose plus : vous pouvez spécifier quelle version du framework votre assmbly devra embarquer (dans le cas où vous avez plusieurs versions du framework sur votre poste servant à la génération des assemblys).

Notez que l'utilisation de cette fonction augmente la taille des assemblys produites !

Vous pouvez ensuite spécifier le chemin vers le fichier de la paire de clés de nom fort (fichier .snk) dans le cas où vous désirez signer votre assembly pour pouvoir l'enregistrer dans la GAC (Global Assembly Cache).

Le dernier paramètre à fournir est tout simplement le chemin où l'assembly « finale » devra être générée.

Vous êtes maintenant prêt à générer votre assembly : un clic sur le bouton « Aperçu des assemblys » vous donnera un aperçu de ce à quoi ressemblera le code source de votre assembly après « xenocodage ».

Un clic sur le bouton « XenoCodez les assemblys » se charge alors de faire le travail : il vous sera alors peut-être demandé d'accepter un contrat de licence, si vous avez choisi d'incorporer, dans votre assembly, le framework .NET.

III - Résultats

Voici un aperçu des résultats sur une application de test.

L'application a été « xenocodée » avec un réglage agressif, une obfuscation du flux de contrôle au maximum, l'élimination du code mort et la compression de sortie activée.
La framework .NET 1.1 a également été incorporé à l'assembly finale.

Taille de l'application avant XenoCode

Taille de l'application après XenoCode

32,0 Ko

1,65 Mo

Voici le résultat sous Réflector avant :

Puis après

Même chose avec ILDASM :

Avant :

Après :

IV - Conclusion

XenoCode, outil d'obfuscation et d'optimisation de code .NET, est un produit très complet. Toutes ses possibilités ne vous ont pas été montrées dans cet article, mais vous en avez suffisamment vu pour vous faire votre propre avis.

Pour information, XenoCode est une application .NET qui utilise sa propre technologie : vous ne pourrez donc pas utiliser un décompilateur pour tenter de découvrir son code source.

V - Mon avis

Après avoir testé XenoCode, je ne peux rien faire d'autre que le recommander.
En effet, celui-ci est simple à utiliser et rapide, bien qu'un peu gourmand en mémoire à certains moments par exemple à la fin de la génération d'une assembly).
Après un tour d'horizon des plus instructifs de XenoCode, les résultats sont on ne peut plus convaincants et parlent d'eux-mêmes : code optimisé et protégé, assemblys compressées (idéal pour le déploiement d'applications lourdes), intégration du framework .NET, etc.

Pour faire simple, je dis oui à XenoCode et je vous invite à l'essayer par vous-même afin de juger s'il serait intéressant pour vous. Un nouvel outil à ajouter à votre boite à outils ;)

VI - Vidéo

Je vous joins une vidéo qui vous montre comment utiliser XenoCode : ce qui est filmé est en fait l'application de test qui est proposée en téléchargement à la fin de l'article.

Vidéo explicative (5 minutes - 2,84 Mo).

VII - Téléchargements

Je vous joins les fichiers de l'application de test que j'ai utilisés pour tester XenoCode, dans le cas où vous seriez juste intéressé par le résultat :

Application non xenocodée
Application xénocodée

VIII - Liens

Site Web de XenoCode
Téléchargez la version d'évaluation de XenoCode
Achetez XenoCode