Reputation: 3675
I have .jar file with not common obfuscated names, obfuscated class looks like:
nul.goto. ...\\.. .final.
(NOTE: this first gap isn't normal space, it's no-break space)
I can't get this class by Class.forName
I make my own version of code with obfuscation like that, and it don't work even if I use Class.forName(SomeClass.class.getName())
(it's just code to test) after obfuscation java will throw error that it can't find this class.
How to get class like that?
Upvotes: 1
Views: 1552
Reputation: 3675
So after some time I decided to go back to this, I used code like this for testing:
pckg.Main
:
public static void main(String[] args) {
Class<ObfuscateMe> obfuscateMeClass = ObfuscateMe.class;
System.out.println("Class name: `" + obfuscateMeClass + "`");
try {
System.out.println("Class.forName: " + Class.forName(obfuscateMeClass.getName()));
}
catch (ClassNotFoundException exception) { System.out.println("Can't find class using Class.forName: `" + obfuscateMeClass.getName() + "`"); }
try {
System.out.println("ClassLoader.loadClass: " + Main.class.getClassLoader().loadClass(obfuscateMeClass.getName()));
}
catch (ClassNotFoundException exception) { System.out.println("Can't find class using ClassLoader.loadClass: `" + obfuscateMeClass.getName() + "`"); }
}
Obfuscated with ProGuard:
-injars 'B:\Java\ProGuardTestApp\test.jar'
-outjars 'B:\Java\ProGuardTestApp\testObf.jar'
-dontshrink
-dontoptimize
-classobfuscationdictionary 'B:\Java\ProGuardTestApp\obfs.txt' # contains just one line "final"
-repackageclasses 'nul.goto. ...\\.. .final.'
-ignorewarnings
-keepclasseswithmembers public class * {
public static void main(java.lang.String[]);
}
First I noticed that this no longer is a problem in java 11: class like that will just fail to load with error:
Error: LinkageError occurred while loading main class pckg.Main
java.lang.ClassFormatError: Illegal class name "nul/goto/▒///\\// /final//final" in
class file pckg/Main
But there is still inconsistency between forName
and loadClass
, forName
would still throw ClassNotFoundException
but loadClass
with throw ClassFormatError
instead. OpenJ9 throws ClassFormatError
in both cases.
On java 8 hotspot it works fine, and just like @Holger suggested in comment, version with ClassLoader works just fine. So I decided to dig a bit a find out why.
In class loader it just goes to very simple piece of code inside URLClassLoader
: name.replace('.', '/').concat(".class");
and just load that class from jar without any issues via native method defineClass
.
But Class.forName
goes directly to native method and validates class name: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/native/java/lang/Class.c
if (VerifyFixClassname(clname) == JNI_TRUE) {
/* slashes present in clname, use name b4 translation for exception */
(*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname);
JNU_ThrowClassNotFoundException(env, clname);
goto done;
}
if (!VerifyClassname(clname, JNI_TRUE)) { /* expects slashed name */
JNU_ThrowClassNotFoundException(env, clname);
goto done;
}
So all .
are changed to /
and then in VerifyClassname
we can find this:
if (slash_okay && ch == '/' && last_ch) {
if (last_ch == '/') {
return 0; /* Don't permit consecutive slashes */
}
}
And that prevents class from loading.
Upvotes: 1