2016-06-25 19 views
0

我正在爲Java創建一個靜態分析工具,並且還有一些關於我正在分析的程序的信息,如果我可以從.class文件中的字節碼中獲取它,這將更容易得到。有沒有一種巧妙的方法來確定Java字節碼指令的長度?

我不在乎每個人都可能在類文件中的instructions。例如,我可能只需要查看是否有任何getfield說明。

問題是,由於每條指令的長度都是可變的,所以在一般情況下,我需要(在我的代碼中)指定每個操作碼的長度,然後才能確定指令的位置(例如)getfield指令開始和結束。

對於其他一些指令集(如x86),有一些規則,比如「0x0F以下的任何操作碼是1個字節,任何等於或大於0x0F的操作碼都是兩個字節」。

在Java字節碼指令中是否有像這樣的任何方便的模式?

+2

即使沒有模式(也有一些varadic操作碼)你想要寫一個靜態分析工具,不希望,最壞的情況下,使<< 256個操作碼的映射表。祝你好運。 – msw

回答

2

如果您嘗試指令操作碼映射到指令大小,你會得到以下沮喪表:

0 - 15  1 bytes 
16   2 bytes 
17   3 bytes 
18   2 bytes 
19 - 20  3 bytes 
21 - 25  2 bytes 
26 - 53  1 bytes 
54 - 58  2 bytes 
59 - 131  1 bytes 
132   3 bytes 
133 - 152 1 bytes 
153 - 168 3 bytes 
169   2 bytes 
170 - 171 special handling 
172 - 177 1 bytes 
178 - 184 3 bytes 
185 - 186 5 bytes 
187   3 bytes 
188   2 bytes 
189   3 bytes 
190 - 191 1 bytes 
192 - 193 3 bytes 
194 - 195 1 bytes 
196   special handling 
197   4 bytes 
198 - 199 3 bytes 
200 - 201 5 bytes 

換句話說,沒有在指令的數值,也沒有它的位編碼沒有尺寸信息模式,但還有另一個屬性,你可以考慮某種模式:在大約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 
     } 
    } 
} 

我有意保持一些指令分離具有相同數目的後續字節,但具有不同的含義。畢竟,你想在某些地方插入一些實際的邏輯,我猜。

請注意,兩個switch字節碼指令的處理被省略了,它們需要填充,其實現需要關於緩衝區內代碼對齊的知識,這是控制調用者的。所以這取決於您的具體應用。請參閱lookupswitchtableswitch的文檔。

當然,處理的所有單字節指令爲default意味着代碼不會趕上未知或無效的指令。如果你想安全,你要插入的情況下...

1

The JVM spec是關於指令集相當清楚:

操作數的數量和尺寸由操作碼來確定。

您可以嘗試利用現有的字節碼庫(如Apache Commons BCEL),並使用有關此處定義的操作碼的元數據爲您的應用程序構建單獨的數據結構。例如,it contains a GETFIELD class以及代表JVM指令的getLength()方法。

0

字節碼設計中沒有這樣的功能。這些操作碼根本就是grouped。 JVM實現我已經看過使用表查找字節碼的長度,特殊處理wide,tableswitchlookupswitch字節碼。

Such table非常小:只有202個字節碼。

注意使長度不僅取決於操作碼本身,而且還取決於位置的字節碼的tableswitchlookupswitch具有可變的長度填充由於對齊要求。

相關問題