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

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

我几天前开始学习Java Agent。但文档不是很好,像我这样的初学者正在努力理解基础知识。我创建了一个基本的乘数类,并使用eclipse将其导出到runnable 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的值。我试着像这样创建Agent类

代理:

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将x的值从代理类设置为乘数类?结果应该是125。

java bytecode java-bytecode-asm javaagents
3个回答
0
投票

第一件事,你的经纪人必须做的是registering a ClassFileTransformer。首先,类文件转换器应该在其transform方法中执行,检查参数以查明当前请求是否与我们感兴趣的类有关,如果不是则立即返回。

如果我们在要转换的类中,我们必须处理传入的类文件字节以返回一个新的字节数组。您可以使用ASM的ClassReader处理传入的字节并将其链接到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调用返回的super.visitMethod传递给我们的MethodVisitor的构造函数,我们启用了一个链接,默认情况下会重现原始类;我们没有覆盖的所有方法将委托给指定的ClassWriter / MethodVisitor再现遇到的工件。与the tutorial about ASM’s event model比较。

上面的例子通过将ClassReader实例传递给ClassWriter的构造函数来实现优化。这样,只需进行一些小改动就可以提高仪表的工具效率,就像我们在这里做的那样。

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


1
投票

你已经在main方法中声明了x。所以它的范围是本地的。这就是为什么你不能从任何其他类改变x的值。


0
投票

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

特别是你需要覆盖visitIntInsn方法,所以当你在BIPUSH中看到第一个main指令时,你可以用你选择的值替换值10。

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

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