102

How do I lock compiled Java classes to prevent decompilation?

I know this must be very well discussed topic on the Internet, but I could not come to any conclusion after referring them.

Many people do suggest obfuscator, but they just do renaming of classes, methods, and fields with tough-to-remember character sequences but what about sensitive constant values?

For example, you have developed the encryption and decryption component based on a password based encryption technique. Now in this case, any average Java person can use JAD to decompile the class file and easily retrieve the password value (defined as constant) as well as salt and in turn can decrypt the data by writing small independent program!

Or should such sensitive components be built in native code (for example, VC++) and call them via JNI?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
jatanp
  • 3,736
  • 4
  • 35
  • 42
  • Even the disassembled native code is quite readable to some people anyway, you would have to probably use some obfuscator or hand made assembly to make it non-obvious (common C++ compiled with optimization is readable enough). Whatever code runs on user's device, can be intercepted. Although the cost/skill requirements of such interception may be quite high, for example breaking into smart-card "tamper-proof" chips code is not trivial, only doable with top equipment and skill. With Java classes... I wouldn't bother much, you can probably encrypt them enough to turn script kids away, not more. – Ped7g Sep 22 '16 at 10:43

9 Answers9

98

Some of the more advanced Java bytecode obfuscators do much more than just class name mangling. Zelix KlassMaster, for example, can also scramble your code flow in a way that makes it really hard to follow and works as an excellent code optimizer...

Also many of the obfuscators are also able to scramble your string constants and remove unused code.

Another possible solution (not necessarily excluding the obfuscation) is to use encrypted JAR files and a custom classloader that does the decryption (preferably using native runtime library).

Third (and possibly offering the strongest protection) is to use native ahead of time compilers like GCC or Excelsior JET, for example, that compile your Java code directly to a platform specific native binary.

In any case You've got to remember that as the saying goes in Estonian "Locks are for animals". Meaning that every bit of code is available (loaded into memory) during the runtime and given enough skill, determination and motivation, people can and will decompile, unscramble and hack your code... Your job is simply to make the process as uncomfortable as you can and still keep the thing working...

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Roland Tepp
  • 7,673
  • 10
  • 53
  • 68
16

As long as they have access to both the encrypted data and the software that decrypts it, there is basically no way you can make this completely secure. Ways this has been solved before is to use some form of external black box to handle encryption/decryption, like dongles, remote authentication servers, etc. But even then, given that the user has full access to their own system, this only makes things difficult, not impossible -unless you can tie your product directly to the functionality stored in the "black box", as, say, online gaming servers.

Erlend Halvorsen
  • 937
  • 9
  • 14
13

Disclaimer: I am not a security expert.

This sounds like a bad idea: You are letting someone encrypt stuff with a 'hidden' key that you give him. I don't think this can be made secure.

Maybe asymmetrical keys could work:

  • deploy an encrypted license with a public key to decrypt
  • let the customer create a new license and send it to you for encryption
  • send a new license back to the client.

I'm not sure, but I believe the client can actually encrypt the license key with the public key you gave him. You can then decrypt it with your private key and re-encrypt as well.

You could keep a separate public/private key pair per customer to make sure you actually are getting stuff from the right customer - now you are responsible for the keys...

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Daren Thomas
  • 61,662
  • 38
  • 143
  • 192
  • 2
    I've used this technique before and it works fine. However, from an attack perspective, the first approach would be to just modify the code that performs the license check and remove it. There are things you can do to make this attack vector harder, but if you give the attacker control of the hardware, your destined to fail with a suitably motivated and skilled attacker. In practice, the goal is just to keep the mostly honest people, honest. – Jim Rush Jun 24 '10 at 12:04
12

No matter what you do, it can be 'decompiled'. Heck, you can just disassemble it. Or look at a memory dump to find your constants. You see, the computer needs to know them, so your code will need to too.

What to do about this?

Try not to ship the key as a hardcoded constant in your code: Keep it as a per-user setting. Make the user responsible for looking after that key.

Daren Thomas
  • 61,662
  • 38
  • 143
  • 192
7

@jatanp: or better yet, they can decompile, remove the licensing code, and recompile. With Java, I don't really think there is a proper, hack-proof solution to this problem. Not even an evil little dongle could prevent this with Java.

My own biz managers worry about this, and I think too much. But then again, we sell our application into large corporates who tend to abide by licensing conditions--generally a safe environment thanks to the bean counters and lawyers. The act of decompiling itself can be illegal if your license is written correctly.

So, I have to ask, do you really need hardened protection like you are seeking for your application? What does your customer base look like? (Corporates? Or the teenage gamer masses, where this would be more of an issue?)

marcospereira
  • 11,462
  • 3
  • 42
  • 49
Stu Thompson
  • 36,763
  • 19
  • 104
  • 155
  • In fact, there is a proper, hack-proof solution that btw looks like a dongle: http://www.excelsior-usa.com/blog/excelsior-jet/java-bytecode-encryption-revisited/ – Dmitry Leskov May 23 '11 at 10:27
  • @DmitryLeskov 'hack resistant', maybe. But it is merely a speed bump to anyone who wants at the code. At the end of the day, the byte code has to run on a host platform unencrypted. Full stop. – Stu Thompson Aug 08 '13 at 15:14
  • You have not read the post I linked to. The bytecode is *converted* to the dongle's CPU code and encrypted. When the end user runs the protected app, that encrypted code is transferred to the "dongle". The dongle decrypts and runs it on its CPU. – Dmitry Leskov Aug 10 '13 at 07:47
  • 3
    Corporate teenage gamers. – odiszapc Mar 14 '15 at 16:29
