当前位置: 首页 > 知识库问答 >
问题:

ASM-分支目标处的堆栈映射帧不一致

钱安和
2023-03-14

我正在尝试做一个简单的Java字节码混淆器,它通过用简单的条件跳转替换GOTO指令来工作,例如,if 10!=15 GOTO,否则会抛出IllegalStateException。我当前的代码是:

    final AbstractInsnNode[] insns = method.instructions.toArray().clone();

    for (final AbstractInsnNode insn : insns) {
        final int op = insn.getOpcode();

        if ((op == GOTO) || (op == IFLE) || (op == IFGE)) {
            LabelNode l0 = new LabelNode();
            LabelNode l1 = new LabelNode();
            LabelNode l2 = new LabelNode();

            int locals = (method.localVariables == null) ? 0 : method.localVariables.size();
            int params = (method.parameters == null) ? 0 : method.parameters.size();

            int v0index = locals + params;
            int v1index = v0index + 1;
            int exindex = v1index + 1;

            // Init fake conditional fields
            method.instructions.insertBefore(insn, new LdcInsnNode(10F));
            method.instructions.insertBefore(insn, new VarInsnNode(FSTORE, v0index));

            method.instructions.insertBefore(insn, new LdcInsnNode(45F));
            method.instructions.insertBefore(insn, new VarInsnNode(FSTORE, v1index));

            // Crossing jumps
            method.instructions.insertBefore(insn, l1);
            method.instructions.insert(insn, l0);
            method.instructions.insert(l0, l2);

            LabelNode l3 = new LabelNode();
            LabelNode l4 = new LabelNode();

            method.instructions.insert(l2, l3);
            method.instructions.insert(l3, l4);

            // If 'v0!=v1', jump to l0, otherwise goto l3
            method.instructions.insertBefore(l1, new VarInsnNode(FLOAD, v0index));
            method.instructions.insertBefore(l1, new VarInsnNode(FLOAD, v1index));
            method.instructions.insertBefore(l1, new InsnNode(FCMPG));
            method.instructions.insertBefore(l1, new JumpInsnNode(IFNE, l0));
            method.instructions.insertBefore(l1, new JumpInsnNode(GOTO, l3));

            // Jump to l3 results in throwing an exception
            // Create and throw the exception
            method.instructions.insertBefore(l4, new TypeInsnNode(NEW, "java/lang/IllegalStateException"));
            method.instructions.insertBefore(l4, new InsnNode(DUP));
            method.instructions.insertBefore(l4, new MethodInsnNode(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "()V", false));
            method.instructions.insertBefore(l4, new InsnNode(ATHROW));

            method.instructions.insertBefore(l0, new JumpInsnNode(GOTO, l2));
            method.instructions.insertBefore(l2, new JumpInsnNode(GOTO, l1));

            // Exception handler
            LabelNode start = new LabelNode();
            LabelNode handler = new LabelNode();
            LabelNode end = new LabelNode();

            method.instructions.insertBefore(l0, start);

            method.instructions.insert(l2, end);
            method.instructions.insert(end, handler);

            // Just throw the exception again
            LabelNode l5 = new LabelNode();

            method.instructions.insert(handler, l5);
            method.instructions.insertBefore(l5, new TypeInsnNode(NEW, "java/lang/IllegalStateException"));
            method.instructions.insertBefore(l5, new InsnNode(DUP));
            method.instructions.insertBefore(l5, new MethodInsnNode(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "()V", false));
            method.instructions.insertBefore(l5, new InsnNode(ATHROW));

            // Try/catch
            TryCatchBlockNode tryBlock = new TryCatchBlockNode(start, end, handler, "java/lang/IllegalStateException");
            method.tryCatchBlocks.add(tryBlock);

            // Init local variables
            method.visitLocalVariable("_v0_" + Rand.alphaNumeric(5), "F", null, l0.getLabel(), l2.getLabel(), v0index);
            method.visitLocalVariable("_v1_" + Rand.alphaNumeric(5), "F", null, l0.getLabel(), l2.getLabel(), v1index);
            method.visitLocalVariable("_ex_" + Rand.alphaNumeric(5), "Ljava/lang/IllegalArgumentException;", null, start.getLabel(), handler.getLabel(), exindex);
        }
    }

其中method是混淆方法的参数,类型为omeodNode,类实现接口操作码

这很好,但不是所有的方法都可以使用(我对字节码很陌生,所以不知道具体的情况)。例如,它可以很好地用于主方法:

原始Java代码(在Procyon中反编译):https://p.reflex.rip/DLMT.cs

原始字节码:https://p.reflex.rip/ywJt.go

模糊Java代码(在Procyon中反编译):https://p.reflex.rip/Er9V.cs

模糊字节码:https://p.reflex.rip/JBAb.go

但是,它破坏了其他方法之一,divMinByMax,方法:

原始Java代码(在Procyon中反编译):https://p.reflex.rip/AW9W.java

原始字节码:https://p.reflex.rip/GX2k.cpp

模糊Java代码(在Procyon中反编译,失败):https://p.reflex.rip/Eqju.java

模糊字节码:https://p.reflex.rip/isiX.cpp

