LMF*_*LMF 1 java jvm jvm-bytecode
我正在为 Java 创建一个静态分析工具,并且有一些有关我正在分析的程序的信息,如果我可以从文件中的字节码中获取这些信息,将更容易获得这些信息.class。
我不关心类文件中可能存在的每一条指令。例如,我可能只需要看看是否有任何getfield说明。
问题是,由于每条指令都有一个可变的长度,似乎在一般情况下,我需要(在我的代码中)指定每个操作码的长度,然后才能确定(例如)指令的开始和getfield结束位置。
对于其他一些指令集(例如x86),有一些规则,例如“任何低于 0x0F 的操作码都是 1 个字节,任何等于或大于 0x0F 的操作码都是两个字节。”
Java字节码指令中有类似这样方便的模式吗?
如果您尝试将指令操作码映射到指令大小,您\xe2\x80\x99将得到以下令人沮丧的表:
\n\n0 - 15 1 bytes\n16 2 bytes\n17 3 bytes\n18 2 bytes\n19 - 20 3 bytes\n21 - 25 2 bytes\n26 - 53 1 bytes\n54 - 58 2 bytes\n59 - 131 1 bytes\n132 3 bytes\n133 - 152 1 bytes\n153 - 168 3 bytes\n169 2 bytes\n170 - 171 special handling\n172 - 177 1 bytes\n178 - 184 3 bytes\n185 - 186 5 bytes\n187 3 bytes\n188 2 bytes\n189 3 bytes\n190 - 191 1 bytes\n192 - 193 3 bytes\n194 - 195 1 bytes\n196 special handling\n197 4 bytes\n198 - 199 3 bytes\n200 - 201 5 bytes\nRun Code Online (Sandbox Code Playgroud)\n\n换句话说,指令\xe2\x80\x99s 数值及其位模式中没有编码大小信息,但还有另一个属性,您可以考虑某种模式:在约 200 个定义的指令中,大致150 条指令的大小为 1 个字节,只剩下约 50 条指令需要任何处理。即使这一小组指令也可以进一步细分为逻辑组,其中大多数组占用三个字节,第二大组占用两个字节。
\n\n因此,快速执行指令的方法的代码可能如下所示:
\n\nstatic void readByteCode(ByteBuffer bb) {\n while(bb.hasRemaining()) {\n switch(bb.get()&0xff) {\n case BIPUSH: // one byte embedded constant\n case LDC: // one byte embedded constant pool index\n // follow-up: one byte embedded local variable index\n case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD:\n case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: case RET:\n case NEWARRAY: // one byte embedded array type\n bb.get();\n break;\n\n case IINC: // one byte local variable index, another one for the constant\n case SIPUSH: // two bytes embedded constant\n case LDC_W: case LDC2_W: // two bytes embedded constant pool index\n // follow-up: two bytes embedded branch offset\n case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE:\n case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE:\n case IF_ICMPGT: case IF_ICMPLE: case IF_ACMPEQ: case IF_ACMPNE:\n case GOTO: case JSR: case IFNULL: case IFNONNULL:\n // follow-up: two bytes embedded constant pool index to member or type\n case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD:\n case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case NEW:\n case ANEWARRAY: case CHECKCAST: case INSTANCEOF:\n bb.getShort();\n break;\n\n case MULTIANEWARRAY:// two bytes pool index, one byte dimension\n bb.getShort();\n bb.get();\n break;\n\n // follow-up: two bytes embedded constant pool index to member, two reserved\n case INVOKEINTERFACE: case INVOKEDYNAMIC:\n bb.getShort();\n bb.getShort();\n break;\n\n case GOTO_W: case JSR_W:// four bytes embedded branch offset\n bb.getInt();\n break;\n\n case LOOKUPSWITCH:\n // special handling left as an exercise for the reader...\n break;\n case TABLESWITCH:\n // special handling left as an exercise for the reader...\n break;\n case WIDE:\n int widened=bb.get()&0xff;\n bb.getShort(); // local variable index\n if(widened==IINC) {\n bb.getShort(); // constant offset value\n }\n break;\n default: // one of the ~150 instructions taking one byte\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我故意将一些指令分开,具有相同数量的后续字节,但具有不同的含义。毕竟,我想你想在某些地方插入一些实际的逻辑。
\n\n请注意,两个字节码指令的处理switch被省略,它们需要填充,其实现需要了解缓冲区内的代码对齐情况,而缓冲区由调用者控制。因此,\xe2\x80\x99 取决于您的具体应用。lookupswitch请参阅和的文档tableswitch。
当然,处理所有单字节指令意味着default代码不会捕获未知或无效指令。如果你想安全,你\xe2\x80\x99必须插入case\xe2\x80\xa6