How to patch Java font rendering for AA
November 11, 2020 - 4 min read
This post was inspired by a hypothetical closed source piece of software from
a hardware vendor, written in Java, which has unusable font rendering that
makes it inaccessible for me, but I need to use it for class, so what am I to
do? I want to write evil
LD_PRELOAD hacks but it's probably easier to patch
the program itself, so that's what we're going to do.
I use IntelliJ IDEA for my Java work. It includes quite a nice Java decompiler, which is (probably) intentionally not exposed to the user in its full functionality, but includes a main class that lets us access it anyway.
First, make an IntelliJ project for your sources. Include all the libraries that they depend on. Now, time for some mild reversing!
Decompile the bad JAR file (hat tip to StackOverflow):
PS> $p = 'C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.2.1\plugins\java-decompiler\lib\java-decompiler.jar' PS> mkdir decomp PS> java -cp $p org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler .\ProblemProgram.jar decomp
Then, you will get a source JAR with all the sources in it. You can just unzip this with whatever tool you prefer:
PS> Expand-Archive decomp/ProblemProgram.jar -dest src
You should have all the files in your source directory and can work on them!
There are probably a pile of compile errors, because decompilers aren't
perfect. They are, however, likely fairly easy to fix to convince the project
to build. In the tool I patched, it was primarily mysteriously inserted
javac getting confused about generics.
Time to patch!
Classes that you are looking for are subclassing
JPanel or similar AWT
classes. They should have a
setFont call you can patch, and an implementation
paint(Graphics). First, patch the
setFont in the sources to use a better
font (because their choice is probably not good):
this.setFont(new Font("Iosevka", 0, 14));
Then, for the magic incantations to patch the actual rendering (thanks again, StackOverflow):
// at the top of the file import java.awt.Graphics2D; import java.awt.RenderingHints; // in paint(Graphics g) ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
This enables subpixel antialiasing (which is superior to the default antialiasing type that ends up rather blurry).
Recompile, and you can do the final stage of patching:
Reincorporating the patches
You can use the
jar tool included with your Java Development Kit to update
the file. Note that the path to the class file must have the package name at
# Make a backup!! PS> cp ProblemProgram.jar ProblemProgram-orig.jar # Patch it! PS> jar uf ../ProblemProgram.jar com/problemcompany/problemprogram/UI.class
We replace only the class that is causing us problems to reduce exposure to anything bad that happened in the round trip through the decompiler.
Now, for the result:
Font rendering may not be the only thing wrong with this closed source program, and you have to figure out some weird behaviours or find a configuration file. A debugger can be fantastically useful for this purpose. IntelliJ provides quite a smooth experience at debugging closed source code.
If you don't want to commit to fixing any decompilation errors, you can add the
program's JAR as a library in
File>Project Structure in IDEA, and it will let
you set breakpoints in arbitrary class files without having to decompile and
Run the program with a similar Java command to this:
PS> java '-agentlib:jdwp=transport=dt_socket,address=127.0.0.1:5678,server=y,suspend=y' -jar C:\ThatVendor\ProblematicProgram.jar
Once you have the configuration set up in IDEA, you can click the "Debug" button and it will connect to your JVM and start running the remote program.
In case we're thinking of the same program from a blue FPGA vendor
Monitor_Program/amp.config has a setting
debug yes to enable a debug
console, though it doesn't have much of interest in it.
monitor.properties has a setting
that, if disabled, as it seems to have done to itself initially, it disables
all the file related functionality in the program, which is quite confusing
indeed (and was the reason I first got out the decompiler).