如何使用ASM修改常量池?

LEO*_*EON 1 java java-bytecode-asm jvm-bytecode

我已经从这篇文章中了解了如何在运行时使用ASM操作类。

但是我还有一个关于如何修改常量池的问题。以下是我要修改的示例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);

    }

}
Run Code Online (Sandbox Code Playgroud)

我想将变量a从修改"Hello World""Multiply Of x*y is: "

我的特工班

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

    }
}
Run Code Online (Sandbox Code Playgroud)

控制台窗口上的结果应为

Multiply Of x*y is: 
250
ASM is awasome
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 5

评论中所述,使用ASM,您根本不需要处理常量池;您只处理常量的实际使用。

关于您的示例,您必须意识到这样的事实,像这样的声明
… final String a = "Hello World";是编译时常量。因此,将在字段中描述该值的ConstantValue属性,但是对常量的每次读取访问都将在编译时被替换。

因此,要a有效地“更改”,您必须替换每次实际使用的常数值,但是您必须意识到,您无法识别出该值的出现是否确实是由于对字段的访问a或仅是该字段的另一种用法相同的常数值。

样例代码中的用法很容易更改;您只需要查找ldc说明。替换字段本身的常量值声明也很容易。涉及更多的是替换注释中的常量,但是要替换使用常量作为语句的case标签确实很困难switch,因为如果您仅在这种情况下替换常量,程序逻辑就会中断。您将必须重写更多代码,才能使字符串切换使用其他常数。

仅关注简单的任务,转换代码变为:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • 同样,这是一个完美的解决方案,您是一位很棒的老师。您不仅提供了解决方案,还指出了可能发生的其他问题。谢谢您的好回答。希望我将来有幸能向您学习。 (2认同)