有没有一种聪明的方法来确定Java字节码指令的长度?
我正在为Java创建一个静态分析工具,如果我可以从有没有一种聪明的方法来确定Java字节码指令的长度?,java,jvm,jvm-bytecode,Java,Jvm,Jvm Bytecode,我正在为Java创建一个静态分析工具,如果我可以从.class文件中的字节码中获得一些关于我正在分析的程序的信息,那么这些信息将更容易获得 我不关心类文件中的每一个。例如,我可能只需要查看是否有任何getfield指令 问题在于,由于每条指令的长度都是可变的,因此在一般情况下,我需要(在代码中)指定每条操作码的长度,然后才能确定(例如)getfield指令的开始和结束位置 对于其他一些指令集(如),有这样的规则:“任何低于0x0F的操作码都是1字节,任何等于或大于0x0F的操作码都是2字节。”
.class
文件中的字节码中获得一些关于我正在分析的程序的信息,那么这些信息将更容易获得
我不关心类文件中的每一个。例如,我可能只需要查看是否有任何getfield
指令
问题在于,由于每条指令的长度都是可变的,因此在一般情况下,我需要(在代码中)指定每条操作码的长度,然后才能确定(例如)getfield
指令的开始和结束位置
对于其他一些指令集(如),有这样的规则:“任何低于0x0F的操作码都是1字节,任何等于或大于0x0F的操作码都是2字节。”
Java字节码指令中有这样的方便模式吗 非常清楚指令集:
操作数的数目和大小由操作码决定
您可以尝试利用现有的字节码库,如ApacheCommons BCEL,并使用其中定义的操作码元数据为应用程序构建单独的数据结构。例如,与表示JVM指令的getLength()
方法一起使用。非常清楚指令集:
操作数的数目和大小由操作码决定
您可以尝试利用现有的字节码库,如ApacheCommons BCEL,并使用其中定义的操作码元数据为应用程序构建单独的数据结构。例如,除了表示JVM指令的
getLength()
方法之外。字节码设计中没有此类功能。操作码的含义很简单。我见过的JVM实现使用字节码长度的表查找,并对和字节码进行特殊处理
它非常小:只有202个字节码
注意长度不仅取决于操作码本身,还取决于字节码的位置:
表开关
和查找开关
由于对齐要求,具有可变长度填充。字节码设计中没有此类功能。操作码的含义很简单。我见过的JVM实现使用字节码长度的表查找,并对和字节码进行特殊处理
它非常小:只有202个字节码
注意长度不仅取决于操作码本身,还取决于字节码的位置:
表开关
和查找开关
由于对齐要求,具有可变长度的填充。如果尝试将指令操作码映射到指令大小,您将得到以下令人沮丧的表格:
0-15 1字节
16.2字节
17.3字节
18.2字节
19-20 3字节
21-25 2字节
26-53 1字节
54-58 2字节
59-131 1字节
132 3字节
133-152 1字节
153-168 3字节
169 2字节
170-171特殊处理
172-177 1字节
178-184 3字节
185-186 5字节
1873字节
1882字节
189 3字节
190-191 1字节
192-1933字节
194-195 1字节
196特殊处理
197 4字节
198-199 3字节
200-201 5字节
换句话说,在指令的数值和位模式中没有编码大小的信息,但是还有另一个属性,你可以考虑某种模式:从200个定义的指令中,大约有150个指令的大小是一个字节,只留下50个指令,这些指令需要任何处理。即使是这一小部分指令也可以进一步细分为逻辑组,大多数指令占用三个字节,第二大指令占用两个字节
因此,快速浏览指令的方法代码可能如下所示:static void readByteCode(ByteBuffer bb) {
while(bb.hasRemaining()) {
switch(bb.get()&0xff) {
case BIPUSH: // one byte embedded constant
case LDC: // one byte embedded constant pool index
// follow-up: one byte embedded local variable index
case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD:
case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: case RET:
case NEWARRAY: // one byte embedded array type
bb.get();
break;
case IINC: // one byte local variable index, another one for the constant
case SIPUSH: // two bytes embedded constant
case LDC_W: case LDC2_W: // two bytes embedded constant pool index
// follow-up: two bytes embedded branch offset
case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE:
case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE:
case IF_ICMPGT: case IF_ICMPLE: case IF_ACMPEQ: case IF_ACMPNE:
case GOTO: case JSR: case IFNULL: case IFNONNULL:
// follow-up: two bytes embedded constant pool index to member or type
case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD:
case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case NEW:
case ANEWARRAY: case CHECKCAST: case INSTANCEOF:
bb.getShort();
break;
case MULTIANEWARRAY:// two bytes pool index, one byte dimension
bb.getShort();
bb.get();
break;
// follow-up: two bytes embedded constant pool index to member, two reserved
case INVOKEINTERFACE: case INVOKEDYNAMIC:
bb.getShort();
bb.getShort();
break;
case GOTO_W: case JSR_W:// four bytes embedded branch offset
bb.getInt();
break;
case LOOKUPSWITCH:
// special handling left as an exercise for the reader...
break;
case TABLESWITCH:
// special handling left as an exercise for the reader...
break;
case WIDE:
int widened=bb.get()&0xff;
bb.getShort(); // local variable index
if(widened==IINC) {
bb.getShort(); // constant offset value
}
break;
default: // one of the ~150 instructions taking one byte
}
}
}
我故意将一些指令分开,它们具有相同的后续字节数,但含义不同。毕竟,我想你想在某些地方插入一些实际的逻辑
请注意,两个开关字节码指令的处理被省略,它们需要填充,其实现需要了解缓冲区内的代码对齐情况,缓冲区由调用者控制。这取决于您的具体应用。请参阅和的文档
当然,将所有单字节指令作为默认值处理意味着代码不会捕获未知或无效指令。如果您想要安全,您必须插入案例…如果您尝试将指令操作码映射到指令大小,您将得到以下令人沮丧的表格:
0-15 1字节
16.2字节
17.3字节
18.2字节
19-20 3字节
21-25 2字节
26-53 1字节
54-58 2字节
59-131 1字节
132 3字节
133-152 1字节
153-168 3字节
169 2字节
170-171特殊处理
172-177 1字节
178-184 3字节
185-186 5字节
1873字节
1882字节
189 3字节
190-191 1字节
192-1933字节
194-195 1字节
196特殊处理
197 4字节
198-199 3字节
200-201 5字节
换句话说,在指令的数值和位模式中没有编码大小的信息,但是还有另一个属性,你可以考虑某种模式:从200个定义的指令中,大约有150个指令的大小是一个字节,只留下50个指令,这些指令需要任何处理。即使是这一小部分指令也可以进一步细分