如何使用 ASM 更改静态变量值?

How to change static variable value using ASM?

几天前我开始学习Java Agent。但是文档不是很好,像我这样的初学者很难理解基础知识。我创建了一个基本乘法器 class 并使用 eclipse 将其导出到可运行的 jar。这是代码片段。

主 jar 文件:

public class Multiplier {

    public static void main(String[] args) {
        int x = 10;
            int y = 25;
            int z = x * y;

            System.out.println("Multiply of x*y = " + z);
    }
}

Bytecode for above class

现在我想从代理操纵 x 的值。我尝试像这样创建代理 class

代理人:

package myagent;

import org.objectweb.asm.*;
import java.lang.instrument.*;

public class Agent {

    public static void premain(final String agentArg, final Instrumentation inst) {
        System.out.println("Agent Started");        
        int x_modified = 5;

        //Now How to push the new value (x_modified) to the multiplier class? 
        //I know I have to use ASM but can't figure it out how to do it.
        //Result should be 125

    }

}

我的问题

如何使用 ASM 将代理 class 的 x 值设置为乘数 class? 结果应该是 125.

您已在 main 方法中声明了 x。所以它的范围是局部的。这就是为什么你不能从任何其他 class.

更改 x 的值

要使用 ASM,您需要在传递给 ClassReader 的自定义 ClassWriter 中使用自定义 CodeWriter。 http://asm.ow2.org/doc/tutorial.html 这将允许您访问每个方法的代码中的所有说明。

特别是您需要覆盖 visitIntInsn 方法,这样当您在 main 中看到第一条 BIPUSH 指令时,您可以将值 10 替换为您选择的任何值.

ClassWriter 的输出是一个字节[],您的 Instrumentation 将 return 而不是原始代码,此时 x 将是您在代码中创建的任何值。

您的代理人必须做的第一件事是 registering a ClassFileTransformer。 class 文件转换器应该在其 transform 方法中做的第一件事是检查参数以查明当前请求是否与我们感兴趣的 class 相关,以 return如果没有就立即。

如果我们在 class 处想要转换,我们必须将传入的 class 文件字节处理为 return 一个新的字节数组。您可以使用 ASM 的 ClassReader to process to incoming bytes and chain it to a ClassWriter 来生成一个新数组:

import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;

public class ExampleAgent implements ClassFileTransformer {
    private static final String TRANSFORM_CLASS = "Multiplier";
    private static final String TRANSFORM_METHOD_NAME = "main";
    private static final String TRANSFORM_METHOD_DESC = "([Ljava/lang/String;)V";

    public static void premain(String arg, Instrumentation instrumentation) {
        instrumentation.addTransformer(new ExampleAgent());
    }

    public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                            ProtectionDomain pd, byte[] classfileBuffer) {
        if(!TRANSFORM_CLASS.equals(className)) return null;

        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, 0);
        cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc,
                                             String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(
                                       access, name, desc, signature, exceptions);
                if(name.equals(TRANSFORM_METHOD_NAME)
                && desc.equals(TRANSFORM_METHOD_DESC)) {
                    return new MethodVisitor(Opcodes.ASM5, mv) {
                        @Override
                        public void visitIntInsn(int opcode, int operand) {
                            if(opcode == Opcodes.BIPUSH && operand == 10) operand = 5;
                            super.visitIntInsn(opcode, operand);
                        }
                    };
                }
                return mv;
            }
        }, 0);
        return cw.toByteArray();
    }
}

请注意,通过将 ClassWriter 传递给我们自定义的 ClassVisitor 的构造函数并将 MethodVisitor return 由 super.visitMethod 调用传递给我们的MethodVisitor 的构造函数,我们启用默认情况下复制原始 class 的链接;我们没有覆盖的所有方法都将委托给指定的 ClassWriter/MethodVisitor 来再现遇到的工件。比较 the tutorial about ASM’s event model.

上面的示例通过将 ClassReader 实例传递给 ClassWriter 的构造函数来启用优化。这提高了检测 class 的效率,就像我们在这里所做的那样,只进行小的更改。

关键部分是当我们处于“热”方法时将 visitMethod 覆盖到 return 我们的自定义 MethodVisitor 并覆盖 visitIntInsn 以更改所需的指令。注意这些方法如何在不改变行为时委托给 super 调用,就像我们没有覆盖的方法一样。