当我尝试使用java-jar运行混淆的JAR时,此方法会导致VerifyError:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 27
Exception Details:
  Location:
    test/one/HelloRandom.divMinByMax(DD)D @21: goto
  Reason:
    Current frame's stack size doesn't match stackmap.
  Current Frame:
    bci: @21
    flags: { }
    locals: { double, double_2nd, float, float }
    stack: { }
  Stackmap Frame:
    bci: @27
    flags: { }
    locals: { double, double_2nd, float, float }
    stack: { 'java/lang/IllegalStateException' }
  Bytecode:
    0x0000000: 2826 9712 7145 1272 4624 2596 9a00 0ca7
    0x0000010: 0014 9e00 48a7 0006 a7ff fabb 0016 59b7
    0x0000020: 0073 bfbb 0016 59b7 0073 bf00 0000 0000
    0x0000030: 00bf 0000 00bf 0000 0000 0000 00bf 0000
    0x0000040: bf00 00bf 0000 bf00 00bf 0000 0000 0000
    0x0000050: 00bf 0000 0000 0000 00bf 2826 6faf
  Exception Handler Table:
    bci [24, 27] => handler: 27
  Stackmap Table:
    full_frame(@18,{Double,Float,Float},{Integer})
    same_locals_1_stack_item_frame(@24,Integer)
    same_locals_1_stack_item_frame(@27,Object[#22])
    same_locals_1_stack_item_frame(@35,Integer)
    full_frame(@43,{},{Object[#159]})
    same_locals_1_stack_item_frame(@50,Object[#159])
    same_locals_1_stack_item_frame(@54,Object[#159])
    same_locals_1_stack_item_frame(@62,Object[#159])
    same_locals_1_stack_item_frame(@65,Object[#159])
    same_locals_1_stack_item_frame(@68,Object[#159])
    same_locals_1_stack_item_frame(@71,Object[#159])
    same_locals_1_stack_item_frame(@74,Object[#159])
    same_locals_1_stack_item_frame(@82,Object[#159])
    append_frame(@90,Double,Float,Float)
    same_locals_1_stack_item_frame(@93,Double)

我做了很多研究,我发现唯一的原因是:据我所知,问题是21上的堆栈(跳转到标签,它会引发新的非法状态异常):

堆栈: { }

(为空)与跳转目标标签上27处的堆栈不匹配:

堆栈:{“java/lang/IllegalStateException”}(其中包含它应该“抛出”的异常)。

所以基本上,正如我所理解的,错误发生在我试图执行

如何解决此问题?也许有一种方法可以使21处的堆栈在跳转之前也包含java/lang/IllegalStateException(以便这两个堆栈,跳转之前的堆栈和跳转之后的堆栈,匹配)?或者我能用它做些什么?

共有1个答案

凌啸
2023-03-14

您正在指令后插入异常处理程序,但当指令是条件分支(即IFLE或IFGE)时,可能不会执行该分支,并且代码流在指令后继续,运行到异常处理程序中。

这会创建一个不一致的状态,因为异常处理程序期望堆栈上有一个可抛出的,而当指令插入后代码流继续时,堆栈上不存在该状态。但当然,在这种情况下,您不想执行异常处理程序,因此如果我做得对,您必须插入另一个转到,从l2到l5。

当插入指令时,这不是问题,指令永远不会在指令之后继续。

在这里,我推荐一种不同的编码风格。在不同的引用节点之前和之后插入,使得在读取代码时无法预测实际的代码结构。如果只使用一个引用节点插入一个线性指令列表,那么它将更易于维护。

 类似资料:
  • 例外情况详情: 位置:com/sonicsw/mf/comm/jms/ConnectorClient。setRequestTimeout(J)V@3:ifnonnull原因:此位置应为stackmap帧。 谁能解决这个问题?谷歌搜索了一个多星期,并尝试了所有可能的替代方案。Used-XX:-UseSplitVerifier。它也不起作用。使用noverify选项,效果很好。Java版本是1.7.0

  • 成功地编译了项目,并与Maven一起构建。这是我的第一个maven项目。但我不知道为什么会出现下面的错误。 在tomcat上部署war,点击我的url,并在我的浏览器中显示以下错误。 有人能提供一些信息吗?谢谢你的帮助。 配置:

  • 一个运行良好的应用程序项目已经开始在Android Studio中给出这个错误。我尝试过各种各样的事情,比如改变主楼的结构。xml文件、更改Google Play服务版本等,但没有任何帮助。这个错误出现在“显示”选项卡中,我尝试使用api 18进行查看。此外,由于某些原因,间隙广告的填充率已降至0,并且不会返回任何填充。 建筑格拉德尔

  • 我正在开发我的公司项目。 我有2个表。我用一个多-多个关系连接到这个表。我使用postgreSql。 但我得到了这个错误。 原因:java。lang.VerifyError:在分支目标5处应为stackmap帧异常详细信息:位置:com/karcin/template/persistence/entities/TblAgentPropertyParameters。类$(Ljava/lang/Str

  • 使用jdk 1.8.051Source=1.7,target=1.7 Junit 4.6,ant 1.8.0 发生了什么事<所有以1.7通过的测试现在都失败了。我需要知道以下几点: 为什么会这样? 有没有像-XX:-UseSplitVeriator(java 1.7)这样的选项 如何找出问题的根源? 要采取什么步骤来解决这个问题? 我们有用于构建、部署和测试的ant目标 构建和部署工作正常,但Ju

  • 升级到JDK 1.7后,我遇到了以下异常: