Change reflection loading to replace classes in the jar loader instead, pending testing
This commit is contained in:
		| @@ -3,10 +3,11 @@ package me.libraryaddict.disguise.utilities.reflection.asm; | |||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
| import org.objectweb.asm.*; | import org.objectweb.asm.*; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Modifier; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| @@ -15,25 +16,38 @@ import java.util.Map; | |||||||
|  */ |  */ | ||||||
| public class Asm13 implements IAsm { | public class Asm13 implements IAsm { | ||||||
|     @Getter |     @Getter | ||||||
|     private Method defineMethod; |     private final LibsJarFile libsJarFile; | ||||||
|  |  | ||||||
|     public Asm13() throws NoSuchMethodException { |     public Asm13() throws Throwable { | ||||||
|         defineMethod = getDefineClassMethod(); |         ClassLoader pluginClassLoader = getClass().getClassLoader(); | ||||||
|  |         Class c = Class.forName("org.bukkit.plugin.java.PluginClassLoader"); | ||||||
|  |         Field file = c.getDeclaredField("file"); | ||||||
|  |         file.setAccessible(true); | ||||||
|  |  | ||||||
|  |         libsJarFile = new LibsJarFile((File) file.get(pluginClassLoader)); | ||||||
|  |  | ||||||
|  |         Field field = c.getDeclaredField("jar"); | ||||||
|  |         field.setAccessible(true); | ||||||
|  |  | ||||||
|  |         Field modifiersField = Field.class.getDeclaredField("modifiers"); | ||||||
|  |         modifiersField.setAccessible(true); | ||||||
|  |         modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); | ||||||
|  |  | ||||||
|  |         field.set(pluginClassLoader, libsJarFile); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Class<?> createClassWithoutMethods(String className, |     public void createClassWithoutMethods(String className, ArrayList<Map.Entry<String, String>> illegalMethods) | ||||||
|             ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException, |             throws IOException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { | ||||||
|             IllegalAccessException, NoSuchFieldException { |         className = className.replace(".", "/") + ".class"; | ||||||
|         ClassReader cr = new ClassReader( |  | ||||||
|                 getClass().getClassLoader().getResourceAsStream(className.replace(".", "/") + ".class")); |         ClassReader cr = new ClassReader(getClass().getClassLoader().getResourceAsStream(className)); | ||||||
|         ClassWriter writer = new ClassWriter(cr, 0); |         ClassWriter writer = new ClassWriter(cr, 0); | ||||||
|  |  | ||||||
|         cr.accept(new ClassVisitor(Opcodes.ASM5, writer) { |         cr.accept(new ClassVisitor(Opcodes.ASM5, writer) { | ||||||
|             public MethodVisitor visitMethod(int access, String name, String desc, String signature, |             public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||||||
|                     String[] exceptions) { |  | ||||||
|  |  | ||||||
|                 Map.Entry<String, String> entry = illegalMethods.stream() |                 Map.Entry<String, String> entry = | ||||||
|                         .filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null); |                         illegalMethods.stream().filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null); | ||||||
|  |  | ||||||
|                 if (entry != null) { |                 if (entry != null) { | ||||||
|                     return null; |                     return null; | ||||||
| @@ -43,23 +57,6 @@ public class Asm13 implements IAsm { | |||||||
|             } |             } | ||||||
|         }, 0); |         }, 0); | ||||||
|  |  | ||||||
|         byte[] bytes = writer.toByteArray(); |         libsJarFile.addClass(className, writer.toByteArray()); | ||||||
|  |  | ||||||
|         ClassLoader loader = getClass().getClassLoader(); |  | ||||||
|         Field field = loader.getClass().getDeclaredField("classes"); |  | ||||||
|         field.setAccessible(true); |  | ||||||
|         Map<String, Class<?>> map = (Map<String, Class<?>>) field.get(loader); |  | ||||||
|         Class newClass = (Class<?>) defineMethod.invoke(getClass().getClassLoader(), className, bytes, 0, bytes.length); |  | ||||||
|  |  | ||||||
|         map.put(className, newClass); |  | ||||||
|         return newClass; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private Method getDefineClassMethod() throws NoSuchMethodException { |  | ||||||
|         Method defineClass = ClassLoader.class |  | ||||||
|                 .getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); |  | ||||||
|         defineClass.setAccessible(true); |  | ||||||
|  |  | ||||||
|         return defineClass; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import java.util.Map; | |||||||
|  * Created by libraryaddict on 17/02/2020. |  * Created by libraryaddict on 17/02/2020. | ||||||
|  */ |  */ | ||||||
| public interface IAsm { | public interface IAsm { | ||||||
|     Class<?> createClassWithoutMethods(String className, |     void createClassWithoutMethods(String className, | ||||||
|             ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException, |             ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException, | ||||||
|             IllegalAccessException, NoSuchMethodException, NoSuchFieldException; |             IllegalAccessException, NoSuchMethodException, NoSuchFieldException; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package me.libraryaddict.disguise.utilities.reflection.asm; | ||||||
|  |  | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.jar.JarEntry; | ||||||
|  | import java.util.jar.JarFile; | ||||||
|  | import java.util.zip.ZipEntry; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by libraryaddict on 15/05/2021. | ||||||
|  |  */ | ||||||
|  | public class LibsJarFile extends JarFile { | ||||||
|  |     private final HashMap<String, byte[]> customFiles = new HashMap<>(); | ||||||
|  |  | ||||||
|  |     public LibsJarFile(File file) throws IOException { | ||||||
|  |         super(file); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public synchronized InputStream getInputStream(ZipEntry ze) throws IOException { | ||||||
|  |         if (customFiles.containsKey(ze.getName())) { | ||||||
|  |             return new ByteArrayInputStream(customFiles.get(ze.getName())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return super.getInputStream(ze); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void addClass(String name, byte[] bytes) { | ||||||
|  |         customFiles.put(name, bytes); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void close() throws IOException { | ||||||
|  |         customFiles.clear(); | ||||||
|  |         super.close(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -99,10 +99,10 @@ public class WatcherSanitizer { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             for (Map.Entry<String, ArrayList<Map.Entry<String, String>>> entry : toRemove.entrySet()) { |             for (Map.Entry<String, ArrayList<Map.Entry<String, String>>> entry : toRemove.entrySet()) { | ||||||
|                 Class result = asm.createClassWithoutMethods(entry.getKey(), entry.getValue()); |                 asm.createClassWithoutMethods(entry.getKey(), entry.getValue()); | ||||||
|                 mapped.add(entry.getKey()); |                 mapped.add(entry.getKey()); | ||||||
|             } |             } | ||||||
|         } catch (IOException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException | LinkageError e) { |         } catch (Throwable e) { | ||||||
|             e.printStackTrace(); |             e.printStackTrace(); | ||||||
|             LibsDisguises.getInstance().getLogger().severe("Registered: " + new Gson().toJson(mapped)); |             LibsDisguises.getInstance().getLogger().severe("Registered: " + new Gson().toJson(mapped)); | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user