Astuce Java 22: Protégez vos bytecodes de l'ingénierie inverse / décompilation

Si vous écrivez des classes Java et que vous les distribuez sur Internet, sachez que les utilisateurs peuvent effectuer une rétro-ingénierie, désassembler ou décompiler vos classes en code source Java. Le décompilateur le plus utilisé (au moins publiquement) est Mocha. Mocha lit un ou plusieurs fichiers de bytecodes (classes) et les reconvertit en code source Java. Bien que le code généré par Mocha ne soit pas exactement le même que le code source d'origine, il est suffisamment proche pour que quelqu'un le comprenne et le modifie. Si vous souhaitez développer des classes Java et les distribuer sur Internet - et que vous souhaitez les protéger contre la décompilation - lisez la suite.

Moka: un exemple

Avant de présenter Crema, nous allons parcourir un exemple utilisant Mocha. Le programme simple suivant affiche la chaîne «Bonjour» à l'écran:

test de classe {public static void main (String argv []) {System.out.println ("Bonjour"); }}

Si les quatre lignes ci-dessus étaient enregistrées dans un fichier test.java,, la compilation test.javagénérerait un nouveau fichier test.class,, contenant les bytecodes Java représentant ce code source Java. Maintenant, exécutons Mocha sur le fichier de classe et voyons la sortie Mocha:

% java mocha.Decompiler test.class // l'invite% est mon shell C sous UNIX. 

La commande ci-dessus génère un fichier appelé test.mocha, qui contient le code source Java généré par Mocha:

% plus de test.mocha / * Décompilé par Mocha à partir de test.class * / / * Initialement compilé à partir de test.java * / import java.io.PrintStream; test de classe {public static void main (String astring []) {System.out.println ("Bonjour"); } test () {}}

Comme vous pouvez le voir dans l'exemple ci-dessus, Mocha nous a donné un code source Java facile à lire et à comprendre. Si vous copiez ce fichier test.java, compilez-le à nouveau et exécutez-le, il se compilera et fonctionnera parfaitement.

Crema à la rescousse!

Alors, comment pouvez-vous protéger vos classes contre la décompilation? Une réponse est Crema. Crema brouille les informations symboliques de vos .classfichiers afin qu'ils deviennent moins vulnérables à la décompilation. Les informations symboliques que Crema brouille incluent le nom de la classe, sa superclasse, les interfaces, les noms de variables, les méthodes, etc. Ces noms symboliques sont nécessaires à la machine virtuelle Java (JVM) pour lier vos classes aux packages de bibliothèque. Crema brouille ces noms symboliques et y fait des références de la même manière afin que la JVM puisse toujours réaliser la liaison correcte entre les classes et les packages.

Alors, comment fonctionne Crema? En gros, avant de distribuer vos fichiers de cours sur Internet, exécutez Crema dessus. Crema brouillera les informations symboliques qu'ils contiennent et placera chaque nouvelle classe dans le fichier 1.crema. Votre travail consiste alors à renommer 1.cremaquelque chose comme filename.classavant de le distribuer sur Internet.

Exécutons Crema sur notre test.classexemple ci-dessus, puis essayez de le décompiler avec Mocha:

% java Crema -v test.class // -v est une option pour activer le mode // verbeux. Il existe de nombreuses autres options. CREMA - The Java Obfuscator - VERSION D'ÉVALUATION Copyright (c) 1996 Hanpeter van Vliet Chargement test.class Test d'obfuscation Enregistrement du test en tant que 1.crema REMARQUE: Les classes traitées avec la version d'évaluation de Crema ne peuvent être utilisées que localement, car la plupart des navigateurs refusent de le faire chargez-les. Pour la version complète de Crema, pointez votre navigateur vers: //www.inter.nl.net/users/HPvan.Vliet/crema.html (voir Ressources)

La commande ci-dessus a généré un nouveau fichier 1.crema, qui contient les bytecodes avec des informations symboliques brouillées. Notez que Crema a de nombreux paramètres d'option de ligne de commande que vous pouvez utiliser; pour plus d'informations sur Crema, consultez la section Ressources.

Maintenant, déplaçons ce fichier à test.classnouveau et décompilons-le en utilisant Mocha:

