5

I compiled some [relatively complex] Java code into a .class file, then used jad to decompile it back into java. Of course, the code was obfuscated, which was to be expected. However, given that I had the original code, I thought I'd be able to look through the decompiled code fairly easily. However, I noticed differences in the code, such as where certain variables were defined (including differences of scope).

Is there any main reason for this? I imagine it's one of those things that just happen in decompilation of code, but I'm more curious about what factors cause the change (e.g. complexity of code, whether it refers to other files, etc.).

Could someone provide me with a good explanation on what factors cause the differences in the code before and after?

Edit

Technically, the .class file is pulled from a jar. I extracted the contents and used the .class file in there.

As far as which obfuscator I used, I used the Retroguard obfuscator with the following options (I'm currently just exploring obfuscation and finding out what each thing does to the final result):

.option Application
.option Applet
.option Repackage
.option Annotations
.option MapClassString
.attribute LineNumberTable
.attribute EnclosingMethod
.attribute Deprecated

Documentation for the script can be found on their site. It's a little unorganized, but you should be able to find adequate explanations in there. It's also noteworthy that I stripped the generics and the local variable table.

I have now also set up a way (inspired by the creators of the Minecraft Coder Pack) to rename the sources using data from a file (or files), which is passed to a dictionary of lists for packages, classes, methods, and fields.

# snippet from the MCP version (mine's slightly different) (all in Python):
srg_types = {'PK:': ['obf_name', 'deobf_name'],
            'CL:': ['obf_name', 'deobf_name'],
            'FD:': ['obf_name', 'deobf_name'],
            'MD:': ['obf_name', 'obf_desc', 'deobf_name', 'deobf_desc']}
parsed_dict = {'PK': [],
               'CL': [],
               'FD': [],
               'MD': []}

A line is then parsed from the file, and it is passed into the parsed_dict and then used to rename everything (back and forth). Implemented after compiling than decompiling the first time (after I noticd differences).

  • By the way, which obfuscator did you use? Doesn't sound like a very good obfuscators. Also, Jad is pretty old. I'd recommend using Procyon instead for unobfuscated code. – Antimony Jul 23 '13 at 6:06
  • @Antimony Edited the question with the answer to that. As a side note, I'm not trying to completely obfuscate everything right now. I'm throwing things at the wall and seeing what sticks (yes, very disorganized, but also fun). – JMcAfreak Jul 25 '13 at 17:27
  • If you're trying to decompile obfuscated classes, you should be using Krakatau. It's specifically designed to handle obfuscated classes and can often deal with stuff that no other decompiler can. github.com/Storyyeller/Krakatau – Antimony Jul 26 '13 at 4:39
  • @Antimony Sweet. Thanks for the suggestion. I'll look into that. – JMcAfreak Jul 26 '13 at 17:41
10

Java compiles to bytecode that is run in the JVM, and stored in the .class files. This bytecode is not a 1:1 representation of the original code, and includes several compiler-implemented optimizations. Information is lost when these optimizations are performed, and due to that lost information decompilers can't reconstruct the code back into exactly what it was.

6

To add on to what Ditmar said, the big problem is probably your obfuscation. Normal Java bytecode is actually surprisingly close to the original source, at least from the perspective of C or C++ (or even Scala). You'll always lose some information, but unobfuscated Java can be decompiled to something close to the original, especially if you compile with debug information.

All that goes out the window with obfuscation however, as obfuscators deliberately try to manipulate the structure of the code. When decompiling obfuscated code, you should be more amazed when you find similarities at all.

  • 1
    Yeah, according to the poster's edit this is certainly a question sparked by obfuscation. Good call on predicting that. Readers should note that these answers apply to .NET MSIL languages(like C#) in a very similar fashion in regards to obfuscation/their close to 1:1 bytecode. – Ditmar Wendt Jul 26 '13 at 21:01
  • @Ditmar Java's a bit closer to the bytecode then with CIL since the bytecode was only designed for a single language. But you're right that they're very similar. – Antimony Jul 27 '13 at 1:57
4

Your choice of decompiler can greatly affect the outcome. You should try either JODE, or Fernflower (which I believe goes by Androchef).

Before decompiling, it might be a good idea to do some simple deobfuscation yourself. For example, you could try

  • Remapping of Java keywords that have been used as class/method/variable names to legal Java identifiers. This task is very easy to do by using the Remapping adapters provided by the ASM library, http://asm.ow2.org/asm40/javadoc/user/org/objectweb/asm/commons/RemappingClassAdapter.html

  • Some simple reorganization of blocks. Many obfuscators will stick gotos into awkward places that would be "within a single expression" in the Java source code, e.g. pushing something to the stack, then jumping, then storing it to a local variable in the destination is perfectly viable in bytecode, but understanding that the bytecode is simply doing var = value might give some decompilers a hard time.

  • 1
    Fernflower isn't very good, and I don't think it's even publicly available any more. Also, Krakatau will automatically handle the second deobfuscation you mentioned as well as many others, though the results aren't as good if the code is already unobfuscated. – Antimony Aug 30 '13 at 5:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.