使用Java中的ASM测试跳转

时间:2019-03-13 22:46:56

标签: java bytecode java-bytecode-asm

我正在尝试通过删除条件跳转来防止条件跳转无效:

if(1 > 10)  {
   return;
}

因此,我决定创建一个BasicInterpreter来检查跳转的帧是否为null,如果是,请将其删除。而且它不是null,因此不会将其检测为无用的。

无效的代码:

Analyzer<BasicValue> an = new Analyzer<BasicValue>(new BasicInterpreter());
Frame<BasicValue>[] frames = an.analyze(cn.name, mn);

for (int i = 0; i < frames.length; i++) {
    if ((mn.instructions.get(i) instanceof JumpInsnNode)) {
        if (mn.instructions.get(i).getOpcode() >= IFEQ && mn.instructions.get(i).getOpcode() <= IF_ACMPNE) {
            if(frames[i] == null)  {
                System.out.println("This jump is useless");
            }
        }
    }
}

然后,我尝试获取一些堆栈值进行手动计算,但没有成功(我发现,但是我无法移植代码以在跳转ASM Get exact value from stack frame上使用它:)

Analyzer<BasicValue> an = new Analyzer<BasicValue>(new BasicInterpreter());
Frame<BasicValue>[] frames = an.analyze(cn.name, mn);

for (int i = 0; i < frames.length; i++) {
    if ((mn.instructions.get(i) instanceof JumpInsnNode)) {
        if (mn.instructions.get(i).getOpcode() >= IFEQ && mn.instructions.get(i).getOpcode() <= IF_ACMPNE) {
            // getStackSize() returns 2 so -> 0 and 1
            frames[i].getStack(0); // this is probably 1
            frames[i].getStack(1); // this is probably 10
            // but it returns a BasicValue so I can't check if the code works or not (we cannot get values)
        }
    }
}

我最后尝试做的是获取跳转使用的指令大小以将其删除(当然,它不会检测它是否是无用的代码,但我至少可以删除它)。

实际上,我尝试创建一个返回常量int的方法,以便我可以检测是否在跳转指令中调用了getValue(如果检测到调用,则删除跳转指令,当然还要删除跳转本身) ):

示例:

if(1 > getValue())  { //getValue() returns 10
   return;
}

代码:

Analyzer<BasicValue> an = new Analyzer<BasicValue>(new BasicInterpreter());
Frame<BasicValue>[] frames = an.analyze(cn.name, mn);
ArrayList<AbstractInsnNode> nodesR = new ArrayList<>();

for (int i = 0; i < frames.length; i++) {
    if ((mn.instructions.get(i) instanceof JumpInsnNode)) {
        if (mn.instructions.get(i).getOpcode() >= IFEQ && mn.instructions.get(i).getOpcode() <= IF_ACMPNE) {
            ArrayList<AbstractInsnNode> toRemove = new ArrayList<>();

            for (int ia = 1; ia < frames[i].getMaxStackSize() + 2; ia++) { // I started from 1 and added 2 to getMaxStackSize because I wasn't getting all the instructions
                toRemove.add(mn.instructions.get(i - ia));
            }

            toRemove.add(mn.instructions.get(i)); // I add the jump to the list

            for (AbstractInsnNode aaa : toRemove) {
                if (aaa.getOpcode() == INVOKESTATIC) { // the invokestatic is getValue
                    for (AbstractInsnNode aaas : toRemove) {
                        nodesR.add(aaas);
                    }
                    break;
                 }
            }
        }
    }
}

for (AbstractInsnNode aaas : nodesR) {
    mn.instructions.remove(aaas);
}

} catch (AnalyzerException e) {
    e.printStackTrace();
}

这段代码可能很可怕并且没有经过优化,但是我尝试了很多事情,但都没有成功。 getMaxStackSize()不会返回100%正确的数字(有时它不进行加法运算,因此会删除标签等指令)。

我要做什么:

通过方法进行分析,并检查条件跳转是否始终为false(因此将永远不会执行其中的代码),然后将其删除。 我尝试了两种不同的方式:

  • 使用BasicInterpreter检查此跳转是否将以恒定值执行,然后尝试查看它是否始终为假
  • 检查跳转是否包含某个方法(例如getValue(),该方法返回10,并比较是否小于1),然后将其删除 我不明白的地方:
  • 我认为每条指令中都有框架,并且其中包含局部变量表以及该框架使用的值– StackMap? -(例如,如果指令比较一个int小于另一个int,则返回[II]对吗?
  • 我不知道我是否可以使用BasicInterpreter测试两个常量int是否总是返回相同的结果
  • StackMap =堆栈?还是不同,例如StackMap是堆栈的一部分,其中包含指令所需的值?

0 个答案:

没有答案