如何使用ASM修改常量池?
How To Modify Constant Pool Using ASM?
我已经了解如何使用来自 的 ASM 在运行时操作 class。
但是我还有一个关于如何修改常量池的问题。下面是我要修改的示例 java 程序
主 jar 文件:
public class test {
private static final String a = "Hello World";
private static final String b = "ASM is awasome";
public static void main(String[] args) {
int x = 10;
int y = 25;
int z = x * y;
System.out.println(a);
System.out.println(z);
System.out.println(b);
}
}
我想将变量 a
从 "Hello World"
修改为 "Multiply Of x*y is: "
我的经纪人Class
import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;
public class ExampleAgent implements ClassFileTransformer {
private static final String TRANSFORM_CLASS = "src/test";
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);
//Code to modify the value of a
return mv;
}
}, 0);
return cw.toByteArray();
}
}
控制台上的结果Window应该是
Multiply Of x*y is:
250
ASM is awasome
如 所述,使用 ASM 你根本不需要处理常量池;你只处理常量的实际使用。
关于你的例子,你必须意识到一个事实,像
这样的声明
… final String a = "Hello World";
是一个 compile-time 常量。因此,在描述该值的字段中将有一个 ConstantValue
attribute,但是对常量的每次读取访问都将在 compile-time 处被替换。
所以要有效地“改变a
”,你必须替换常量值的每个实际使用,但你必须意识到你无法识别值的出现是否真正源于对 a
的字段访问或只是同一常量值的另一种使用。
您的示例代码中的用法很容易更改;您只需查找 ldc
instructions。替换字段本身的常量值声明也很容易。更多涉及的是替换注释中的常量,但真正困难的是将常量的使用替换为 switch
语句的 case
标签,因为如果您只是替换常量,程序逻辑就会中断设想。您将不得不重写更多代码以使用不同的常量切换字符串。
仅关注较简单的任务,转换代码变为:
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 FieldVisitor visitField(int access, String name, String desc,
String signature, Object cst) {
if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
return super.visitField(access, name, desc, signature, cst);
}
@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 visitLdcInsn(Object cst) {
if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
super.visitLdcInsn(cst);
}
};
}
return mv;
}
}, 0);
return cw.toByteArray();
}
我已经了解如何使用来自
但是我还有一个关于如何修改常量池的问题。下面是我要修改的示例 java 程序
主 jar 文件:
public class test {
private static final String a = "Hello World";
private static final String b = "ASM is awasome";
public static void main(String[] args) {
int x = 10;
int y = 25;
int z = x * y;
System.out.println(a);
System.out.println(z);
System.out.println(b);
}
}
我想将变量 a
从 "Hello World"
修改为 "Multiply Of x*y is: "
我的经纪人Class
import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;
public class ExampleAgent implements ClassFileTransformer {
private static final String TRANSFORM_CLASS = "src/test";
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);
//Code to modify the value of a
return mv;
}
}, 0);
return cw.toByteArray();
}
}
控制台上的结果Window应该是
Multiply Of x*y is:
250
ASM is awasome
如
关于你的例子,你必须意识到一个事实,像
这样的声明
… final String a = "Hello World";
是一个 compile-time 常量。因此,在描述该值的字段中将有一个 ConstantValue
attribute,但是对常量的每次读取访问都将在 compile-time 处被替换。
所以要有效地“改变a
”,你必须替换常量值的每个实际使用,但你必须意识到你无法识别值的出现是否真正源于对 a
的字段访问或只是同一常量值的另一种使用。
您的示例代码中的用法很容易更改;您只需查找 ldc
instructions。替换字段本身的常量值声明也很容易。更多涉及的是替换注释中的常量,但真正困难的是将常量的使用替换为 switch
语句的 case
标签,因为如果您只是替换常量,程序逻辑就会中断设想。您将不得不重写更多代码以使用不同的常量切换字符串。
仅关注较简单的任务,转换代码变为:
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 FieldVisitor visitField(int access, String name, String desc,
String signature, Object cst) {
if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
return super.visitField(access, name, desc, signature, cst);
}
@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 visitLdcInsn(Object cst) {
if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
super.visitLdcInsn(cst);
}
};
}
return mv;
}
}, 0);
return cw.toByteArray();
}