我想将一些代码注入现有的类/方法。但是我无法让类加载器“找到”类以使用修改后的字节码。
MyClassInjector.java
import org.objectweb.asm.*;
public class MyClassInjector {
public static void main(String[] args) throws Exception {
// Load the MyClass class
ClassReader cr = new ClassReader("MyClass");
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
MyClassVisitor cv = new MyClassVisitor(cw);
cr.accept(cv, 0);
// Inject code into the myMethod method
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "myMethod", "()V", null, null);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "MyClassInjector", "newMethod", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
// Define the newMethod method
MethodVisitor mv2 = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "newMethod", "()V", null, null);
mv2.visitCode();
mv2.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv2.visitLdcInsn("Injected code");
mv2.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv2.visitInsn(Opcodes.RETURN);
mv2.visitMaxs(0, 0);
mv2.visitEnd();
// Define the new byte array with the modified class bytecode
byte[] modifiedClass = cw.toByteArray();
// Define a new class loader to load the modified class
ClassLoader cl = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("MyClass")) {
return defineClass(name, modifiedClass, 0, modifiedClass.length);
} else {
return super.findClass(name);
}
}
};
// Load the modified class and call myMethod
Class<?> myClass = cl.loadClass("MyClass"); <----------------------- HERE
Object myObject = myClass.newInstance();
myClass.getMethod("myMethod").invoke(myObject);
}
}
class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
}
MyClass.java
public class MyClass {
public void myMethod() {
System.out.println("Hello, world!");
}
}
当我调用 loadClass(上面用 HERE 标记)时,它没有调用“findClass”,所以方法没有被修改。根据我的阅读,loadClass() 应该对 findClass() 进行分类。有什么想法吗?
调用
findClass
时没有调用loadClass
方法的原因是Java中的ClassLoader
实现使用委托模型加载类。这意味着当您调用 loadClass
时,ClassLoader
将首先尝试在其缓存中查找该类,然后委托其父级 ClassLoader
加载该类。如果父ClassLoader
找不到类,那么ClassLoader
将调用它自己的findClass
方法。
在您的情况下,您没有指定父级
ClassLoader
,因此Java中的ClassLoader
实现将使用系统类加载器作为父级。由于系统类加载器已经加载了MyClass
类,所以当你调用loadClass时它不会调用你的ClassLoader的findClass方法。
要解决此问题,您可以在创建
ClassLoader
对象时指定父级 ClassLoader
。例如,您可以传递 null 以使用引导类加载器作为父级:
ClassLoader cl = new ClassLoader(null) {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("MyClass")) {
return defineClass(name, modifiedClass, 0, modifiedClass.length);
} else {
return super.findClass(name);
}
}
};
有了这个改变,当你调用
loadClass
时,ClassLoader
实现将首先委托给bootstrap类加载器,它不会找到MyClass
类,所以它会调用你的ClassLoader的findClass
方法来加载修改类。
干杯,祝你好运