This all happened to coincide with my own search for an obfuscator, so it seems worth describing what I found.
My reason for looking for an obfuscator was to add some degree of protection to some of the licencing code in my ObMimic product. I’m not expecting to make this “unbreakable” – realistically there’s very little one can do to make such code un-hackable. Also, being a tool for Java developers, I’d expect ObMimic’s users to be rather more trustworthy and respectful of licences than the general population (and legitimate licences are going to be pretty cheap anyway).
It’s also important that the rest of the ObMimic library remains amenable to IDE code-completion, variable inspection in debuggers etc.
So all I’m looking for is some way to make a handful of specific classes and methods at least slightly non-trivial to decompile, adjust and replace. For such limited results, the effort and cost involved needs to be low, and there needs to be minimal risk of introducing any bugs or other problems. Also, any such tool will run as part of an Ant build script, so Ant integration is relevant but any GUI front-end isn’t.
Given the tendency for everything to be open-source these days, one wouldn’t expect obfuscators to be a hot topic. But even taking this into account a quick look at various Java obfuscators was rather disappointing:
- Many of these tools haven’t been updated in a long time, don’t cater for Java SE 5 or higher, and look like “abandonware”.
- Even some of those that are still “current” have a rather ancient feel (e.g. “COM.*” package names, no Ant task, use of CLASSPATH etc).
- For most of the tools, “obfuscation” is almost synonymous with renaming, which is seen as being for reducing the size of the code at least as much as for obfuscation. It might be more accurate to describe these as “renaming” tools that claim size reduction and obfuscation as possible benefits (with varying degrees of emphasis).
- They all seem to be based on wanting to obfuscate everything and having to identify or specify which things should be left un-obfuscated. Any explicit configuration tends to be in terms of what should be excluded from the obfuscation, sometimes separately for different types of obfuscation. This can make it difficult or impossible to specify that you only want to apply particular obfuscations to particular elements.
- It’s often surprisingly hard to check exactly what changes these tools have made – their logs tend to show either too little or too much (e.g a handful of changes buried in lists of everything that has not been changed).
- For the commercial products, support is generally by e-mail, with no obvious sign of any public forums or bug lists.
The overall impression is that most of these tools are rather long in the tooth, and that many of them are focused primarily on “renaming” just on the basis that it can be done, rather than addressing the requirements for protecting code from reverse-engineering.
For my own purposes, “renaming” isn’t much use. I’m just looking to protect selected methods whilst leaving most of the code amenable to examination and debugging within IDEs, and renaming isn’t going to achieve much if it only affects a tiny number of methods and local variables. So instead I’m looking for “control-flow” obfuscation and any other anti-decompiler measures that can be applied to selected classes and methods.
After ruling out the “abandonware”, products without “control-flow” obfuscation, and products that are otherwise clearly unsuitable, I ended up with the following shortlist (all commercial products, by the way):
- Dash-O (version 3.3)
- SmokeScreen (version 3.53)
- Allatori (version 1.6)
- Zelix KlassMaster (version 5.03)
Of these, Dash-O might be very good – the web-site and documentation look as good or better than any of the others, it seems to have a full set of features, and they claim it’s used by Sun for JCE and JSSE code. However, it’s more expensive than I’m willing to pay at the moment given my limited needs and limited funds ($1,890 for a single named developer, or $4,950 for one “build machine” and up to five named developers), whilst not offering me anything especially unique. So I haven’t looked at it in any detail.
SmokeScreen looked quite promising, but proved rather tricky to configure as required. More fundamentally, it crashed on one of my classes (with a “Non-array descriptor for array reference” message, on code that compiles and runs successfully, passes all my FindBugs/PMD/Checkstyle checks, and didn’t cause any problems for the other tools).
Allatori looked good in terms of overall quality and usability, but its configuration facilities didn’t seem to provide any way to restrict it to just “control-flow” obfuscation on just the relevant methods. Also, when applied to ObMimic’s licencing code its “control-flow” obfuscation had only minimal effect. It did give better results on other methods, so at a pinch this could possibly be improved by adjusting the code, but that still wouldn’t solve the configuration issues.
Zelix KlassMaster nearly didn’t get looked at, because their web-site rather put me off – the home page looks amateurish and loads a fairly pointless video.
When I did try it, the scripting language through which it can be controlled looked very flexible but proved rather tricky to configure correctly. In the end I got it to do what I needed by:
- Defining my own @Obfuscatable annotation, using this to mark the methods I want to have obfuscated, and then configuring KlassMaster to exclude all methods that don’t have this annotation. This led to a much simpler and more maintainable KlassMaster configuration, and looks like a useful general technique regardless of the particular obfuscation tool used.
- Using the “verbose” option for KlassMaster’s log file so that it explains the reasons for excluding individual methods. This pointed out a particular call from a static-initializer as its reason for not obfuscating one of the methods. A minor change to the relevant code solved this.
Having sorted this out, the resulting obfuscation looks ok – viewing the class files in a decompiler shows some pretty garbled code including untranslated byte-code. It’s a bit hard to make a final judgement, as the evaluation version of KlassMaster limits its “code-flow” obfuscation to no more than one or two methods per class, but it seems to be doing what I need from it.
So for the time being, my plan is to go with Zelix KlassMaster combined with my own @Obfuscatable annotation to indicate the relevant methods.
I do like the idea of having an annotation to control and document which methods should be obfuscated, so will probably keep this even if I ultimately end up switching to a different obfuscator.
More generally, this is clearly a small and unfashionable niche within the overall Java ecology, but you’d think there would be scope for someone to come in with a truly modern tool for protecting Java class files from reverse-engineering. My wish list would be:
- Focused on code-flow obfuscation and other “anti-decompiler” techniques rather than renaming.
- Taking advantage of the latest JVM enhancements where possible. For example, perhaps the addition of “invokedynamic” to the JVM will provide new ways to adjust byte-code so that it can’t be decompiled to Java source code?
- Ability to specify positive configuration of what technniques to apply to which elements, rather than “negative” exclusions.
- Logs that clearly show what changes the tool has made.
- Cheaper than Dash-O.
- With a modern web-site and public discussion forums.
It’s hard to tell if there’s enough of a market for anyone to make money out of this, but a good newcomer ought to be able to completely outclass the existing products, maybe even redefine the whole issue.