Java ASM 字节码操作将代码注入到不起作用的方法中

问题描述 投票:0回答:1

我想将一些代码注入现有的类/方法。但是我无法让类加载器“找到”类以使用修改后的字节码。

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() 进行分类。有什么想法吗?

java classloader java-bytecode-asm dynamic-class-loaders
1个回答
0
投票

调用

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
方法来加载修改类。

干杯,祝你好运

© www.soinside.com 2019 - 2024. All rights reserved.