使用ASM库重写Java字节码中的局部变量名

使用ASM库重写Java字节码中的局部变量名,java,bytecode,java-bytecode-asm,Java,Bytecode,Java Bytecode Asm,我使用ASM Java字节码库阅读了一个类的方法及其内容。这是为了输出类中任何方法的局部变量名称: ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass.class"))); final ClassNode classNode = new ClassNode(); reader.accept(classNode, 0); for (final MethodNode mn : (List<M

我使用ASM Java字节码库阅读了一个类的方法及其内容。这是为了输出类中任何方法的局部变量名称:

ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass.class")));

final ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);

for (final MethodNode mn : (List<MethodNode>)classNode.methods) {
    for (LocalVariableNode n : (List<LocalVariableNode>)mn.localVariables) {
        System.out.println(n.name);
    }
}

因此,逻辑上,输出是,
this
c
d
现在,我需要将这个编译过的类复制到一个新文件中,但是将
方法的参数(局部变量)更改为不同的名称(
e
f
)。我该怎么做呢?
我对MethodVisitor之类的东西几乎没有经验。

您需要编写一个适配器(ClassVisitor的子类
),并将其与
阅读器链接起来。比如说,

ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer, 
    new PrintWriter(System.getProperty("java.io.tmpdir") 
            + File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();
使用它,您将获得
字节[]
,您可以将其保存到文件中,也可以使用

TraceClassVisitor
只是另一个
ClassVisitor
,我将它链接起来,以便在临时目录中获得一个可读的日志。)

适配器可能如下所示。要覆盖的方法是:


你为什么要这样做?局部变量名的唯一用途是调试和反射。@这里提供的示例只是一个示例。这样做的最终目标是在没有名称的方法中为不同的局部变量指定名称。例如,一个方法可以包含9个不同的局部变量,但都具有相同的“name”属性(这在字节码中是可能的),目标是为每个变量分配不同的名称(var1,var2,…)。最终,类本身将使用不同的局部变量名进行反编译。为什么不在反编译过程中添加名称呢?那就是我要做的。并非所有的反编译器都会首先使用LocalVariableTable信息。如果您只想帮助一个在错误的本地变量表上出错的反编译器,最简单的解决方案是完全删除此调试信息。然后,反编译器被迫为局部变量生成名称,并且不会尝试使用无意义的名称。
ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer, 
    new PrintWriter(System.getProperty("java.io.tmpdir") 
            + File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();
public class ClassAdapter extends ClassVisitor {
    public ClassAdapter(ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

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

public class MethodAdapter extends MethodVisitor {
    public MethodAdapter(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitLocalVariable(String name, String desc, String signature,
            Label start, Label end, int index) {
        // Put your rename logic here
        if (name.equals("c"))
            name = "e";
        else if (name.equals("d"))
            name = "f";
        super.visitLocalVariable(name, desc, signature, start, end, index);
    }
}