Change reflection loading to replace classes in the jar loader instead, pending testing

This commit is contained in:
libraryaddict 2021-05-15 12:31:45 +12:00
parent 44ab504509
commit c8bd25ba57
4 changed files with 71 additions and 34 deletions

View File

@ -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;
} }
} }

View File

@ -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;
} }

View File

@ -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();
}
}

View File

@ -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));
} }