使用ASM修改局部变量

时间:2016-01-17 02:17:50

标签: java java-bytecode-asm

我需要使用ASM在方法中查找局部变量,即:

String var4 = "hello!";

我创建了三个班级。进行转换的一个,一个扩展ClassVisitor,另一个扩展MethodVisitor,如下所示:

Transformer入口点(Transformationer.java)

package RainbowBansTransAgent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;

public class Transformationer implements ClassFileTransformer {

    public byte[] transform(String arg1, byte[] arg2){
        ClassReader cr = new ClassReader(arg2);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
        cr.accept(cw, 0);
        return cw.toByteArray();
    }

    @Override
    public byte[] transform(ClassLoader arg0, String className, Class<?> arg2,
            ProtectionDomain arg3, byte[] arg4)
            throws IllegalClassFormatException {
        BooleanKeys.transformer_loaded = true;
        byte[] b = null;
        String realName = className.replaceAll("/", ".");
        if(realName.equals("joebkt.PlayerList")){
            if(BooleanKeys.returned_bytes){
                return null;
            }else{
            BooleanKeys.found_class = true;
            b =  transform(realName, arg4);
            if(b !=null){
                BooleanKeys.returned_bytes = true;
            }
            }
        }
        else System.out.println("Class name " + realName + " is not what we're looking for!");
        return b;
    }

}

ClassVisior Extender(RBClassVisitor.java)

package RainbowBansTransAgent;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class RBClassVisitor extends ClassVisitor{

    public RBClassVisitor() {
        super(Opcodes.ASM5);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
        String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature,
                exceptions);
        return new RBMethodVisitor(mv);
    }

}

MethodVisitor Extender(RBMethodVisitor.java)

package RainbowBansTransAgent;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class RBMethodVisitor extends MethodVisitor {

    MethodVisitor mv;

    public RBMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
        this.mv = mv;
    }

    public void visitLineNumber(int line, Label start){
        if(line == 409){
            mv.visitCode();
        }
    }

    public void visitCode(){

    }

}

如您所见,我的visitCode()方法为空。我知道这是应该发生字节码操作的方法。

我看到MethodVisitor有一个

mv.visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index);   

方法,但我不知道如何正确使用Label类。

我的变换器将读取文件,并将变量更改为文件的内容。使用ASM,我该怎么做?

编辑:我想要更改的字节码是: ldc“你好!” (java.lang.String中)

我希望将其更改为:

ldc“再见!” (java.lang.String中)

1 个答案:

答案 0 :(得分:3)

如果不基本了解Java字节码的工作原理,就不应该尝试进行字节码转换。有关它的信息的主要来源是The Java® Virtual Machine Specification。对于入门者,您可以阅读§3 “Compiling for the Java Virtual Machine”了解某些语言结构如何映射到byte code instructions

最重要的是,您必须了解您的预期转换类型,即在字节码级别上没有局部变量(您从Java语言中了解它的方式)。堆栈帧中存在一定数量的局部变量,这可以通过数字索引来解决。只有在编译了包含调试信息的类文件时,才会调用ASM API中发现的方法visitLocalVariable

如果它被调用,它会告诉你一个局部变量name以及它映射到的indexLabel参数告诉您变量的范围在其范围之外,索引可能用于不同的变量。如果使用这种学习变量特定索引的方式编写代码,则必须记住,此代码仅适用于包含调试提示的类。

所以在字节代码级别上,没有变量var4的正式声明,而只是将字符串"hello"赋值给特定变量,如果不变量则隐式创建该变量存在。赋值在字节代码级别上实现为两个指令ldc "hello",后跟astore n,其中n是局部变量的索引。实际上,ldc仅包含保存字符串的常量池的索引,但ASM会处理此问题,并在遇到此指令时调用visitLdcInsn("hello")

因此,您可以搜索两个指令序列,这意味着您必须首先找到正确的索引(例如,使用调试信息,如果存在)。或者,如果字符串"hello"仅出现在此单个赋值中,则等待visitLdcInsn("hello")的出现并将其替换为不同的常量字符串是最简单的替换形式。

相关问题