Protect Java sources from Reverse Engineering

Overview

If we are developing java application whose byte code is publicly distributed over internet, there is always possibility to reverse engineer the class files using various decompilation tools.
To make reverse engineering much difficult, Java obfuscators can be used.
Java obfuscators will ensure to replace the identifiers to names which are unclear and also make the flow very difficult to understand after decompilation.
There are multiple free and commercial Java obfuscators whose list can be found here.
The current post deals with obfuscation process using Proguard.

Pre-requisites

  • Jdk installed, can be downloaded here
  • Java decompiler, the current post uses JAD, can be downloaded here. JAD is currently outdated and doesn’t support latest jdk enhancements
  • Java obfuscator, the current post uses Proguard, can be downloaded here.

Sample source file

The below is sample java source files (NoObsfuscation.java and ObfuscationImpl.jav) which will be explained in current post, other sample files can be found here.

	public class NoObsfuscation {

	public static void main(String[] args) {
		ObfuscationInterface obfuscationInterface = new ObfuscationImpl();
		obfuscationInterface.printInput("obfuscation interface instance");
		obfuscationInterface.printDefaultMthd();
		
		ObfuscationImpl obfuscationImpl = new ObfuscationImpl();
		obfuscationImpl.genericAbstractMethod();
		obfuscationImpl.genericImpl();
	}
}
public class ObfuscationImpl extends AbstractObfuscation implements
		ObfuscationInterface {

		public void printInput(String input) {
		System.out.println("printInput implementation:--->"+this.hashCode());
	}

	@Override
	public void genericAbstractMethod() {
		System.out.println("genericAbstractMethod:-->"+this.hashCode());
	}

Obfuscation Steps with Proguard

  • 1. Create Proguard Conf flile

  • The proguard conf file provides configuaruon details to be considered while obfuscation. The below mentioned are few config details and complete list can be found here.

          # The input jar which need to be obfuscated.
            -injars  core-java.jar
    
          # The obfuscated jar. 
            -outjars core_java_obfuscated.jar
    
          # The java source library jar which is the run time jar for code execution.
            -libraryjars "C:/Program Files/Java/jdk1.8.0_25/jre/lib/rt.jar"
    
          # Proguard provides map file which provides mapping between original java source identifiers and obsfuscted names for trouble shooting  and debugging purpose.
            -printmapping proguard.map
    
          # Proguard removes the unused and unreferenced sources to reduce the size of obfusctaed file. dontshrink options instructs proguard to retain all the files without removing them.
            -dontshrink
    
         # This file is not obfuscated and considered as root of the current references
           -keep public class com.siva.mythoughts.obfustcation.NoObsfuscation { *; }
    
         # This ensures all the files with below mentioned package are not obfusctaed and remain in tact
           -keep class com.siva.mythoughts.executorservice.* { *; }
    
         

  • 2. Execute Proguard

  • The proguard can be executed via command line Or it can also be executed during build process using Maven.
    The successful execution of proguard results in out jar as well as map file

            Execute via Command line
            C:\obfuscation\proguard5.2.1\proguard5.2.1\bin>proguard.bat @../examples/myconfig.pro
         
    	<profiles>
     	  <profile>
    	    <id>obfuscatedemo</id>
      	    <build>
      	     <plugins>
      		<plugin>
      		   <groupId>com.github.wvengen</groupId>
      		   <artifactId>proguard-maven-plugin</artifactId>
      		   <version>2.0.11</version>
      		   <executions>
      		     <execution>
      			<phase>package</phase>
      			<goals><goal>proguard</goal></goals>
      		     </execution>
      		    </executions>
      		   <configuration>
      			<outjar>core_java_obfuscated.jar</outjar>
      			<options>
      		 	   <option>-dontshrink</option>
      			   <option>-dontwarn</option>
      			   <option>-keep public class com.siva.mythoughts.obfustcation.NoObsfuscation { *; }</option>
      			   <option>-keep class com.siva.mythoughts.executorservice.* { *; }</option>
      			</options>
      		    </configuration>
      		</plugin>
      	     </plugins>
      	   </build>  		
    	</profile>
         </profiles>
         

  • 3. Decompiled sources

  • Find below the comparison between decompiled sources after and before obfuscation.

           import java.io.PrintStream;
           // Referenced classes of package com.siva.mythoughts.obfustcation:
           //            b, c
           public class NoObsfuscation
           {
    public NoObsfuscation()
        {
        }
    
        public static void main(String args[])
        {
            (args = new b()).c();
            args.a_();
            args = args = new b();
            System.out.println((new StringBuilder("genericAbstractMethod:-->")).append(args.hashCode()).toString());
            System.out.println("Generic Abstract Implementation");
        }
    }
    
    package com.siva.mythoughts.obfustcation;
    
    // Referenced classes of package com.siva.mythoughts.obfustcation:
    //            ObfuscationImpl, ObfuscationInterface
    
    public class NoObsfuscation
    {
    
        public NoObsfuscation()
        {
        }
    
        public static void main(String args[])
        {
            ObfuscationInterface obfuscationInterface = new ObfuscationImpl();
            obfuscationInterface.printInput("obfuscation interface instance");
            obfuscationInterface.printDefaultMthd();
            ObfuscationImpl obfuscationImpl = new ObfuscationImpl();
            obfuscationImpl.genericAbstractMethod();
            obfuscationImpl.genericImpl();
        }
    }
    
  • 4. Proguard Map file

  • The below is the proguard map file which can be used for debugging and troubleshooting purpose.

        com.siva.mythoughts.obfustcation.AbstractObfuscation -> com.siva.mythoughts.obfustcation.a:
        void <init>() -> <init>
        void genericImpl() -> a
        void genericAbstractMethod() -> b
    
        com.siva.mythoughts.obfustcation.NoObsfuscation -> com.siva.mythoughts.obfustcation.NoObsfuscation:
        void <init>() -> <init>
        void main(java.lang.String[]) -> main
    
        com.siva.mythoughts.obfustcation.ObfuscationImpl -> com.siva.mythoughts.obfustcation.b:
        void <init>() -> <init>
        void printInput$552c4e01() -> c
        void genericAbstractMethod() -> b
    
        com.siva.mythoughts.obfustcation.ObfuscationInterface -> com.siva.mythoughts.obfustcation.c:
        void printInput$552c4e01() -> c
        void printDefaultMthd() -> a_
    		

Limitations of Obfusction

  • Obfuscation process doesnt change the name of java API classes, they are still visible in decompiled obfuscated code
  • Dynamic generated classes obfuscation may create instability in the program execution
  • Obfuscation of life cycle call back methods may create instability during call backs
  • Serialized classes are not obfuscated
  • Further enhanced releases of the software should ensure to maintain compatibility with previous released obfuscated code
  • Some of the obfuscators may change the code work flow which may prevent jvm optimizations
  • Some anit virus softwares may alert obfuscated code as malicious