【问题标题】:Java bytecode modification using ASM throws ClassFormatError: Invalid length XXX in LocalVariableTable使用 ASM 修改 Java 字节码抛出 ClassFormatError: Invalid length XXX in LocalVariableTable
【发布时间】:2020-09-29 03:39:07
【问题描述】:

我正在使用 ASM(也使用树和实用程序)并且遇到了一个奇怪的异常

Exception in thread "main" java.lang.ClassFormatError: Invalid length 65526 in LocalVariableTable in class file 

我正在尝试编辑 .class 文件的字节码,以生成新的。我有一个 for 循环,里面有一些 if 分支,我尝试修改它。我在两个 for 循环的代码下方发布了初始循环和所需生成的代码。

首字母

int[] ar = new int[]{1,2,3,4,5};
int[] ar2 = new int[]{8,9,9,94,3,2};
MyClass myar = new MyClass(ar);
MyClass myar2 = new MyClass(ar2);
int sum=0;
for(int i=0; i<ar.length; i++)
  if(ar[i]>3) {
    if(ar2[i]>8) {
      sum+=(ar[i]+ar2[i]);
    }
  }
}

希望

int[] ar = new int[]{1,2,3,4,5};
int[] ar2 = new int[]{8,9,9,94,3,2};
MyClass myar = new MyClass(ar);
MyClass myar2 = new MyClass(ar2);
int sum=0;
for(int i=0; i<ar.length; i++)
  int[] temp = callAFunctionToDoSthing(myar, myar2);
  if(temp[0]>3) {
    if(temp[1]>8) {
      sum+=(temp[0]+temp[1]);
    }else i+=1;
  }else i+=2;
}

为了做到这一点,我使用 ASM 树 ListIterator 来检测 for 循环(我通过检查某些命令是否一个接一个地出现来做到这一点)。我使用 intellj 的 ASM 字节码扩展来比较初始字节码和所需字节码。然后,使用 ASM 树删除并在字节码列表中添加我想要的指令。 (我尝试过 ASM5 和 ASM8) 问题是,当我运行生成的 .class 文件时,在这个操作之后,我得到了上面的异常。 关键是我没有手动编辑 LocalVariableTable,也不知道如何编辑它。如果我在所需代码中运行 javap -v 命令,我什至看不到 LocalVariableTable,而如果我在生成的 .class 文件中运行 javap -v,我会看到它。

我尝试了另一种修改,以发现我对 ASM 树的想法可以在没有 if 和唯一数组的更简单的 for 循环中工作,并且在那里一切正常。

另外,我尝试过使用 SKIP_DEBUG,但我看不到 Label 和 LineNumber -Nodes,在我的情况下,我需要删除一些 LabelNode 和 LineNumberNode 指令。

编辑:

我正在举一个简单的例子来说明我进行转换的方式。我使用初始和所需 ASM 字节码的差异检查器,它会产生如下图所示的内容 所以为了操作的代码是

IntInsnNode bp7 = (IntInsnNode) aload17.getNext();
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
InsnList l4 = new InsnList();
LabelNode l45Ins = new LabelNode();
l4.add(new JumpInsnNode(Opcodes.IF_ICMPGT, l45Ins));
LabelNode l46Ins = new LabelNode();
l4.add(l46Ins);
l4.add(new LineNumberNode(178, l46Ins));
l4.add(new VarInsnNode(Opcodes.ALOAD, 20));
l4.add(new InsnNode(Opcodes.ICONST_0));
instructions.insert(bp7, l4);

【问题讨论】:

  • 您是否尝试过使用 Krakatau 反汇编程序而不是 javap? Krakatau 应该让您更准确地了解类文件中的实际内容。 github.com/Storyyeller/Krakatau
  • 你把东西留在循环中的堆栈上。
  • 为什么要操作行号调试信息?这与您在该声明之前给出的任务描述相矛盾。此外,您应该根据执行转换的代码来展示您实际做了什么。
  • 这似乎是您所描述的代码操作的一小部分。没有调用callAFunctionToDoSthing,没有重定向ar/ar2 访问temp,并且没有定义标签l45Ins 的代码位置。此外,您没有展示解析原始类和生成新类的方式。
  • 我们不能事先说哪个部分与问题相关。如果您怀疑问题是由转换的特定部分引起的,只需尝试剥离其他所有内容并测试问题是否仍然存在。当您拥有重现问题所需的最少代码时发布它,无论它有多大。

标签: java bytecode java-bytecode-asm bytecode-manipulation jvm-bytecode


【解决方案1】:

我终于能够弄清楚如何解决上述问题。问题出在标签节点上。正如您在问题的编辑部分中看到的那样,我删除了 LabelNode 39 并插入了一个新的 LabelNode。在 main 的 Return 语句之后,我也在最后的 LabelNode 中执行此操作。 虽然 ASM 无法检测到我插入的新 LabelNode 并用它替换旧的。更具体地说,我在 LocalVariableTable 中拥有的全局范围变量在程序的最后一个标签上完成。虽然,当我删除那个 LabelNode 时,他们得到了一个非常大的数字,导致程序崩溃。 为了解决这个问题,我只是使用了与初始版本相同的 LabelNodes,并且仅在没有其他选择时才在我想要的程序中插入新的。当然,我仍然编辑 LinenumberNode 以反映我想要的行。对于上面的示例,解决问题的代码如下

IntInsnNode bp7 = (IntInsnNode) aload17.getNext();
instructions.remove(bp7.getNext());

LabelNode l39 = (LabelNode) bp7.getNext();
LineNumberNode lnl39 = (LineNumberNode) l39.getNext();
lnl39.line = 178;
instructions.remove(lnl39.getNext());
instructions.remove(lnl39.getNext());

InsnList l4 = new InsnList();
LabelNode l45Ins = new LabelNode();
l4.add(new JumpInsnNode(Opcodes.IF_ICMPGT, l45Ins));
instructions.insert(bp7, l4);

InsnList l5 = new InsnList();
l5.add(new VarInsnNode(Opcodes.ALOAD, 20));
l5.add(new InsnNode(Opcodes.ICONST_0));
instructions.insert(lnl39, l5);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-21
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多