我需要使用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中)
答案 0 :(得分:3)
如果不基本了解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")
的出现并将其替换为不同的常量字符串是最简单的替换形式。