Java Class.forName 和混淆名称,java 找不到 class

Java Class.forName and obfuscated names, java fail to find class

我有一个 .jar 文件,其中包含不常见的混淆名称,混淆后的 class 看起来像:
nul.goto. ...\.. .final.(注意:第一个间隙不正常 space,它是不间断的 space)
我无法通过 Class.forName

得到这个 class 我用这样的混淆制作了我自己的代码版本,即使我在混淆后使用 Class.forName(SomeClass.class.getName())(它只是要测试的代码)它也不起作用 java 会抛出它找不到的错误这个class.

如何获得这样的 class?

所以过了一段时间我决定回到这个,我使用这样的代码进行测试:
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() + "`"); }
}

使用 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[]);
}

首先我注意到这不再是 java 11 中的问题:class 那样只会加载失败并出现错误:

Error: LinkageError occurred while loading main class pckg.Main
    java.lang.ClassFormatError: Illegal class name "nul/goto/▒///\// /final//final" in 
class file pckg/Main

但是forNameloadClass之间仍然存在不一致,forName仍然会抛出ClassNotFoundException但是loadClass会抛出ClassFormatError .在这两种情况下,OpenJ9 都会抛出 ClassFormatError

在 java 8 热点上它工作正常,就像@Holger 在评论中建议的那样,带有 ClassLoader 的版本工作正常。所以我决定深入挖掘一下原因。
在 class 加载器中,它只是进入 URLClassLoader 中非常简单的一段代码: name.replace('.', '/').concat(".class"); 并通过本机方法 defineClass 从 jar 中加载 class 没有任何问题.
但是 Class.forName 直接进入本地方法并验证 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;
}

所以所有的.都变成了/然后在VerifyClassname中我们可以找到这个:

if (slash_okay && ch == '/' && last_ch) {
    if (last_ch == '/') {
        return 0;       /* Don't permit consecutive slashes */
    }
}  

来源:http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/native/common/check_format.c#l155

这会阻止 class 加载。