2024/08/19

Hidden Class in java 15

Java 15 提供規格 JEP-371 稱為 Hidden Class 的功能,Hidden Class 提供高效且彈性的功能,可在 run time 動態產生 class。hidden class 無法直接被 byte code 或其他 classes 使用,可以用在特殊的保護的程式碼,讓這些程式碼,不會在 VM 裡面被動態修改。

Hidden Class 的特性

hidden class 動態產生的 class 有以下特性

  • non-discoverable

    無法在 bytecode 被使用,無法被 class loader 使用,無法用 reflective methods ( Class:forName, ClassLoader:findLoadedClass , Lookup:findClass) 找到這些 class

  • 不能將 hidden class 當作 superclass, field tyoe, return type, parameter type

測試

以下測試的概念,是將要被隱藏的類別,先編譯出來,然後把編譯後的 byte code,用 base 64 的方式編碼。

該編碼後的字串,就等同於一個已經編譯過的類別,可以用 Hidden Class 的方式,載入該類別並呼叫該類別的一個靜態 method

hidden/Hello.java

package hidden;

public class Hello {
    public static String greet() {
        return "hello";
    }
}

hidden/ClassToBase64Converter.java

這個 class 是用來將 class byte code 的 byte array,用 base64 編碼

package hidden;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class ClassToBase64Converter {
    public static void main(String[] args) {
        // Provide the path to your .class file
        Class<?> clazz = hidden.Hello.class;
        String className = clazz.getName();
        String classAsPath = className.replace('.', '/') + ".class";

        try {
            byte[] classBytes;
            try (InputStream stream = clazz.getClassLoader().getResourceAsStream(classAsPath)) {
                // Read the .class file as bytes
//            byte[] classBytes = Files.readAllBytes(Paths.get(classFilePath));
                classBytes = stream != null ? stream.readAllBytes() : new byte[0];
            }
            // Encode the bytes to Base64
            String base64Encoded = Base64.getEncoder().encodeToString(classBytes);
            // Print or use the Base64-encoded string
            System.out.println("Base64 Encoded Class:\n" + base64Encoded);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行後可得到這個結果

Base64 Encoded Class:
yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAFaGVsbG8HAAoBAAxoaWRkZW4vSGVsbG8BAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEADkxoaWRkZW4vSGVsbG87AQAFZ3JlZXQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAFAAEAEgAAAAIAEw==

接下來可把這個 base64 字串,放到另一個 class 的 string

package hidden;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;

public class HiddenClassDemo {
    static final String CLASS_IN_BASE64 = "yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAFaGVsbG8HAAoBAAxoaWRkZW4vSGVsbG8BAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEADkxoaWRkZW4vSGVsbG87AQAFZ3JlZXQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAFAAEAEgAAAAIAEw==";
    public static void main(String[] args) throws Throwable {
        testHiddenClass();
    }
    // create a hidden class and run its static method
    public static void testHiddenClass() throws Throwable {
        byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
        Class<?> proxy = MethodHandles.lookup()
                .defineHiddenClass(classInBytes,
                        true, MethodHandles.Lookup.ClassOption.NESTMATE)
                .lookupClass();
        System.out.println(proxy.getName());
        MethodHandle mh = MethodHandles.lookup().findStatic(proxy,
                "greet",
                MethodType.methodType(String.class));
        String status = (String) mh.invokeExact();
        System.out.println(status);
    }
}

執行結果

hidden.Hello/0x0000000800c00400
hello

References

Hidden Classes in Java 15 | Baeldung

What are Hidden Classes in Java 15 And How Are They Used? | foojay

Hidden Class in Java - Java Code Geeks

How to use Hidden Classes in Java - Mastertheboss

沒有留言:

張貼留言