Browse documentation
Documentation / Protection passes / Class name obfuscation

Class name obfuscation

ImplementedEvery eligible class is renamed into one flat, opaque package.

What it does

Every eligible class is renamed into one shared, flat package using short, opaque names (a.a, a.b, ... rolling over past 26 the way a spreadsheet column does). The rename is applied with a constant-pool-aware bytecode remapper, not hand-rolled patching, so it rewrites every place a class name can appear: descriptors, signatures, InnerClasses/EnclosingMethod metadata, annotation values, an LDC of a Class literal, and stack-map frame type entries.

Scope and boundaries

Before any renaming happens, every LDC string operand and every static final String field value across the whole JAR is collected. A class whose name appears anywhere in that set, a Class.forName target, a ServiceLoader provider-file entry, is excluded from renaming entirely, since a bytecode remapper cannot see, and therefore cannot safely rewrite, a reference that only exists as a string. The JAR's own Main-Class, if declared, is excluded, since renaming it would make the JAR unlaunchable. Interfaces are renamed like any other class (nativization's separate, unrelated exclusion of interface methods doesn't apply here, see Nativization).

What you get alongside the JAR

A plain-text rename map (<output>.rename-map.txt, sorted, dotted form) is written alongside the output JAR. RJNI does not read it back; it is there so you can tell which obfuscated class was which when comparing before/after output.

Threat model

Removes your package and class names from the constant pool and every metadata location a decompiler or a static analysis tool would otherwise read them from. Does not rename anything reachable only through a string constant, see Scope above, and a class with an external supertype still keeps its original name if renaming would otherwise break something this pass can't see (this specific case doesn't apply to class renaming itself, but does apply to member renaming, see Method & field renaming).

Verified. On an unmodified Apache Commons Exec build: every eligible class renamed, with the two name-referenced classes left alone. On the 1,208-class application: 1,208 of 1,208 classes eligible and renamed, zero excluded, with zero IllegalAccessError/VerifyError in the output, including a package-private interface renamed consistently alongside its public implementor, an edge case found and fixed during that run.