{"id":933,"date":"2023-06-02T11:26:01","date_gmt":"2023-06-02T10:26:01","guid":{"rendered":"https:\/\/www.pixnbgames.com\/blog\/?p=933"},"modified":"2023-06-03T11:41:24","modified_gmt":"2023-06-03T10:41:24","slug":"libgdx-proguard-how-to-obfuscate-the-desktop-jar-file","status":"publish","type":"post","link":"https:\/\/www.pixnbgames.com\/blog\/libgdx\/libgdx-proguard-how-to-obfuscate-the-desktop-jar-file\/","title":{"rendered":"libGDX + ProGuard: How to obfuscate the Desktop JAR file"},"content":{"rendered":"\n<p>I have recently published my libGDX game Rainbow Diamonds for desktop computers. One problem I had was how to obfuscate the desktop JAR file for distribution, and this is how I solved it.<\/p>\n\n\n\n<p>I&#8217;m using <em>openjdk 11.0.13<\/em> distributed as part of Android Studio.<\/p>\n\n\n\n<p>After a few searches on internet, I found this post on <a rel=\"noreferrer noopener\" href=\"https:\/\/esotericsoftware.com\/forum\/d\/14713-libgdx-android-studio-how-to-obfuscate-your-code-desktop\/2\" target=\"_blank\">esotericsoftware.com<\/a>. Please, check it out as that is the base solution for my custom one.<\/p>\n\n\n<h3 style=\"text-align: left;\">ProGuard rules<\/h3>\n\n\n<p>As the solution is based on ProGuard, we need a file to specify how we want to trafsform our code. I use a file named <em><strong>proguard-desktop.txt<\/strong><\/em>, placed in the <em><strong>desktop\/<\/strong><\/em> project folder with the following content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-verbose\n-dontusemixedcaseclassnames\n-dontskipnonpubliclibraryclasses\n-keepattributes Signature,InnerClasses,SourceFile,LineNumberTable\n\n-dontwarn com.badlogic.**\n-dontwarn org.lwjgl.**\n-dontwarn org.objectweb.**\n\n-keep class com.badlogic.**\n-keep class org.lwjgl.**\n-keep class org.objectweb.**\n\n-keepclassmembers class com.badlogic.** { *; }\n-keepclassmembers class org.lwjgl.** { *; }\n-keepclassmembers class org.objectweb.** { *; }\n\n-keepclasseswithmembernames class * {\n  native &lt;methods&gt;;\n}\n\n-keep public class your.game.package.name.desktop.DesktopLauncher {\n    public static void main(java.lang.String&#91;]);\n}\n\n-keepclassmembers,allowoptimization enum * {\n    public static **&#91;] values();\n    public static ** valueOf(java.lang.String);\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Note:<\/strong> <em>The previous file can vary depending on your project needs.<\/em><\/p>\n\n\n<h3>ProGuard gradle task<\/h3>\n\n\n<p>Then, we will a create a ProGuard task to obfuscate the code. Let&#8217;s declare it in <strong><em>\/desktop\/build.gradle<\/em><\/strong> (note that is the gradle file of the Desktop project). After the <em>dist<\/em> task, we insert our new one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>task obfuscate(dependsOn: dist, type: proguard.gradle.ProGuardTask) {\n\n    \/\/ Files\n    def inputFile = dist.outputs.files.singleFile.path\n    def outputJar = dist.destinationDirectory.file(&lt;your_desired_jar_name.jar&gt;)\n    def outputJarFilePath = outputJar.get().asFile.path\n\n    \/\/ Task configuration\n    configuration files(\"proguard-desktop.txt\")\n    libraryjars(&lt;jar_with_base_classes&gt;)\n    injars  files(inputFile)\n    outjars files(outputJarFilePath)\n}<\/code><\/pre>\n\n\n\n<p>The declared task has three main sections:<br><\/p>\n\n\n\n<ul>\n<li><strong>Task declaration<\/strong><br>We indicate the task type and dependency, as this task requires the build from the <em>dist<\/em> task. <\/li>\n\n\n\n<li><strong>Files<\/strong><br>We use the output from the <em>dist<\/em> task as the input for our (<em>obfuscate<\/em>) task. This way we will create a new obfuscated jar (place the desired name in <code>&lt;your_desired_jar_name.jar&gt;<\/code>) in the same folder as the <em>dist<\/em> did.<\/li>\n\n\n\n<li><strong>Task configuration<\/strong><br>We use the options <code>configuration<\/code>, <code>libraryjars<\/code>, <code>injars<\/code> and <code>outjars<\/code>. You can see all the available options on <a rel=\"noreferrer noopener\" href=\"https:\/\/www.guardsquare.com\/manual\/setup\/gradle\" target=\"_blank\">its reference page<\/a>.<\/li>\n<\/ul>\n\n\n<h4>Task configuration<\/h4>\n<ul>\n<li><strong>configuration<\/strong><br>We use the file <em>proguard-desktop.txt<\/em> we created in the first step of this guide.<\/li>\n<li><strong>injars<\/strong><br>The input of the task is the output JAR from the <em>dist<\/em> task.<\/li>\n<li><strong>outjars<\/strong><br>This is the obfuscated JAR the task will create.<\/li>\n<li><strong>libraryjars<\/strong><br>This param can be confused, so I will try to explain it in the next section.<\/li>\n<\/ul>\n<h3>&nbsp;<\/h3>\n<h3>libraryjars<\/h3>\n<p>From the <a href=\"https:\/\/www.guardsquare.com\/manual\/configuration\/usage\" target=\"_blank\" rel=\"noopener\">official doc<\/a>, I extract the following information about this param:<\/p>\n<blockquote>\n<p>&#8230;The specified library jars should at least contain the class files that are&nbsp;<em>extended<\/em> by application class files. &#8230; This means that you explicitly have to specify the run-time jar that your code will use &#8230;<\/p>\n<\/blockquote>\n<p>So we need to provide the classes our project classes are extending from. So, for an standard project, we need to provide at least the base Java classes (java.lang.Object, lava.lang.String, etc.). If you are using Java 8 or older versions, this classes are distributed in JAR files within the JRE. So you can use something like this:<\/p>\n\n\n<pre class=\"wp-block-code\"><code>libraryjars files(\"&lt;path_to_your_jre&gt;\/lib\/rt.jar\", \"&lt;path_to_your_jre&gt;\/lib\/jce.jar\")<\/code><\/pre>\n\n\n\n<p>But if you are using a newer Java version (like me), you no longer have those JARs in your JRE, as from Java 9 these classes are distributed in a Java module file. This file is located at <code>&lt;JRE_LOCATION&gt;\/lib\/modules<\/code>. Well, My first try was trying to directly use that file path as <em>libraryjars<\/em> param, but at least for me it didn&#8217;t work. So I decided to create a JAR with the classes stored in <em>\/lib\/modules<\/em> file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the modules JAR<\/h3>\n\n\n\n<p>First, I recommend to make the following steps with a copy of the modules file. If you damage the original modules file probably you will get an unusable JRE.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Copying the modules file<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir MODULES_JAR\ncp &lt;JRE&gt;\/lib\/modules MODULES_JAR\ncd MODULES_JAR<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Extracting the classes<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>jimage extract --dir=temp modules\ncd temp<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Preparing the JAR classes<\/h4>\n\n\n\n<p>After the classes extraction we have all the classes of the <em>modules<\/em> file in the <em>temp\/<\/em> folder. The problem now is that the folder structure we have is not directly packajable in a JAR file, as all of the classes are classified in a first folder with the module name they bellong.<\/p>\n\n\n\n<p>For example, suppossing we are in the <em>temp\/<\/em> folder, the <code>java.lang.Object<\/code> class is located in <em><strong>java.base\/<\/strong>java\/lang\/Object.class<\/em>, but it should be just in <em>java\/lang\/Object.class<\/em>, as said its package declaration.<\/p>\n\n\n\n<p>So now we need to put all the classes of the different modules in a common folder structure. Basically if we have something like this in the temp\/ folder right now:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>temp\/\n  |- java.base\/\n  |    |- java\/\n  |        |- lang\/\n  |           |- Object.class\n  |           |- String.class\n  |           |- ...\n  |- java.logging\/\n  |    |- java\/\n  |        |- util\/\n  |            |- logging\/\n  |                |- ConsoleHandler.class\n  |                |- ...\n  |- ...<\/code><\/pre>\n\n\n\n<p>we need to remove the first level of folders, having a final file structure like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>temp\/\n  |- java\/\n  |   |- lang\/\n  |      |- Object.class\n  |      |- String.class\n  |      |- ...\n \n  |- java\/\n  |   |- util\/\n  |       |- logging\/\n  |           |- ConsoleHandler.class\n  |           |- ...\n  |- ...<\/code><\/pre>\n\n\n\n<p>If you like, I have created a script to make all this tedious work. <a href=\"https:\/\/www.pixnbgames.com\/files\/ModulesClassesOrganizer.java\" target=\"_blank\" rel=\"noreferrer noopener\">You can find it here<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Packaging the classes<\/h4>\n\n\n\n<p>Inside the <em><strong>temp\/<\/strong><\/em> folder, we can execute the following command to package all the classes in a new JAR file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jar --create --file=..\/modulesclasses.jar . <\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Final ProGuard task<\/h3>\n\n\n\n<p>And now we have the JAR file with the required classes to be used to obfuscate our code.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>task obfuscate(dependsOn: dist, type: proguard.gradle.ProGuardTask) {\n\n    \/\/ Files\n    def inputFile = dist.outputs.files.singleFile.path\n    def outputJar = dist.destinationDirectory.file(&lt;your_desired_jar_name.jar&gt;)\n    def outputJarFilePath = outputJar.get().asFile.path\n\n    \/\/ Task configuration\n    configuration files(\"proguard-desktop.txt\")\n    libraryjars(&lt;your_path&gt;\/MODULES_JAR\/modulesclasses.jar)\n    injars  files(inputFile)\n    outjars files(outputJarFilePath)\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Final comments<\/h2>\n\n\n\n<p>This a very long tutorial about how I solved the original question (<em>How to obfuscate my libGDX JAR for desktop distribution?<\/em>). Probably this is not the only one option, and I don&#8217;t know if it is definetly the right way of solve this. But at least it&#8217;s a working solution and I hope it is a little bit easy for you if you have the same problem and don&#8217;t know exactly how to continue.<\/p>\n\n\n\n<p>Thanks for reading this and good luck for your projects!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have recently published my libGDX game Rainbow Diamonds for desktop computers. One problem I had was how to obfuscate the desktop JAR file for distribution, and this is how I solved it. I&#8217;m using openjdk 11.0.13 distributed as part <a class=\"more-link\" href=\"https:\/\/www.pixnbgames.com\/blog\/libgdx\/libgdx-proguard-how-to-obfuscate-the-desktop-jar-file\/\">Seguir leyendo <span class=\"screen-reader-text\">  libGDX + ProGuard: How to obfuscate the Desktop JAR file<\/span><span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[36,14],"tags":[15,60],"language":[4],"_links":{"self":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/933"}],"collection":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/comments?post=933"}],"version-history":[{"count":12,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/933\/revisions"}],"predecessor-version":[{"id":948,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/posts\/933\/revisions\/948"}],"wp:attachment":[{"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/media?parent=933"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/categories?post=933"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/tags?post=933"},{"taxonomy":"language","embeddable":true,"href":"https:\/\/www.pixnbgames.com\/blog\/wp-json\/wp\/v2\/language?post=933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}