The Struggles of Editing JAR files
A month ago, when I was working as usual, on a project called Caffeine in Ptidej, I encountered a GUI prompt out of nowhere flashing the message "This message appears because JIProlog is not registered." while running Junit tests on the intended project.
I then discussed this prompt with my supervisor Yann, who mentioned it was indeed bought and registered. So I thought of using a Java decompiler – to see what JIProlog was doing and figured out where exactly it was causing the issue. It was occurring at the verification of the binary /regcred.bin (registration credential) against /regver.bin (registration verification).
I speculated that it might be due to the binaries being corrupted but I couldn’t check it further as it would be tedious, so I took a step back to think how I could prevent this prompt from showing up. I identified that, maybe I could delete the try and subsequent catch blocks, which validate the binary files that can resolve this issue.
Since JAR files contain compiled classes, I couldn’t edit them directly like normal Java files. My first thought was to download the source code but it wasn’t available for this particular version. So, I used JD-GUI to save the source code from the decompiled classes. However, this source code wasn’t useful, because it saved the decompiled classes. Let’s see it a bit in detail, Java compiles to .class files and for optimization and obfuscation, it modifies the identifiers of variables, methods and classes. It replaced with letters and constants and reserved key words like ‘for’ ‘while’ ‘if’ and such. This is cs.java, and we can see the renaming of the classes and variables here.
This obfuscation makes it very hard to understand what is going on, and if you try to recompile it again, it won’t compile and throw it out errors left and right. Without the sources of the JAR files, it’s sort of a one way ticket, but not entirely.
Then, I discussed with my supervisor to see if there was a way, and that’s when we thought of using a deobfuscator, which de-obfuscates the compiled classes and gives the Java file which can be recompiled. I searched on GitHub for deobfuscators and there were some good projects, some active and some not supported any more. I tried a couple of them but most didn’t seem to be working well. There are a couple of reasons for that. Suppose the obfuscation happened with changing the identifiers, there’s no way to reverse it unless we have the source file to it and of course, the obfuscation used must be identified by the deobfuscators, which wasn’t the case with our JAR file.
With no sign of luck, I was on my way to find other projects that might help. That’s when I stumbled upon the GitHub Repo Recaf. Recaf is an open-source byte-code editor the decompiles and recompiles Java code, allows bytecode editing and even editing the Hex Table itself.
So, once you put your JAR file and it will decompile it and view it in decompile mode initially. There are 3 open-source decompilers among which to choose: CFR, FernFlower (maintained by IntelliJ), and Procyon. CFR supports most modern Java features till 14, Fernflower is an analytical decompiler which analyses the compiled output and Procyon supports Java 5 and is similar to the handling done by CFR.
But it’s less efficient when dealing with obfuscation and tries to recreate the source code at it’s best, but most of the time it can’t be recompiled again, though it gives us a rough idea of the actual workings of the decompiled application. So, let’s try to decompile our JAR file using CFR.
Seems like we have gotten a bunch of parse errors. Actually, I could delete the try-catch block of code which will disable the prompt from showing up, but it will not work, since the entire jar file has to recompile again which is not possible, thanks to obfuscation and the errors. Even other decompilers were producing similar scenarios.
Then I thought of editing at the byte code level, and since Recaf provides support for it, we could make use of the ‘table’ class mode that segregates classes, fields and methods. This helps to localize the code and makes it easier to make the necessary changes.
Using the decompiler I could identify which constructor in specific I should look at, and it is a constructor with arguments (frame, String, PrintStream). We can edit it with the assembler to view the byte code of that specific code block. Here in line 69, we could see EX_START_5 which indicates the try catch block we saw earlier when decompiling.
If we remove from line 69 till line 165 along with Line 2 till Line 6, the try and catch blocks will be removed and the byte code will be saved and the entire JAR file will recompile in this way. Finally, we could remove the flashing GUI prompt and run test cases with ease : )
References :
https://www.coley.software/Recaf/
https://github.com/Col-E/Recaf
https://stackoverflow.com/questions/62298929/what-is-an-analytical-decompiler/
https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine