Created
July 11, 2021 16:27
-
-
Save ZekerZhayard/aa015e63a068b6ab7b0d610942527a75 to your computer and use it in GitHub Desktop.
HOLLY SHIT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.zekerzhayard.wrappertest; | |
import java.io.File; | |
import java.lang.invoke.MethodHandles; | |
import java.lang.invoke.MethodType; | |
import java.lang.module.Configuration; | |
import java.lang.module.ModuleDescriptor; | |
import java.lang.module.ModuleFinder; | |
import java.lang.module.ModuleReference; | |
import java.lang.module.ResolvedModule; | |
import java.nio.file.Path; | |
import java.nio.file.Paths; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.function.Function; | |
import java.util.stream.Collectors; | |
import sun.misc.Unsafe; | |
public class Wrapper { | |
private static List<String> moduleList = List.of("asm-", "bootstraplauncher-", "securejarhandler-"); | |
@SuppressWarnings("unchecked") | |
public static void main(String[] args) throws Throwable { | |
// Get theUnsafe | |
var unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); | |
unsafeField.setAccessible(true); | |
var unsafe = (Unsafe) unsafeField.get(null); | |
// Get IMPL_LOOKUP | |
var implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); | |
var implLookup = (MethodHandles.Lookup) unsafe.getObject(unsafe.staticFieldBase(implLookupField), unsafe.staticFieldOffset(implLookupField)); | |
// Define "legacyClassPath" | |
System.setProperty("legacyClassPath", System.getProperty("java.class.path")); | |
// Find all extra modules | |
var finder = ModuleFinder.of(Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator)).map(Paths::get).filter(p -> moduleList.stream().anyMatch(s -> p.getFileName().toString().startsWith(s))).toArray(Path[]::new)); | |
var loadModuleMH = implLookup.findVirtual(Class.forName("jdk.internal.loader.BuiltinClassLoader"), "loadModule", MethodType.methodType(void.class, ModuleReference.class)); | |
// Resolve modules to a new config | |
var config = Configuration.resolveAndBind(finder, List.of(ModuleLayer.boot().configuration()), finder, finder.findAll().stream().peek(mref -> { | |
try { | |
// Load all extra modules in system class loader (unnamed modules for now) | |
loadModuleMH.invokeWithArguments(ClassLoader.getSystemClassLoader(), mref); | |
} catch (Throwable throwable) { | |
throw new RuntimeException(throwable); | |
} | |
}).map(ModuleReference::descriptor).map(ModuleDescriptor::name).collect(Collectors.toList())); | |
// Copy the new config graph to boot module layer config | |
var graphGetter = implLookup.findGetter(Configuration.class, "graph", Map.class); | |
var graphMap = new HashMap<>((Map<ResolvedModule, Set<ResolvedModule>>) graphGetter.invokeWithArguments(config)); | |
var cfSetter = implLookup.findSetter(ResolvedModule.class, "cf", Configuration.class); | |
// Reset all extra resolved modules config to boot module layer config | |
graphMap.forEach((k, v) -> { | |
try { | |
cfSetter.invokeWithArguments(k, ModuleLayer.boot().configuration()); | |
v.forEach(m -> { | |
try { | |
cfSetter.invokeWithArguments(m, ModuleLayer.boot().configuration()); | |
} catch (Throwable throwable) { | |
throw new RuntimeException(throwable); | |
} | |
}); | |
} catch (Throwable throwable) { | |
throw new RuntimeException(throwable); | |
} | |
}); | |
graphMap.putAll((Map<ResolvedModule, Set<ResolvedModule>>) graphGetter.invokeWithArguments(ModuleLayer.boot().configuration())); | |
implLookup.findSetter(Configuration.class, "graph", Map.class).invokeWithArguments(ModuleLayer.boot().configuration(), Map.copyOf(graphMap)); | |
// Reset boot module layer resolved modules as new config resolved modules to prepare define modules | |
var oldBootModules = ModuleLayer.boot().configuration().modules(); | |
var modulesSetter = implLookup.findSetter(Configuration.class, "modules", Set.class); | |
var modulesSet = new HashSet<>(config.modules()); | |
modulesSetter.invokeWithArguments(ModuleLayer.boot().configuration(), Set.copyOf(modulesSet)); | |
// Prepare to add all of the new config "nameToModule" to boot module layer config | |
var nameToModuleGetter = implLookup.findGetter(Configuration.class, "nameToModule", Map.class); | |
var nameToModuleMap = new HashMap<>((Map<String, ResolvedModule>) nameToModuleGetter.invokeWithArguments(ModuleLayer.boot().configuration())); | |
nameToModuleMap.putAll((Map<String, ResolvedModule>) nameToModuleGetter.invokeWithArguments(config)); | |
implLookup.findSetter(Configuration.class, "nameToModule", Map.class).invokeWithArguments(ModuleLayer.boot().configuration(), Map.copyOf(nameToModuleMap)); | |
// Define all extra modules and add all of the new config "nameToModule" to boot module layer config | |
((Map<String, Module>) implLookup.findGetter(ModuleLayer.class, "nameToModule", Map.class).invokeWithArguments(ModuleLayer.boot())).putAll((Map<String, Module>) implLookup.findStatic(Module.class, "defineModules", MethodType.methodType(Map.class, Configuration.class, Function.class, ModuleLayer.class)).invokeWithArguments(ModuleLayer.boot().configuration(), (Function<String, ClassLoader>) name -> ClassLoader.getSystemClassLoader(), ModuleLayer.boot())); | |
// Add all of resolved modules | |
modulesSet.addAll(oldBootModules); | |
modulesSetter.invokeWithArguments(ModuleLayer.boot().configuration(), Set.copyOf(modulesSet)); | |
// Reset cache of boot module layer | |
implLookup.findSetter(ModuleLayer.class, "modules", Set.class).invokeWithArguments(ModuleLayer.boot(), null); | |
implLookup.findSetter(ModuleLayer.class, "servicesCatalog", Class.forName("jdk.internal.module.ServicesCatalog")).invokeWithArguments(ModuleLayer.boot(), null); | |
// Add reads from extra modules to jdk modules | |
var implAddReadsMH = implLookup.findVirtual(Module.class, "implAddReads", MethodType.methodType(void.class, Module.class)); | |
config.modules().forEach(rm -> ModuleLayer.boot().findModule(rm.name()).ifPresent(m -> oldBootModules.forEach(brm -> ModuleLayer.boot().findModule(brm.name()).ifPresent(bm -> { | |
try { | |
implAddReadsMH.invokeWithArguments(m, bm); | |
} catch (Throwable throwable) { | |
throw new RuntimeException(throwable); | |
} | |
})))); | |
// Add opens and exports, securejarhandler requires it | |
ModuleLayer.boot().findModule("cpw.mods.securejarhandler").ifPresent(m -> { | |
try { | |
implLookup.findVirtual(Module.class, "implAddExports", MethodType.methodType(void.class, String.class, Module.class)).invokeWithArguments(Object.class.getModule(), "sun.security.util", m); | |
implLookup.findVirtual(Module.class, "implAddOpens", MethodType.methodType(void.class, String.class, Module.class)).invokeWithArguments(Object.class.getModule(), "java.util.jar", m); | |
} catch (Throwable throwable) { | |
throw new RuntimeException(throwable); | |
} | |
}); | |
// Launch | |
implLookup.findStatic(Class.forName("cpw.mods.bootstraplauncher.BootstrapLauncher"), "main", MethodType.methodType(void.class, String[].class)).invokeWithArguments(new Object[] { args }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment