If you're to try this method following the steps described below you'll need to have the following skills and tools:
- Good understanding of Java class file structure
- Good understanding of Java bytecode instructions
- Basic understanding of How LIFO stack works
Update 2014-01-19: Discovered a tool that simplifies the task dramatically. Apart from handy text-editor type interface for bytecode editing, it also adjusts exceptions and conditional jumps addressing automatically. reJ - http://rejava.sourceforge.net/index.html
NOTE: Neutrino EK sample was obtained from www.malware-traffic-analysis.net
Not that long ago Neutrino EK authors started using some bytecode obfuscation technique that rendered some of the Java Decompilers useless when restoring Neutrino's source code. Here is how decompiled Neutrino EK used to look before this 'upgrade'...
Neutrino EK 2013-09 sample decompiled using JD-GUI
Apart from some string obfuscation, the code is pretty readable. And here is how one of the recent samples looks like when decompiled using the same JD-GUI tool...
Neutrino EK 2014-01 sample decompiled using JD-GUI
Where some of the source code was successfully decompiled, the methods are padded with tons of meaningless entries. First thing that comes to mind is to simply remove all of this rubbish. Lets do just that and see what happens...
Neutrino EK decompiled code after rubbish clean up
Some parts of the output can help to understand what the code is doing, but it's not a valid Java code. The same approach definitely wouldn't work for methods similar to the one below...
Neutrino EK decompiled obfuscated Java method
If we're to remove all the rubbish from this method we end up with no code left, but this method actually performs the following...
Neutrino EK decompiled deobfuscated Java method
So, how do we jump from 'no sense' to 'full picture'? The answer is in the bytecode.
'txjLPjLxt' method in bytecode view
What we're looking for are 'push' and 'pop' instructions and more importantly what happens in between. As you read through the bytecode, you might notice patterns start to emerge involving these two operations. Patterns similar to these...
examples of 'push' & 'pop' bytecode patterns
In terms of application execution, these instructions have no effect on any application parts, but doing great keeping CPU busy running them. For example, pattern 1 --> pushes 2 bytes on the stack --> swapping them --> popping them of the stack. So, what happens if we're to relief the CPU from the burden of running these instructions by removing them from the bytecode. The below is what's left of the 'real' instructions...
bytecode after 'push' and 'pop' patterns are removed
Lets now come back to the 'init' method we started with and perform the same bytecode clean up and compare the result with the previous deobfuscation method where we simply removed the rubbish entries from the decompiled code.
Neutrino EK deobfuscation results comparison
Where in a few places the results match, the majority of the decompiled source code is different. After the bytecode clean up we can clearly see what happens step-by-step.
There are a few ways this bytecode clean up method can be performed in. The steps for the simplest one are below:
- take a note of the exceptions(if any exist in the method you're about to edit) - 'start', 'end' and 'handler' addresses and instructions they are pointing at.(my personal preference is to use dirtyJOE for viewing/editing exceptions).
- replace 'push' & 'pop' patterns with 'nop' instructions(Java Bytecode Editor allows you to do it more efficiently through text editor style interface).
- (optional) edit exception/s values if the addressing skewed for some reason
- save the changes and check decompilation results
To make it more challenging you could take it a little bit further and remove 'nop' instructions completely. A good care should be taken here, as any 'conditional jumps', 'goto', etc, instructions would have to be edited (in addition to any existing exceptions) to reflect the new addressing.
NOTE: Java Bytecode Editor performs bytecode integrity check before saving edited methods. It will NOT allow you to save methods that are either performing the sandbox escape(exploit) or those running instructions that leverage post-exploit condition. dirtyJOE will do this dirty job for you, but at the cost of convenient code editor interface.