For high-level languages such as .NET or Java, it is quite trivial to reverse-engineer application binaries to recover the original source code if the application doesn’t employ any type of encryption or obfuscation. Some popular decompilers are: Dotpeek, Reflector, JustDecompile (.NET), Java Decompiler, Mocha, DJ Java Decompiler (Java). In a penetration test, program’s source code is valuable information that can be used to attack application and trusted resources. To help prevent the recovery of source code, obfuscation is often being used. Obfuscation is the intentional act of converting original source code to a form that is difficult for human to understand. The main goal of obfuscation is to provide “security through obscurity” in order to prevent attackers from:
Recovering original source code (Intellectual Property (IP) theft)
Tampering with business logic by modifying source code
Obfuscators (programs that help obfuscating source code), generally achieve its purposes by:
Name obfuscation: renaming classes, methods, packages, fields, etc.
Code and Data Flow obfuscation: alternating program logic or adding gibberish instructions that may not be decompiled into valid source code
Inject Unnecessary Code
Remove debug information
This blog post aims to provide a simple example of Java Obfuscation. Hopefully it will be helpful for programmers wanting to protect their applications.
Choosing Java Obfuscator
If you just want to simply do name/data flow obfuscation for your Java program, free obfuscators such as ProGuard or yGuard will suit your needs. For stronger protection and extra capabilities such as String Encryption, Incremental Obfuscation, Stack Trace Transplantation, you may need to choose a commercial product. This article provides a great comparison among popular Java Obfuscators I am usually a proponent of open source programs; however for this tutorial I will be using Zelix KlassMaster to obfuscate a custom server/client Java program. In my opinion, it provides decent feature set for a reasonable price point. For complete feature listing visit Zelix Klassmaster
Let’s take a look at this client/server program. On the Server side, we have following classes:
ServerRun.java: Main Program
Person.java, SearchObject.java,SearchResult.java: shared classes between client/server
Compiling server classes give us Java Archive file: Server.jar On the Client side, we have following classes:
ClientRun.java: Main Program
Person.java, SearchObject.java,SearchResult.java: shared classes between client/server
As mentioned above, it is trivial to recover original source code with a Decompiler Program such as Java Decompiler (JD-GUI). Here is what it looks like when decompiling Client.jar with JD-GUI:
The original source code has been recovered with great accuracy. With this information, attackers can learn the business logic of the program or import decompiled project into an IDE, modify code flow to bypass program restrictions.
Bad Obfuscation Example
The most common mistake when dealing with obfuscation is to involve it later in the game, after the implementation phase, when the binaries are made and ready for testing. In this bad example below, Client.jar is obfuscated with Zelix KlassMaster with the following settings:
After obfuscation the result binary will have the following structure:
Decompiling it will result in an empty class file as shown below:
The command to run Client program will be the same as before: java -classpath Client.jar com.client.ClientRun However it will generate exception when trying to communicate with Server Program. The reason is: Server and Client programs share some common classes: Person, SearchObject and SearchResult. When Server program attempts to read com.commons.Person object, for example, in Client program it is transformed into com.client.a, causing the Exception in the screenshot below. For both of the programs to work, Client and Server programs must be using the exact same classes, including package name.
General Obfuscation Procedures
Below is any overview of common obfuscation procedures.
Identify common public interfaces and methods that will be used either within the same program or by another components
Identify serialized classes that will be shared among different components
Refactor classes in step 1 and step 2 into separate project
Compile them into Java Archive (.jar) file
Obfuscate common libraries (optional)
Import necessary library into relevant components
Identify main entry points
Obfuscate the rest of the code
Identify common public interfaces and methods that will be used either within the same program or by another componentsRemote Procedure Call (RPC) Applications typically communicate with each other through some common interfaces. They can use those interfaces to invoke functions on another machine or network. Those interfaces must be made available to all involved components. Some examples:
JBoss Remoting Interfaces
Apache XML RPC Interfaces
This principle can also be applied for programs that provide Application Programmable Interface (API) for other software components.The example doesn’t have any of the above interfaces so this step can be ignored.
Identify serialized classes that will be shared among different componentsSerialization allows programmers to save and restore state of objects. In case of transmitting objects between different components, serializable classes must be defined the same in all components. In Java, Serializable classes are easily recognized because they must implement Serializable interface. Example:public classPersonimplementsSerializable. In this example, serializable classes are: Person, SearchObject and SearchResult
Refactor aforementioned classes into separate projectsIn this step, the excluded classes/interfaces mentioned in step 1 and step 2 are refactored or moved into separate projects. It is also a good design practice to refactor common classes into new components, since it will eliminate redundancy, keep the code base clean and easy to maintain.In Eclipse, I created a new project called ServletCommons and import serializable classes in step 2:
Compile them into Java Archive (.jar) librariesThe purpose of this step is to generate jar files that can later be imported into components that will reuse/extend them. The following command will generate servlet-commons.jar file for project ServletCommons. C:ServletCommonsbin> jar cvf servlet-commons.jar *.*
Obfuscate common libraries (optional)If you follow good software design practice, step 1-4 may have already been done. It is now up to you to decide if these libraries should be obfuscated. If your software is well-designed, RPC Interfaces/ API should not publicly expose sensitive methods, and consequently need no obfuscation. The same principle applies for Serializable classes: they should not contain any application logic, other than object’s data. Obfuscate common libraries will provide greater protection, but with a cost. Your vanilla code base will now contain original source code and obfuscated libraries. It will probably add more overhead and introduce more efforts into your development and testing environment.
Import libraries into relevant components, when necessaryNow you will need to import those libraries to projects that need them. This can be done with an IDE, ant or maven build script. You may also need to deploy them on the server, depending on your setup. The example in the screenshot below show how to do it in Eclipse:
If you choose to obfuscate common libraries in step 5, this is the time to fix potential compilation errors due to class/package/method renaming.
Identify main entry points, retain mapping of entry pointsFor complex applications that contain native code (C, C++) call out to Java component, the main entry points to Java components need to be clearly identified and kept consistent. For this reason you should keep track of the mapping between entry points and obfuscate code, and update components that are calling now-obfuscated entry points. For more information check out this documentation: https://www.zelix.com/klassmaster/docs/tutorials/incrementalObfuscation.html
Import necessary libraries and obfuscateFirst you will need to set the classpath of the project. As mentioned in step 4, servlet-common.jar is required for both Client and Server program
The following settings are used to obfuscate Client Program. Note that I excluded public static void main(String args) as it is required to run any Java application:
Specify change log location:
Save obfuscated classes into a new jar file:
Decompiling it with JD-GUI:
Review change log for the new names of entry points. Update all components that are using those entry points. Sample change log will look like: Class: public com.client.ClientRun => d Source: “ClientRun.java” Now move on to obfuscate Server program. To keep it simple, I opt to exclude servlet class name from obfuscation:
Save obfuscated classes into new jar file:
Decompiling it with JD-GUI:
TestDeploy Server.jar and servlet-commons.jar to Tomcat. Run the client with the newly obfuscated entry point: java -classpath Client.jar;servlet-commons.jar d
Obfuscation, in general, makes reverse-engineering a time-consuming process for most attackers. Obfuscation cannot prevent other attack vectors such as bytecode manipulation or communication manipulations. To protect your application from such threats, consider implementing code signing in your application. Regarding Zelix Klassmaster itself, Security Researcher Yiannis Pavlosoglou has discovered that the its String Encryption routine is actually composing of five XOR operations, with encryption keys hard-coded in the class file. More information can be viewed here: https://www.defcon.org/images/defcon-15/dc15-presentations/dc-15-subere.pdf
PTaaS is NetSPI’s delivery model for penetration testing. It enables customers to simplify the scoping of new engagements, view their testing results in real time, orchestrate faster remediation, perform always-on continuous testing, and more - all through the Resolve™ vulnerability management and orchestration platform.
We help organizations defend against adversaries by being the best at simulating real-world, sophisticated adversaries with the products, services, and training we provide. We know how attackers think and operate, allowing us to help our customers better defend against the threats they face daily.
At NetSPI, we believe that there is simply no replacement for human-led manual deep dive testing. Our Resolve platform delivers automation to ensure our people spend time looking for the critical vulnerabilities that tools miss. We provide automated and manual testing of all aspects of an organization’s entire attack surface, including external and internal network, application, cloud, and physical security.
Our proven methodology ensures that the client experience and our findings aren’t only as good as the latest tester assigned to your project. That consistency gives our customers assurance that if vulnerabilities exist, we will find them.
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
YouTube session cookie.
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Analytics cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.
Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.
Cookies are small text files that can be used by websites to make a user's experience more efficient. The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission. This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.