How to change static variable value using ASM?

女生的网名这么多〃 提交于 2019-12-20 05:53:37

问题


I started learning Java Agent few days ago. But documentation is not very good and beginners like me struggling to understand the basics. I created a basic multiplier class and export it to runnable jar using eclipse. Here is the code snippet.

Main jar file:

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

Now I want to manipulate the value of x from an agent. I tried to create the Agent class like this

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

    }

}

My Question

How do I set the value of x from agent class to multiplier class using ASM? Result should be 125.


回答1:


The first thing, your agent has to do, is registering a ClassFileTransformer. The first thing, the class file transformer should do in its transform method, is checking the arguments to find out whether the current request is about the class we’re interested in, to return immediately if not.

If we are at the class we want to transform, we have to process the incoming class file bytes to return a new byte array. You can use ASM’s ClassReader to process to incoming bytes and chain it to a ClassWriter to produce a new array:

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();
    }
}

Note that by passing the ClassWriter to our custom ClassVisitor’s constructor and passing the MethodVisitor returned by the super.visitMethod invocation to our MethodVisitor’s constructor, we enable a chaining that reproduces the original class by default; all methods we’re not overriding will delegate to the specified ClassWriter/MethodVisitor reproducing the encountered artifact. Compare with the tutorial about ASM’s event model.

The example above enables an optimization by also passing the ClassReader instance to the ClassWriter’s constructor. This improves the efficiency of instrumenting a class when only making small changes, like we do here.

The crucial part is overriding visitMethod to return our custom MethodVisitor when we are at the “hot” method and overriding visitIntInsn to change the desired instruction. Note how these methods delegate to the super calls when not altering the behavior, just like the methods we didn’t override.




回答2:


You have declared x inside main method. So it's scope is local. That's why you can't change the value of x from any other class.




回答3:


To use ASM you need a custom CodeWriter in a custom ClassWriter which you pass to a ClassReader. http://asm.ow2.org/doc/tutorial.html This will allow you to visit all instructions in the code for each method.

In particular you will need to override the visitIntInsn method so when you see the first BIPUSH instruction in main you can replace the value 10, with what ever value you chose.

The output of ClassWriter is a byte[] which your Instrumentation will return instead of the original code at which point x will be whatever value you made it in the code.



来源:https://stackoverflow.com/questions/49334673/how-to-change-static-variable-value-using-asm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!