Search code examples
javagradleobfuscationyguard

Obfuscating Java in Gradle using yGuard ~ How to exclude methods?


I want to obfuscate Java code in Gradle using yGuard. So far, I have managed to get the obfuscation working using:

dependencies {
    compile 'com.yworks:yguard:2.9.2'
}

task yGuardObfuscate {
    group 'yGuard'
    description 'Obfuscates existing archives.'
    dependsOn "installDist"

    doLast {
        ant.taskdef(
            name: 'yguard',
            classname: 'com.yworks.yguard.YGuardTask',
                classpath: sourceSets.main.runtimeClasspath.asPath
            )

        ant.yguard {
            inoutpairs {
                fileset(dir: "./build/install/module") {
                    include(name: "module*.jar")
                    exclude(name: "*_obf.jar")
                }
            }

            rename(logFile: "./build/install/module/rename.log") {
                property(name: "naming-scheme", value: "mix")
            }
        }
    }
}

However, that has the problem that the main function gets obscured, and I can't run it anymore using java -jar module_obf.jar (which works with the unobfuscated jar).

I have tried several ways of excluding the main function from obfuscation.

First, I tried using the @Obfuscate annotation as described in the documentation (https://yworks.github.io/yGuard/task_documentation/#controlling-obfuscation-exclusion-with-annotations), like this:

  @com.yworks.util.annotation.Obfuscation( exclude = true, applyToMembers = false)
  public static void main(String[] args) {
    try {
      new Start();
    } catch (SQLException e) {
      LOGGER.error(e, e);
    }
  }

In my IDE (Eclipse 2019-14), this line was shown as valid. However, when trying to compile it with the gradle task I created, I got the following error:

...\Start.java:22: error: package com.yworks.util.annotation does not exist
  @com.yworks.util.annotation.Obfuscation( exclude = true, applyToMembers = false)
                             ^

I tried a number of things to get this to work, but could not arrive at a working solution.

So next, I tried to use a keep > method element in the yGuardObfuscate task to explicitly keep the main function of the Start class, like this:

task yGuardObfuscate {
    group 'yGuard'
    description 'Obfuscates existing archives.'
    dependsOn "installDist"

    doLast {
        ant.taskdef(
            name: 'yguard',
            classname: 'com.yworks.yguard.YGuardTask',
                classpath: sourceSets.main.runtimeClasspath.asPath
            )

        ant.yguard {
            inoutpairs {
                fileset(dir: "./build/install/module") {
                    include(name: "module*.jar")
                    exclude(name: "*_obf.jar")
                }
            }

            rename(logFile: "./build/install/module/rename.log") {
                property(name: "naming-scheme", value: "mix")
                keep {
                    method("class": "com.company.project.module.Start", name: "public static void main(String[] args)")
                }
            }
        }
    }
}

This caused the Gradle build to fail with the following exception:

: java.lang.IllegalArgumentException: '(' expected but found void
[...]
Caused by: java.lang.IllegalArgumentException: '(' expected but found void
    at com.yworks.yguard.ObfuscatorTask.toNativeMethod(ObfuscatorTask.java:188)
    at com.yworks.yguard.ant.MethodSection.addEntries(MethodSection.java:35)
    at com.yworks.yguard.ant.ExposeSection.createEntries(ExposeSection.java:170)
    at com.yworks.yguard.ObfuscatorTask.execute(ObfuscatorTask.java:745)
    at com.yworks.yguard.YGuardTask.execute(YGuardTask.java:116)
[...]
Root cause: java.lang.IllegalArgumentException: '(' expected but found void
    at com.yworks.yguard.ObfuscatorTask.toNativeMethod(ObfuscatorTask.java:188)
    at com.yworks.yguard.ant.MethodSection.addEntries(MethodSection.java:35)
    at com.yworks.yguard.ant.ExposeSection.createEntries(ExposeSection.java:170)
    at com.yworks.yguard.ObfuscatorTask.execute(ObfuscatorTask.java:745)
    at com.yworks.yguard.YGuardTask.execute(YGuardTask.java:116)

Again, I tried several things here, such as writing "class" without the "", adding extra {}, but nothing helped.

So the question here is: What am I doing wrong? And how can I prevent yGuard from obfuscating the main function?


Solution

  • I now figured out a way to make it work, even though it sorta feels more like a workaround than a proper solution, so if anyone knows what I did wrong with the "official" solutions, please do tell me.

    For this, I used the Annotation approach, and instead of using the default annotation, I created a custom annotation that is pretty much an exact copy in my project.

    The annotation class looks like this:

    package com.company.project.module.annotations;
    
    public @interface Obfuscation {
          boolean exclude() default true;
    
          boolean applyToMembers() default true;
    }
    

    I use it in my Start class like this:

    import com.company.project.module.annotations.*;
    
    [...]
    
      @Obfuscation( exclude = true, applyToMembers = false)
      public static void main(String[] args) {
        [...]
      }
    

    And finally, I added this custom annotation class to the rename element of my task like this:

                rename(logFile: "./build/install/module/rename.log", annotationClass: "com.company.project.module.annotations.Obfuscation") {
                    property(name: "naming-scheme", value: "mix")
                }
    

    So the entire gradle task now looks like this:

    task yGuardObfuscate {
        group 'yGuard'
        description 'Obfuscates existing archives.'
        dependsOn "installDist"
    
        doLast {
            ant.taskdef(
                name: 'yguard',
                classname: 'com.yworks.yguard.YGuardTask',
                    classpath: sourceSets.main.runtimeClasspath.asPath
                )
    
            ant.yguard {
                inoutpairs {
                    fileset(dir: "./build/install/module") {
                        include(name: "module*.jar")
                        exclude(name: "*_obf.jar")
                    }
                }
    
                rename(logFile: "./build/install/module/rename.log", annotationClass: "com.company.project.module.annotations.Obfuscation") {
                    property(name: "naming-scheme", value: "mix")
                }
            }
        }
    }
    

    With that, the build now works, and I can successfully run my program using java -jar module_obf.jar.