2 09 2007

In response to my preceding post about An attempt to find a suitable “Obfuscator”, Hans-Eric Grönlund asked for more details of the “@Obfuscatable” annotation that I mentioned.

So for Hans-Eric and anyone else that’s interested, here are the full details.

The annotation itself is trivial, as it’s only used to mark the particular methods that I want to apply code-flow obfuscation to. Excluding the javadoc, it’s just:

package com.openbrace.obcommon.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

public @interface Obfuscatable {

For now, this is just a single annotation with no properties, and that can only be used on methods and isn’t inheritable. That’s all I need, as I’m only concerned with obfuscating the internal implementation of selected methods.

Potentially, one could add properties (or use separate annotations) to specify which particular types of obfuscation (renaming, control-flow, removal of debug information etc) are required for each method. Equally, one could allow the annotation of classes and fields to control “renaming” obfuscation, or the annotation of classes to indicate that all of the class’s methods are obfuscatable.

But the semantics of combinations of such annotations can quickly become complex and dependent on arbitrary rules (as I discussed in Annotations + Inheritance = Confusion). Also, the more detailed and complex the annotation becomes, the less likely it is that an obfuscator can be configured to understand its precise details.

So for my own current purposes, keeping it as simple as possible is fine (especially as this is only for my own internal use, and not part of a public API or otherwise relevant to client code).

Note that:

  • I’ve named this “Obfuscatable”, rather than “Obfuscated”, to emphasise that the method isn’t necessarily always obfuscated, just that it should be targetted for obfuscation in particular deliverables when using a suitable obfuscation tool.
  • The annotation is retained in class files (@Retention of CLASS) so that obfuscators operating on the classes can see it. Ideally one would want to remove it from the class files afterwards (or as part of the obfuscation), as it’s then no longer needed and one doesn’t particularly want to advertise which methods an “attacker” should focus on. But this doesn’t currently seem worth the complication.

This annotation is then added to each of the individual methods whose implementation I want to obfuscate:

public void someSensitiveMethod() {

By having this explicitly present in the code, it documents and highlights which methods are intended to be obfuscated, and makes them easy to find.

Having added the annotation to the relevant methods, it can then be used as a clean and simple way to control the obfuscation process itself – at least for obfuscators that can be appropriately configured.

Now, different obfuscators have different facilities for how to control the obfuscation. To make use of the annotation, we need to be able to configure the obfuscator to select only those methods that have the annotation.

In practice, obfuscators tend to be configurable only in terms of which elements to exclude from the obfuscation. So typically what we need is to exclude all methods that don’t have the annotation (as well as needing whatever other configuration is necessary, e.g. to control what type of obfuscation is carried out). Obviously, not all obfuscators support this, and for those that do the details are obfuscator-specific.

As an example, I’m currently intending to use Zelix KlassMaster, and for this the annotated methods can be excluded from “control-flow” obfuscation by means of the following “obfuscateFlowExclude” command:

obfuscateFlowExclude *.* !(@com.openbrace.obcommon.annotation.Obfuscatable) *(*);

This uses wildcards to select methods in any class and any package (“*.*”) whose method signature does not (“!”) include the annotation, but with any method name and any argument types (“*(*)”). These are excluded from the obfuscation, with the result that a subsequent “obfuscate” command carries out control-flow obfuscation only for those methods that do have the annotation.

For obfuscators that support this type of configuration, this approach seems the simplest, cleanest and most maintainable way to control which specific methods are obfuscated.

Without it, it can be hard or impossible to specify a set of method signatures that jointly identify all of the methods to be excluded whilst not matching any of the methods to be obfuscated. Even where that’s possible, it typically results in a lengthy configuration that includes package names, class names and/or method signatures of the non-obfuscated methods. Maintaining such a configuration could be a real pain and very error-prone. With the annotation-based approach, it’s all controlled by the source code of the obfuscatable methods themselves, and the obfuscator’s configuration remains constant.

For obfuscators that don’t support this type of configuration, you’re stuck with having to find some other way to do the configuration. But even then, having the annotation doesn’t do any harm, and still serves to document which methods are intended to be obfuscated.

Some obfuscators may also provide their own annotations for controlling the obfuscation, either with or without more general support for recognizing arbitrary annotations such as @Obfuscatable. For example, the current beta release of ProGuard version 4.0 provides “@Keep…” annotations of its own that can be used to specify ProGuard “keep” options. This appears to work in conjunction with more general support for recognizing arbitrary annotations, which could just as well be used with @Obfuscatable.

In general, the use of an obfuscator-independent annotation such as @Obfuscatable seems preferable to tying your code to one specific obfuscator or accumulating multiple such obfuscator-specific annotations. But there’s nothing to stop you from doing both. Potentially one might even be able to convert each @Obfuscatable annotation into some obfuscator-specific annotation before running the obfuscator.

For now, my own approach will be to keep the @Obfuscatable annotation as a general-purpose marker, and supplement this with tool-specific annotations for whichever tool I’m currently using if and only if this ever becomes necessary.

This does raise the rather broader issue of when and how annotations for any particular purpose should be standardized. There’s the potential here for many individuals and obfuscator vendors to each define similar but distinct annotations for controlling obfuscation, when a single set of standardized annotations might be preferable. On the other hand, obfuscation doesn’t seem to be a particularly critical or fashionable subject (except perhaps in the guise of “shrinking” code for Java ME applications).

So whilst we have JSR 250 for “Common Annotations” and JSR 305 to standardize annotations for code-checking tools, there probably isn’t enough justification for trying to standardise annotations for obfuscation, and I’m happy enough with my own “@Obfuscatable”.



2 responses

3 09 2007

I have no opinion on obfuscation, I never had the reason to use it myself, but – this is a great, even beautiful, way to use meta properties.

3 09 2007
www.hans-eric.com » Blog Archive » Great use of annotations

[…] Mike Kaufman, for example, used them beautifully to configure an Obfuscator. He have posted the details on his […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: