阻止objectweb asm用athrow替换无法访问的代码

时间:2017-11-23 17:44:34

标签: java bytecode

解决了,此代码执行了替换:https://gitlab.ow2.org/asm/asm/blob/master/asm/src/main/java/org/objectweb/asm/MethodWriter.java#L1382

解决方案是使用ifeq(false)来跳转死代码

我正在尝试使用objectweb asm将一些无法访问的代码注入到方法中。但是它一直用athrow

替换指令

例如我有这个方法:

public static boolean isTurkeyDay() {
    iconst_1
    ireturn
}

我尝试将其更改为:

public static boolean isTurkeyDay() {
    goto L1
    nop
    nop
    :L1
    iconst_1
    ireturn
}

通过运行:

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
    Label l1 = new Label();
    mv.visitJumpInsn(Opcodes.GOTO, l1);
    mv.visitInsn(Opcodes.NOP);
    mv.visitInsn(Opcodes.NOP);
    mv.visitLabel(l1);
    return super(mv);
}

我的实际输出结束了

public static boolean isTurkeyDay() {
    goto L1
    nop
    athrow
    :L1
    iconst_1
    ireturn
}

有没有告诉Objectweb不要用athrow替换我的指令?我想把死代码留在原地。我错过了什么吗?

1 个答案:

答案 0 :(得分:3)

ASM执行此操作的原因是需要生成堆栈映射帧。从历史上看,包含死字节码没有问题 - 它只会被验证者忽略。但是,随着堆栈映射验证的引入,非线性控制流的每个点都需要堆栈映射帧,并且验证器会处理代码。

如果要为任意字节码生成堆栈映射,则会导致问题。例如,考虑以下字节码

goto LWHATEVER 
; dead code
iload_0
aload_0

没有可能的堆栈映射使该字节码有效,因为代码将寄存器0作为int和对象加载。通过推理验证,这没有问题,因为验证者从不首先处理死代码,但是使用堆栈映射验证程序,所有代码都在单个线性传递中处理,无论它是否可达。

因此,为了解决这个问题,ASM只用一系列nop s后跟athrow替换死代码。这确保了a)有一个可以为它生成的有效堆栈帧(特别是堆栈上有一个例外的堆栈帧)和b)死代码似乎没有跳转到其他地方,这将进一步混淆堆栈映射验证。这就是为什么最后只有一个人,而不是仅仅使用所有的nops。

对于if false"解决方案",如果它对您有用,那就没问题,但它不是100%的解决方案。它起作用的原因是因为验证者忽略了条件值并假设可以采用每个可能的分支。因此,您的代码被视为实时。但是,这样做的必然结果是它必须具有有效的类型和控制流,就像代码实际可以访问一样。同样,ASM将看到它是实时代码,只需使用普通类型检查过程来生成堆栈帧。但是,如果您尝试在if false分支下放置无效字节码,则会收到错误,因为它被认为是实时的。

在方法中嵌入任意死代码的唯一真正解决方案是首先不使用堆栈映射,这意味着将类文件版本设置为50.0或更低,并告知ASM不生成堆栈映射。或者,您可以使用Krakatau汇编程序,它可以对生成的字节码进行低级别控制,但代价是不会自动生成堆栈帧。