我需要使用 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 扩展程序 (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 扩展程序 (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,我该怎么做?
编辑:我想要更改的内容的字节码说: 最不发达国家“你好!” (java.lang.String)
我想把它改成:
ldc“再见!” (java.lang.String)
最佳答案
如果没有基本了解 Java 字节码的工作原理,您不应该尝试进行字节码转换。有关它的信息的主要来源是 The Java® Virtual Machine Specification .对于初学者,您可以阅读 §3 “Compiling for the Java Virtual Machine”学习,某些语言如何构造映射到 byte code instructions .
对于您想要的转换类型,您必须了解的最重要的一点是,在字节代码级别上没有局部变量(您从 Java 语言中了解到的方式)。堆栈帧中有一定数量的局部变量的空间,由数字索引寻址。您在 ASM API 中发现的方法 visitLocalVariable
只有在类文件编译时包含调试信息时才会被调用。
如果它被调用,它会告诉您有关局部变量 name
以及它映射到哪个 index
的信息。 Label
参数告诉您变量的范围,因为它超出了范围,索引可能用于不同的变量。如果您使用这种了解变量特定索引的方式编写代码,则必须记住,此代码仅适用于包含调试提示的类。
因此在字节码级别上,没有变量 var4
的正式声明,而只是将字符串 "hello"
赋值给一个特定的变量,隐含地如果该变量不存在,则创建该变量。该赋值在字节码级别上实现为两条指令,ldc "hello"
,后跟astore n
,其中n
是局部变量。实际上,ldc
只包含一个指向保存字符串的常量池的索引,但 ASM 会处理这个并将调用 visitLdcInsn("hello")
遇到这条指令时。
所以你可能会搜索两个指令序列,这意味着你必须首先找到正确的索引(例如,使用调试信息,如果存在的话)。或者,如果字符串 "hello"
预计只出现在这个单一的分配中,等待 visitLdcInsn("hello")
的出现并将其替换为不同的常量字符串是最简单的替换形式。
关于java - 使用 ASM 修改局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34834404/