Q: Si je crypte mes fichiers .class et que j'utilise un chargeur de classe personnalisé pour les charger et les décrypter à la volée, cela empêchera-t-il la décompilation?
R: Le problème de la prévention de la décompilation du code d'octet Java est presque aussi ancien que le langage lui-même. Malgré une gamme d'outils d'obscurcissement disponibles sur le marché, les programmeurs Java novices continuent de penser à des moyens nouveaux et intelligents de protéger leur propriété intellectuelle. Dans cet épisode de questions-réponses Java, je dissipe certains mythes autour d'une idée fréquemment reformulée dans les forums de discussion.
L'extrême facilité avec laquelle les fichiers Java .class peuvent être reconstruits en sources Java qui ressemblent étroitement aux originaux a beaucoup à voir avec les objectifs et les compromis de conception de code octet Java. Entre autres choses, le code d'octet Java a été conçu pour la compacité, l'indépendance de la plate-forme, la mobilité du réseau et la facilité d'analyse par des interpréteurs de code d'octet et des compilateurs dynamiques JIT (juste à temps) / HotSpot. On peut soutenir que les fichiers .class compilés expriment si clairement l'intention du programmeur qu'ils pourraient être plus faciles à analyser que le code source d'origine.
Plusieurs choses peuvent être faites, sinon pour empêcher complètement la décompilation, du moins pour la rendre plus difficile. Par exemple, comme étape de post-compilation, vous pouvez masser les données .class pour rendre le code d'octet plus difficile à lire lorsqu'il est décompilé ou plus difficile à décompiler en code Java valide (ou les deux). Des techniques telles que la surcharge extrême des noms de méthode fonctionnent bien pour la première, et la manipulation du flux de contrôle pour créer des structures de contrôle qu'il n'est pas possible de représenter via la syntaxe Java fonctionnent bien pour la seconde. Les obfuscateurs commerciaux les plus réussis utilisent un mélange de ces techniques et d'autres.
Malheureusement, les deux approches doivent en fait changer le code que la JVM exécutera, et de nombreux utilisateurs craignent (à juste titre) que cette transformation puisse ajouter de nouveaux bogues à leurs applications. En outre, le changement de nom de méthode et de champ peut entraîner l'arrêt des appels de réflexion. La modification des noms de classe et de package réels peut casser plusieurs autres API Java (JNDI (Java Naming and Directory Interface), fournisseurs d'URL, etc.). Outre les noms modifiés, si l'association entre les décalages de code d'octet de classe et les numéros de ligne source est modifiée, la récupération des traces de pile d'exceptions d'origine peut devenir difficile.
Ensuite, il y a la possibilité de masquer le code source Java d'origine. Mais fondamentalement, cela pose un ensemble similaire de problèmes. Crypter, pas obscurcir?
Peut-être que ce qui précède vous a fait penser: "Eh bien, et si au lieu de manipuler du code d'octet je crypte toutes mes classes après compilation et les décrypte à la volée dans la JVM (ce qui peut être fait avec un chargeur de classe personnalisé)? Ensuite, la JVM exécute mon le code d'octet original et pourtant il n'y a rien à décompiler ou à désosser, non? "
Malheureusement, vous vous trompez, à la fois en pensant que vous avez été le premier à avoir cette idée et en pensant que cela fonctionne réellement. Et la raison n'a rien à voir avec la force de votre schéma de cryptage.