package vh;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;

public class StealLookupObjectTest {

    public static void main(String... args) throws Throwable {

        try {
            System.out.println("Trying to steal Unsafe constructor...");
            stealConstructor();
            System.out.println("SUCCESS");
        } catch (Throwable t) {
            System.out.println("FAILED: ");
            t.printStackTrace(System.out);
        }

        try {
            System.out.println("Trying to steal Lookup object first...");
            stealLookupObject();
            System.out.println("SUCCESS");
        } catch (Throwable t) {
            System.out.println("FAILED: ");
            t.printStackTrace(System.out);
        }
    }

    public static void stealConstructor() throws Throwable {
        final Class<?> U_CLASS = Class.forName("jdk.internal.misc.Unsafe");
        Constructor<?> constructor = U_CLASS.getDeclaredConstructor();

        // Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
        // Unable to make member of class jdk.internal.misc.Unsafe accessible:
        //   module java.base does not export jdk.internal.misc to unnamed module @5419f379
        constructor.setAccessible(true);

        Object UNSAFE = constructor.newInstance();
        // ...
    }

    public static void stealLookupObject() throws Throwable {
        final Class<?> U_CLASS = Class.forName("jdk.internal.misc.Unsafe");

        final Constructor<MethodHandles.Lookup> lookupConstructor =
                MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);

        lookupConstructor.setAccessible(true); // IDKFA!

        final MethodHandles.Lookup lookup =
                lookupConstructor.newInstance(U_CLASS); // GOD MODE ENABLED

        final MethodHandle unsafeCnstr = lookup.findConstructor(
                U_CLASS,
                MethodType.methodType(void.class));

        final MethodHandle varHandle = lookup.findVirtual(
                U_CLASS,
                "allocateUninitializedArray",
                MethodType.methodType(Object.class, Class.class, int.class));

        final Object UNSAFE = unsafeCnstr.invoke();

        int[] arr = (int[]) varHandle.invoke(UNSAFE, int.class, 10);

        if (arr.length != 10) {
            throw new IllegalStateException("Whoops");
        }
    }

}
