问题
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