You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java ASM字节码注入失效:自定义类加载器未触发findClass求助

问题原因

Java类加载器遵循双亲委派模型:调用loadClass时,自定义类加载器会先委托父类加载器(此处为系统类加载器)尝试加载类。由于MyClass和MyClassInjector在同一类路径下,运行MyClassInjector时系统类加载器已提前加载MyClass,因此自定义类加载器调用loadClass("MyClass")时,父类加载器直接返回已加载的类,不会触发重写的findClass方法。

同时你的ASM代码存在逻辑错误:在cr.accept后直接调用cv.visitMethod重新定义myMethod,这会完全覆盖原方法代码,而非在原方法中注入逻辑。正确做法是通过ClassVisitor拦截原方法,在原有代码前后插入新逻辑。

解决方案

1. 重写loadClass打破双亲委派(解决类加载器问题)

自定义类加载器时,重写loadClass方法,对目标类MyClass跳过双亲委派,直接用自定义逻辑加载:

ClassLoader cl = new ClassLoader() {
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 检查类是否已加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                if (name.equals("MyClass")) {
                    // 加载修改后的MyClass字节码
                    c = defineClass(name, modifiedClass, 0, modifiedClass.length);
                } else {
                    // 其他类仍走双亲委派流程
                    c = super.loadClass(name, resolve);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
};

2. 修正ASM注入逻辑(解决方法覆盖问题)

修改MyClassVisitor,拦截myMethod并在原方法执行前后插入注入代码:

class MyClassVisitor extends ClassVisitor {
    public MyClassVisitor(ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        // 仅处理目标方法myMethod
        if ("myMethod".equals(name) && "()V".equals(desc)) {
            return new MyMethodVisitor(mv);
        }
        return mv;
    }
}

class MyMethodVisitor extends MethodVisitor {
    public MyMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitCode() {
        // 在原方法代码执行前插入调用newMethod的逻辑
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "MyClass", "newMethod", "()V", false);
        super.visitCode();
    }
}

3. 完整修正后的MyClassInjector.java

import org.objectweb.asm.*;

public class MyClassInjector {
    public static void main(String[] args) throws Exception {
        // 读取原MyClass字节码
        ClassReader cr = new ClassReader("MyClass");
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        MyClassVisitor cv = new MyClassVisitor(cw);
        cr.accept(cv, 0);

        // 定义注入的newMethod方法
        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(2, 0);
        mv2.visitEnd();

        // 获取修改后的类字节码
        byte[] modifiedClass = cw.toByteArray();

        // 自定义类加载器
        ClassLoader cl = new ClassLoader() {
            @Override
            protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
                synchronized (getClassLoadingLock(name)) {
                    Class<?> c = findLoadedClass(name);
                    if (c == null) {
                        if (name.equals("MyClass")) {
                            c = defineClass(name, modifiedClass, 0, modifiedClass.length);
                        } else {
                            c = super.loadClass(name, resolve);
                        }
                    }
                    if (resolve) {
                        resolveClass(c);
                    }
                    return c;
                }
            }
        };

        // 加载修改后的类并调用方法
        Class<?> myClass = cl.loadClass("MyClass");
        Object myObject = myClass.newInstance();
        myClass.getMethod("myMethod").invoke(myObject);
    }
}

class MyClassVisitor extends ClassVisitor {
    public MyClassVisitor(ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if ("myMethod".equals(name) && "()V".equals(desc)) {
            return new MyMethodVisitor(mv);
        }
        return mv;
    }
}

class MyMethodVisitor extends MethodVisitor {
    public MyMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitCode() {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "MyClass", "newMethod", "()V", false);
        super.visitCode();
    }
}
运行结果

执行后会依次输出:

Injected code
Hello, world!

内容的提问来源于stack exchange,提问作者rysv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:40:01