Java ASM 编辑已由 JVM 加载的类/方法

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

我目前正在尝试在 JVM 加载 Java 类之后编辑它的字节码。

我使用 Java 8 和 ASM 5.0.3。 我无法更改命令行或 JVM 参数。

这是我正在尝试做的一个最小示例:

import org.objectweb.asm.*; // Is in a library class ExampleObject { public void exampleMethod(Object o) { System.out.println("Body " + o); } } public class Example { // Comes from the same library public static final ExampleObject exampleObject = new ExampleObject(); public static void main(String[] args) { exampleObject.exampleMethod(1); // Output: // Body 1 injectAtHead(); exampleObject.exampleMethod(2); // Output: // Head 2 // Body 2 } public static void injectAtHead() { // Inject methodToInject at the head of exampleObject#exampleMethod } public static void methodToInject(Object o) { System.out.println("Head " + o); } }
经过大量研究,我发现了很多关于使用 ASM 动态修改字节码的主题。
问题是他们都讨论在 JVM 加载类之前修改它的字节码。
所以,我不知道如何才能做到这一点,甚至不知道是否有可能。

java java-8 java-bytecode-asm
1个回答
0
投票

这个评论几乎回答了问题。

经过几次适应,最初问题的解决方案是:

import net.bytebuddy.agent.ByteBuddyAgent; import org.objectweb.asm.*; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; import static net.bytebuddy.jar.asm.Opcodes.*; public class Example { // Also comes from a library public static final ExampleObject exampleObject = new ExampleObject(); public static void main(String[] args) { exampleObject.exampleMethod(1); // Output: // Body 1 injectAtHead(); exampleObject.exampleMethod(2); // Output: // Head 2 // Body 2 } public static void injectAtHead() { try { ByteBuddyAgent.install(); Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation(); instrumentation.addTransformer(new SimpleClassFileTransformer(), true); instrumentation.retransformClasses(ExampleObject.class); } catch (Exception e) { e.printStackTrace(); } } public static void methodToInject(Object o) { System.out.println("Head " + o); } } class ModifierMethodWriter extends MethodVisitor { private String methodName; public ModifierMethodWriter(int api, MethodVisitor mv, String methodName) { super(api, mv); this.methodName = methodName; } // This is the point we insert the code. Note that the instructions are // added right after // the visitCode method of the super class. This ordering is very // important. @Override public void visitCode() { // invoke methodToInject super.visitCode(); super.visitVarInsn(ALOAD, 1); super.visitMethodInsn(INVOKESTATIC, "Example", "methodToInject", "(Ljava/lang/Object;)V", false); } } // Our class modifier class visitor. It delegate all calls to the super // class // Only makes sure that it returns our MethodVisitor for every method class ModifierClassWriter extends ClassVisitor { private int api; public ModifierClassWriter(int api, ClassWriter cv) { super(api, cv); this.api = api; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (!name.equals("exampleMethod")){ return super.visitMethod(access, name, desc, signature, exceptions); } else { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); ModifierMethodWriter mvw = new ModifierMethodWriter(api, mv, name); return mvw; } } } class SimpleClassFileTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (!className.equals("ExampleObject")) { return classfileBuffer; } else { ClassReader classReader = new ClassReader(classfileBuffer); final ClassWriter cw = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); // Wrap the ClassWriter with our custom ClassVisitor ModifierClassWriter mcw = new ModifierClassWriter(Opcodes.ASM5, cw); classReader.accept(mcw, 0); byte[] byteArray = cw.toByteArray(); return byteArray; } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.