3

If you're looking for a licensing solution, you can check out the TrueLicense API. It's based on the use of asymmetrical keys. However, it doesn't mean your application cannot be cracked. Every application can be cracked with enough effort. What really important is, as Stu answered, figuring out how strong protection you need.

Community
  • 1
  • 1
hakan
  • 1,721
  • 14
  • 14
2

I don't think there exists any effective offline antipiracy method. The videogame industry has tried to find that many times and their programs has always been cracked. The only solution is that the program must be run online connected with your servers, so that you can verify the lincense key, and that there is only one active connecion by the licensee at a time. This is how World of Warcraft or Diablo works. Even tough there are private servers developed for them to bypass the security.

Having said that, I don't believe that mid/large corporations use illegal copied software, because the cost of the license for them is minimal (perhaps, I don't know how much you are goig to charge for your program) compared to the cost of a trial version.

Community
  • 1
  • 1
Telcontar
  • 4,633
  • 6
  • 27
  • 38
2

You can use byte-code encryption with no fear.

The fact is that the cited above paper “Cracking Java byte-code encryption” contains a logic fallacy. The main claim of the paper is before running all classes must be decrypted and passed to the ClassLoader.defineClass(...) method. But this is not true.

The assumption missed here is provided that they are running in authentic, or standard, java run-time environment. Nothing can oblige the protected java app not only to launch these classes but even decrypt and pass them to ClassLoader. In other words, if you are in standard JRE you can't intercept defineClass(...) method because the standard java has no API for this purpose, and if you use modified JRE with patched ClassLoader or any other “hacker trick” you can't do it because protected java app will not work at all, and therefore you will have nothing to intercept. And absolutely doesn't matter which “patch finder” is used or which trick is used by hackers. These technical details are a quite different story.

ThiefMaster
  • 285,213
  • 77
  • 557
  • 610
  • How exactly do you intend to detect a patched JVM? Anyway, all this does is make things slightly harder. – Antimony Apr 01 '13 at 02:14
  • Can't you just find a call to defineClass() in your app launcher ? When you make that call, you have to hand in an array of decrypted bytes anyway. Isn't that another point where the original source could leak ? – Ascendant Jul 10 '13 at 01:14
  • 4
    I don't really agree with this answer. To me this sounds like, "Question: What's the easiest way to find Pi? Answer: Take 2*Pi and divide by two." I am not disagreeing with the idea, but could you include more details? For instance, do you expect the main program to be written in pure java? Does that include the code that's looking for modifications? – Patrick M Mar 29 '14 at 06:29
-1

Q: If I encrypt my .class files and use a custom classloader to load and decrypt them on the fly, will this prevent decompilation?

A: The problem of preventing Java byte-code decompilation is almost as old the language itself. Despite a range of obfuscation tools available on the market, novice Java programmers continue to think of new and clever ways to protect their intellectual property. In this Java Q&A installment, I dispel some myths around an idea frequently rehashed in discussion forums.

The extreme ease with which Java .class files can be reconstructed into Java sources that closely resemble the originals has a lot to do with Java byte-code design goals and trade-offs. Among other things, Java byte code was designed for compactness, platform independence, network mobility, and ease of analysis by byte-code interpreters and JIT (just-in-time)/HotSpot dynamic compilers. Arguably, the compiled .class files express the programmer's intent so clearly they could be easier to analyze than the original source code.

Several things can be done, if not to prevent decompilation completely, at least to make it more difficult. For example, as a post-compilation step you could massage the .class data to make the byte code either harder to read when decompiled or harder to decompile into valid Java code (or both). Techniques like performing extreme method name overloading work well for the former, and manipulating control flow to create control structures not possible to represent through Java syntax work well for the latter. The more successful commercial obfuscators use a mix of these and other techniques.

Unfortunately, both approaches must actually change the code the JVM will run, and many users are afraid (rightfully so) that this transformation may add new bugs to their applications. Furthermore, method and field renaming can cause reflection calls to stop working. Changing actual class and package names can break several other Java APIs (JNDI (Java Naming and Directory Interface), URL providers, etc.). In addition to altered names, if the association between class byte-code offsets and source line numbers is altered, recovering the original exception stack traces could become difficult.

Then there is the option of obfuscating the original Java source code. But fundamentally this causes a similar set of problems. Encrypt, not obfuscate?

Perhaps the above has made you think, "Well, what if instead of manipulating byte code I encrypt all my classes after compilation and decrypt them on the fly inside the JVM (which can be done with a custom classloader)? Then the JVM executes my original byte code and yet there is nothing to decompile or reverse engineer, right?"

Unfortunately, you would be wrong, both in thinking that you were the first to come up with this idea and in thinking that it actually works. And the reason has nothing to do with the strength of your encryption scheme.