Can I obfuscate a module?

Apache NetBeans Wiki Index

Note: These pages are being reviewed.

If you want to protect a NetBeans module from disassembly, you can obfuscate it. For example you can use ProGuard, an open-source obfuscator.

  1. Copy proguard.jar somewhere on disk, referenced by proguard.jar.path. You can do this for example by creating a build.properties in the module directory and reference it in the build.xml like

    <!-- Load project's default properties -->
    <property file="${basedir}/build.properties"/>

    <import file="nbproject/build-impl.xml"/>

<!-- ### BEGIN OBFUSCATION TARGETS ###-->

This example is assuming that you have created a directory called tools parallel to your module. Under that tools directory have you copied the proguard jar file into <pre>tools/obfuscating/jar/proguard.jar</pre>

The build.properties could look like the following example.

tools.dir=../tools
proguard.jar.path=${tools.dir}/obfuscating/jar/proguard.jar
  1. Edit the build.xml of your module and override some targets as in the following excerpt:

<!-- Replace the non-obfuscated jar with the obfuscated one when compiling -->
<target name="netbeans-extra" depends="obfuscate">
    <copy file="${suite.dir}/build/obfuscated/${module.jar}"
          tofile="${cluster}/${module.jar}"/>
</target>

<!-- Overridden debug target that depends on netbeans-debug -->
<target name="debug" depends="netbeans-debug,-jdk-presetdef-nbjpdastart">
    <ant antfile="${harness.dir}/run.xml" target="debug"/>
</target>

<!-- netbeans-debug DOES NOT depends on netbeans-extra
     (then DOES NOT depends on obfuscate) -->
<target name="netbeans-debug"
        depends="init,jar,module-xml-regular,module-xml-autoload,
      module-xml-eager,javahelp,module-auto-deps,release,verify-class-linkage">
    <genlist outputfiledir="${cluster}" module="${module.jar}">
        <fileset dir="${cluster}">
            <patternset refid="module.files"/>
        </fileset>
    </genlist>
</target>

<!-- Overridden to delete also the obfuscated jar -->
<target name="clean" depends="files-init,testuserdir-delete">
    <delete failonerror="false" includeemptydirs="true">
        <fileset dir="build">
            <exclude name="testuserdir/"/>
        </fileset>
    </delete>
    <delete dir="${netbeans.javadoc.dir}/${code.name.base.dashes}"/>
    <delete file="${netbeans.javadoc.dir}/${code.name.base.dashes}.zip"/>
    <delete failonerror="false"> <!-- #59457: OK if cluster does not exist currently -->
        <fileset dir="${cluster}">
            <patternset refid="module.files"/>
        </fileset>
    </delete>
    <delete file="${cluster}/update_tracking/${code.name.base.dashes}.xml"/>
    <delete file="${suite.dir}/build/obfuscated/${module.jar}"/>
</target>

<!--  Just a cut and paste of how the proguard obfuscator works.
      This is not supposed to work below.  In fact, this seems to work
      on jars, not .class files, so it will have to be placed in a
      post jar target, which I haven't identified yet -->
<target name="obfuscate" depends="init">
    <taskdef resource="proguard/ant/task.properties"
             classpath="${proguard.jar.path}" />

    <echo message="Obfuscating ${cluster}/${module.jar}..."/>
    <mkdir dir="${suite.dir}/build/obfuscated"/>
    <proguard printmapping="${suite.dir}/build/obfuscated/${code.name.base.dashes}.map"
              renamesourcefileattribute="SourceFile" ignorewarnings="true">

        <!-- Specify the input jars, output jars, and library jars. -->
        <injar  file="${cluster}/${module.jar}" />
        <outjar file="${suite.dir}/build/obfuscated/${module.jar}" />

        <libraryjar path="${module.run.classpath}" />
        <libraryjar file="${nbjdk.home}/jre/lib/rt.jar" />

        <!-- Keep some useful attributes. -->

        <keepattribute name="InnerClasses" />
        <keepattribute name="SourceFile" />
        <keepattribute name="LineNumberTable" />
        <keepattribute name="Deprecated" />
        <keepattribute name="*Annotation*" />
        <keepattribute name="Signature" />

        <!-- Preserve all public classes,
             and their public and protected fields and methods. -->

        <keep access="public">
            <field  access="public protected" />
            <method access="public protected" />
        </keep>


        <!-- Preserve all .class method names. -->

        <keepclassmembernames access="public">
            <method type      ="java.lang.Class"
                    name      ="class$"
                    parameters="java.lang.String" />
            <method type      ="java.lang.Class"
                    name      ="class$"
                    parameters="java.lang.String,boolean" />
        </keepclassmembernames>

        <!-- Preserve all native method names and the names of their classes. -->

        <keepclasseswithmembernames>
            <method access="native" />
        </keepclasseswithmembernames>

        <!-- Preserve the methods that are required in all enumeration classes. -->

        <keepclassmembers extends="java.lang.Enum">
            <method access="public static"
                    type="**[]"
                    name="values"
                    parameters="" />
            <method access="public static"
                    type="**"
                    name="valueOf"
                    parameters="java.lang.String" />
        </keepclassmembers>

        <!-- Explicitly preserve all serialization members. The Serializable
             interface is only a marker interface, so it wouldn't save them.
             You can comment this out if your library doesn't use serialization.
             With this code serializable classes will be backward compatible -->

        <keepnames implements="java.io.Serializable"/>
        <keepclassmembers implements="java.io.Serializable">
            <field  access    ="final"
                    type      ="long"
                    name      ="serialVersionUID" />
            <field  access    ="!static !transient"
                    name      ="**"/>
            <field  access    ="!private"
                    name      ="**"/>
            <method access    ="!private"
                    name      ="**"/>
            <method access    ="private"
                    type      ="void"
                    name      ="writeObject"
                    parameters="java.io.ObjectOutputStream" />
            <method access    ="private"
                    type      ="void"
                    name      ="readObject"
                    parameters="java.io.ObjectOutputStream" />
            <method type      ="java.lang.Object"
                    name      ="writeReplace"
                    parameters="" />
            <method type      ="java.lang.Object"
                    name      ="readResolve"
                    parameters="" />
        </keepclassmembers>

        <!-- Your application may contain more items that need to be preserved;
             typically classes that are dynamically created using Class.forName -->

    </proguard>
</target>

In this way when running and when creating the NBM (as well from a suite) the module will be obfuscated. When debugging your module you use the non-obfuscated JAR, so you can step through source as well.

This example will obfuscate all your private classes and methods only. This approach should protect you against problems which a full obfuscation could create as layer.xml references to classes will not be found and other lookup/services mechanism would fail. Design your code accordingly.

NOTE2: As a practical suggestion you could also decide not to overwrite the debug target and simply comment out the netbeans-extra target as long as you develop. Only activate the netbeans-extra target when you do a release build or create new NBM’s for an update.

This is verified to work on NB 6.5 running Solaris or Windows using Proguard 4.1. (proguard.jar 548 Kb) Issues were found with 4.2. Others versions (current Feb 2010) the 4.5 beta is out has not been tested yet. For Mac OS X you will need to softlink the classes.jar to rt.jar as described in This blog entry