% mv 1.crema test.class% java mocha.Decompiler test.class java.lang.NullPointerException SIGSEGV 11 * violation de segmentation si_signo [11]: SIGSEGV 11 * violation de segmentation si_errno [0]: erreur 0 si_code [1]: SEGV_ACCERR [ addr: 0x0] stackbase = EFFFF35C, stackpointer = EFFFF040 Dump complet du thread: "Finalizer thread" (TID: 0xee3003b0, sys_thread_t: 0xef490de0) prio = 1 "Async Garbage Collector" (TID: 0xee300368, sys_thread_t "prio 1" thread "(TID: 0xee300320, sys_thread_t: 0xef4f0de0) prio = 0" gestionnaire d'horloge "(TID: 0xee3001f8, sys_thread_t: 0xef5b0de0) prio = 11" main "(TID: 0xee3000a0, sys_thread_t: 0x835 * courant java0) .lang.Throwable.printStackTrace (Throwable.java) java.lang.ThreadGroup.uncaughtException (ThreadGroup.java) java.lang.ThreadGroup.uncaughtException (ThreadGroup.java) Monitor Cache Dump: Registered Monitor Dump: Finalize me verrouillage de la file d'attente: sans propriétaire Verrou de la file d'attente des threads: sans propriétaire Verrou de classe: sans propriétaire Verrou de la pile Java: sans propriétaire Verrou de réécriture de code: sans propriétaire Verrouillage du tas: sans propriétaire A verrouillage de la file d'attente de finalisation: sans propriétaire Verrouillage E / S du moniteur: sans propriétaire Moniteur de décès d'enfant: sans propriétaire Moniteur d'événements: sans propriétaire Moniteur d'E / S: sans propriétaire Moniteur d'alarme: sans propriétaire En attente d'être notifié: "gestionnaire d'horloge" Verrou Sbrk: sans propriétaire Verrou du cache du moniteur: sans propriétaire Registre du moniteur: propriétaire du moniteur: Alarme de thread "principal" Q: Abandonner (core dumpé)sans propriétaire Moniteur d'alarme: sans propriétaire En attente d'être notifié: "gestionnaire d'horloge" Verrou Sbrk: sans propriétaire Verrou du cache du moniteur: sans propriétaire Registre du moniteur: propriétaire du moniteur: Alarme de thread "principal" Q: Abandon (core dumpé)sans propriétaire Moniteur d'alarme: sans propriétaire En attente d'être notifié: "gestionnaire d'horloge" Verrou Sbrk: sans propriétaire Verrou du cache du moniteur: sans propriétaire Registre du moniteur: propriétaire du moniteur: Alarme de thread "principal" Q: Abandon (core dumpé)

Comme vous pouvez le voir dans le code ci-dessus, la première chose dont Mocha se plaint est NullPointerExceptionparce qu'il était confus à propos des informations symboliques. Par conséquent, notre objectif de rendre difficile la décompilation de notre code est atteint.

Il est à noter que l'auteur de Mocha, Hanpeter van Vliet, est également l'auteur de Crema! Le moka est distribué gratuitement. Une copie d'évaluation de Crema est disponible gratuitement, mais la version complète est un produit commercial.

Lorsque vous distribuez des classes Java sur Internet, vous pouvez protéger votre bytecode Java contre le risque de rétro-ingénierie. Les exemples de code ci-dessus montrent comment Mocha est utilisé pour effectuer la décompilation et comment Crema peut venir à la rescousse en empêchant une telle activité.

Qusay H. Mahmoud est un étudiant diplômé en informatique à l'Université du Nouveau-Brunswick, campus de Saint John, Canada.

En savoir plus sur ce sujet

  • Note de l'éditeur: Depuis la mort de M. van Vliet (d'un cancer), les sites qu'il a mis en place pour la distribution de Mocha et Crema ont cessé d'exister.
  • Site de distribution d'Eric Smith Mocha //www.brouhaha.com/~eric/computers/mocha.html
  • Crema sur le site du CERN //java.cern.ch:80/CremaE1/DOC/quickstart.html

Cette histoire, "Java Astuce 22: Protégez vos bytecodes contre l'ingénierie inverse / décompilation" a été initialement publiée par JavaWorld.