运行修改后的.class文件时出现java.lang.VerifyError

时间:2014-10-20 12:05:28

标签: java bytecode

我正在研究ASM框架一段时间,我成功编辑了.class文件。我处于需要用另一个方法调用替换方法调用的情况。我通过覆盖super.visitMethodInsn()

实现了这一目标

我在这里发布编码:

未修改的课程:UserSampleClass.java

package checking;

public class UserSampleClass {

public static void main(String[] args) {
    UserSampleClass usc= new UserSampleClass();
    usc.display();
}

public String display() {

    System.out.println("Hello Method: "+hello());
    System.out.println("Bye Method: "+bye());
    System.out.println("Hello Method: "+hello());
    return "i'm display";
}

private String bye() {

    return "Bye";
}

private String hello() {

    return "Hello";
}

}

上述程序的输出将是:

Hello Method: Hello
Bye Method: Bye
Hello Method: Hello

现在我修改了原始类文件并将三个方法调用(即hello(),bye(),hello())更改为其他3个方法调用,如下所述:

替换第一个hello方法:replaceFirstHelloInvoke.java

package chekingDepend;
public class replaceFirstHelloInvoke {


public  replaceFirstHelloInvoke()
{}

public static String replaceFirstHello()    {
    return "one";
}
}

替换再见方法:replaceByeInvoke.java

package chekingDepend;
public class replaceByeInvoke {

public  replaceByeInvoke()
{}
public static String replaceBye()   {
    return "two";
}

}

替换第二个问候:replaceSecondHelloInvoke.java

package chekingDepend;
public class replaceSecondHelloInvoke {

public replaceSecondHelloInvoke()
{}
public static String replaceSecondHello()   {
    return "three";
}
}

我已将所有这些方法创建为static,因为它们可以通过类名直接引用。

我能够通过ASM成功生成修改后的类。

现在的问题是修改后的类没有运行。当我试图运行它时出现此错误:

Exception in thread "main" java.lang.VerifyError: (class: checking/UserSampleClass, method: display signature: ()Ljava/lang/String;) Incompatible object argument for function call Could not find the main class: UserSampleClass. Program will exit.

我也检查了包的依赖性。一切似乎都是正确的。

我还使用了ASM提供的字节码比较工具来检查字节码级别的修改。

所有变化似乎都是正确的。

我将在此处发布字节代码:

未修改的字节代码:UserSampleClass.class

// class version 49.0 (49)
// access flags 0x21
public class checking/UserSampleClass {

  // compiled from: UserSampleClass.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    NEW checking/UserSampleClass
    DUP
    INVOKESPECIAL checking/UserSampleClass.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 7 L1
    ALOAD 1
    INVOKEVIRTUAL checking/UserSampleClass.display ()Ljava/lang/String;
    POP
   L2
    LINENUMBER 8 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE usc Lchecking/UserSampleClass; L1 L3 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  public display()Ljava/lang/String;
   L0
    LINENUMBER 12 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    LDC "hello:"
    INVOKESPECIAL java/lang/StringBuilder.<init> (Ljava/lang/String;)V
    ALOAD 0
    INVOKESPECIAL checking/UserSampleClass.hello ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC " "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "bye:"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKESPECIAL checking/UserSampleClass.bye ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "    "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "hello:"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKESPECIAL checking/UserSampleClass.hello ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 13 L1
    LDC "i'm display"
    ARETURN
   L2
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L2 0
    MAXSTACK = 4
    MAXLOCALS = 1

  // access flags 0x2
  private bye()Ljava/lang/String;
   L0
    LINENUMBER 18 L0
    LDC "Bye"
    ARETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x2
  private hello()Ljava/lang/String;
   L0
    LINENUMBER 23 L0
    LDC "Hello"
    ARETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

修改后的字节码:UserSampleClass.class

// class version 49.0 (49)
// access flags 0x21
public class checking/UserSampleClass {

  // compiled from: UserSampleClass.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    NEW checking/UserSampleClass
    DUP
    INVOKESPECIAL checking/UserSampleClass.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 7 L1
    ALOAD 1
    INVOKEVIRTUAL checking/UserSampleClass.display ()Ljava/lang/String;
    POP
   L2
    LINENUMBER 8 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE usc Lchecking/UserSampleClass; L1 L3 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  public display()Ljava/lang/String;
   L0
    LINENUMBER 12 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    LDC "hello:"
    INVOKESPECIAL java/lang/StringBuilder.<init> (Ljava/lang/String;)V
    ALOAD 0
    INVOKESTATIC chekingDepend/replaceFirstHelloInvoke.replaceFirstHello ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC " "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "bye:"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKESTATIC chekingDepend/replaceByeInvoke.replaceBye ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "    "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "hello:"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKESTATIC chekingDepend/replaceSecondHelloInvoke.replaceSecondHello ()Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 13 L1
    LDC "i'm display"
    ARETURN
   L2
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L2 0
    MAXSTACK = 6
    MAXLOCALS = 1

  // access flags 0x2
  private bye()Ljava/lang/String;
   L0
    LINENUMBER 18 L0
    LDC "Bye"
    ARETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x2
  private hello()Ljava/lang/String;
   L0
    LINENUMBER 23 L0
    LDC "Hello"
    ARETURN
   L1
    LOCALVARIABLE this Lchecking/UserSampleClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

重载的visitMethodInsn()方法

@Override
     public void visitMethodInsn(int opcode, String owner, String name, String desc) {

         MetaData executionUnitMetaData = new MetaData();

         String[] methodInvocationToReplace =  {"hello", "bye", "hello"};

         String[] replacableClassName = {"chekingDepend.replaceFirstHelloInvoke","chekingDepend.replaceByeInvoke","chekingDepend.replaceSecondHelloInvoke"};

         String[] replacableMethodName = {"replaceFirstHello","replaceBye","replaceSecondHello"};


         if(i<methodInvocationToReplace.length && name.equals(methodInvocationToReplace[i]))    {

            super.visitMethodInsn(Opcodes.INVOKESTATIC, replacableClassName[i].replace('.', '/'), replacableMethodName[i], desc); // replacableMethodDesc[i]);

            i++;

         }else      {

            super.visitMethodInsn(opcode, owner, name, desc);
        }   


      }

    }

这里我将文件从执行前复制到具有相同目录结构的另一个目录,以便不修改原始.class文件。修改后的.class文件是在具有相同包结构的seprate文件夹中创建的。

但我很清楚为什么会得到lava.lang.VerifyError

请帮我解决这个问题。

我使用Java 1.6和ASM 4.0

如果程序运行正常,我的输出应为:

Hello Method: one
Bye Method: two
Hello Method: three

1 个答案:

答案 0 :(得分:1)

当您的原始代码调用私有实例方法时,它会这样:

  ALOAD 0      # push `this`
  INVOKESPECIAL checking/UserSampleClass.hello ()Ljava/lang/String; # call the method.

...并且INVOKESPECIAL将弹出第一个操作数作为调用目标。

在修改过的代码中,我们改为:

  ALOAD 0      # push `this`
  INVOKESTATIC chekingDepend/replaceFirstHelloInvoke.replaceFirstHello ()Ljava/lang/String;

...但INVOKESTATIC不会弹出opstack元素。

验证者(正确地)认为这是错误的。

比较相应字节码指令的操作数堆栈要求: