Code属性用于表示成员方法的代码部分,Code中包含了指令集(byte数组),JVM调用成员方法时实际上就是执行的Code中的指令,而反编译工具则是把Code中的指令翻译成了Java代码。

    1. Code_attribute {
    2. u2 attribute_name_index;
    3. u4 attribute_length;
    4. u2 max_stack;
    5. u2 max_locals;
    6. u4 code_length;
    7. u1 code[code_length];
    8. u2 exception_table_length;
    9. { u2 start_pc;
    10. u2 end_pc;
    11. u2 handler_pc;
    12. u2 catch_type;
    13. } exception_table[exception_table_length];
    14. u2 attributes_count;
    15. attribute_info attributes[attributes_count];
    16. }

    Code解析代码片段:

    1. int maxStack = dis.readUnsignedShort();
    2. int maxLocals = dis.readUnsignedShort();
    3. int codeLength = dis.readInt();
    4. List<String> opcodeList = new ArrayList<>();
    5. byte[] bytes = new byte[codeLength];
    6. // 读取所有的code字节
    7. dis.read(bytes);
    8. // 创建Code输入流
    9. DataInputStream bis = new DataInputStream(new ByteArrayInputStream(bytes));
    10. // 创建属性Map
    11. Map<String, Object> attrMap = new LinkedHashMap<>();
    12. attrMap.put("maxStack", maxStack);
    13. attrMap.put("maxLocals", maxLocals);
    14. attrMap.put("codeLength", codeLength);
    15. // 是否是宽类型
    16. boolean wide = false;
    17. for (int offset = 0; offset < codeLength; offset++) {
    18. int branchOffset = -1;
    19. int defaultOffset = -1;
    20. int switchNumberofPairs = -1;
    21. int switchNumberOfOffsets = -1;
    22. int immediateByte = -1;
    23. int immediateShort = -1;
    24. int arrayDimensions = 0;
    25. int incrementConst = -1;
    26. int incrementConst2 = -1;
    27. int switchMatch = -1;
    28. int switchOffset = -1;
    29. int[] switchJumpOffsets = null;
    30. int bytesToRead = 0;
    31. int code = bis.readUnsignedByte();
    32. Opcodes opcode = Opcodes.getOpcodes(code);
    33. if (opcode == null) {
    34. continue;
    35. }
    36. switch (opcode) {
    37. case BIPUSH:
    38. case LDC:
    39. case ILOAD:
    40. case LLOAD:
    41. case FLOAD:
    42. case DLOAD:
    43. case ALOAD:
    44. case ISTORE:
    45. case LSTORE:
    46. case FSTORE:
    47. case DSTORE:
    48. case ASTORE:
    49. case RET:
    50. case NEWARRAY:
    51. if (wide) {
    52. immediateByte = bis.readUnsignedShort();
    53. } else {
    54. immediateByte = bis.readUnsignedByte();
    55. }
    56. addOpcodes(opcodeList, opcode, immediateByte);
    57. // 因为读取了byte,所以需要重新计算bis偏移量
    58. offset += wide ? 2 : 1;
    59. break;
    60. case LDC_W:
    61. case LDC2_W:
    62. case GETSTATIC:
    63. case PUTSTATIC:
    64. case GETFIELD:
    65. case PUTFIELD:
    66. case INVOKEVIRTUAL:
    67. case INVOKESPECIAL:
    68. case INVOKESTATIC:
    69. case NEW:
    70. case ANEWARRAY:
    71. case CHECKCAST:
    72. case INSTANCEOF:
    73. case SIPUSH:
    74. addOpcodes(opcodeList, opcode, bis.readUnsignedShort());
    75. offset += 2;
    76. break;
    77. case IFEQ:
    78. case IFNE:
    79. case IFLT:
    80. case IFGE:
    81. case IFGT:
    82. case IFLE:
    83. case IF_ICMPEQ:
    84. case IF_ICMPNE:
    85. case IF_ICMPLT:
    86. case IF_ICMPGE:
    87. case IF_ICMPGT:
    88. case IF_ICMPLE:
    89. case IF_ACMPEQ:
    90. case IF_ACMPNE:
    91. case GOTO:
    92. case JSR:
    93. case IFNULL:
    94. case IFNONNULL:
    95. branchOffset = bis.readShort();
    96. opcodeList.add(opcode.getDesc() + " " + branchOffset);
    97. offset += 2;
    98. break;
    99. case GOTO_W:
    100. case JSR_W:
    101. branchOffset = bis.readInt();
    102. opcodeList.add(opcode.getDesc() + " " + branchOffset);
    103. offset += 4;
    104. break;
    105. case IINC:
    106. if (wide) {
    107. incrementConst = bis.readUnsignedShort();
    108. } else {
    109. incrementConst = bis.readUnsignedByte();
    110. }
    111. if (wide) {
    112. incrementConst2 = bis.readUnsignedShort();
    113. } else {
    114. incrementConst2 = bis.readUnsignedByte();
    115. }
    116. opcodeList.add(opcode.getDesc() + " " + incrementConst + " by " + incrementConst2);
    117. offset += wide ? 4 : 2;
    118. break;
    119. case TABLESWITCH:
    120. bytesToRead = readPaddingBytes(bytes, bis);
    121. defaultOffset = bis.readInt();
    122. int lowByte = bis.readInt();
    123. int highByte = bis.readInt();
    124. switchNumberOfOffsets = highByte - lowByte + 1;
    125. switchJumpOffsets = new int[switchNumberOfOffsets];
    126. for (int k = 0; k < switchNumberOfOffsets; k++) {
    127. switchJumpOffsets[k] = bis.readInt();
    128. }
    129. opcodeList.add(opcode.getDesc());
    130. offset += bytesToRead + 12 + 4 * switchNumberOfOffsets;
    131. break;
    132. case LOOKUPSWITCH:
    133. bytesToRead = readPaddingBytes(bytes, bis);
    134. defaultOffset = bis.readInt();
    135. switchNumberofPairs = bis.readInt();
    136. for (int k = 0; k < switchNumberofPairs; k++) {
    137. switchMatch = bis.readInt();
    138. switchOffset = bis.readInt();
    139. }
    140. opcodeList.add(opcode.getDesc());
    141. offset += bytesToRead + 8 + 8 * switchNumberofPairs;
    142. break;
    143. case INVOKEINTERFACE:
    144. immediateShort = bis.readUnsignedShort();
    145. offset += 2;
    146. int count = bis.readUnsignedByte();
    147. // 1byte永远为0,所以直接丢弃
    148. bis.readByte();
    149. addOpcodes(opcodeList, opcode, immediateShort);
    150. offset += 2;
    151. break;
    152. case INVOKEDYNAMIC:
    153. immediateShort = bis.readUnsignedShort();
    154. offset += 2;
    155. // 2byte永远为0,所以直接丢弃
    156. bis.readUnsignedShort();
    157. addOpcodes(opcodeList, opcode, immediateShort);
    158. offset += 2;
    159. break;
    160. case MULTIANEWARRAY:
    161. immediateShort = bis.readUnsignedShort();
    162. offset += 2;
    163. arrayDimensions = bis.readUnsignedByte();
    164. addOpcodes(opcodeList, opcode, immediateShort);
    165. offset += 1;
    166. break;
    167. default:
    168. opcodeList.add(opcode.getDesc());
    169. }
    170. wide = (WIDE == opcode);
    171. }
    172. attrMap.put("opcodes", opcodeList);
    173. // 读取异常表
    174. attrMap.put("exceptionTable", readExceptionTable());
    175. // u2 attributes_count;
    176. int attributesCount = dis.readShort();
    177. attrMap.put("attributeLength", attributeLength);
    178. attrMap.put("attributes", readAttributes(attributesCount));
    179. // 递归读取属性信息
    180. attributeMap.put("Code", attrMap);

    在解析Code属性时code_length表示的是Code的字节长度,max_stackmax_locals是一个固定值,表示的是最大操作数栈和最大局部变量数,这两个值是在编译类方法时自动计算出来的,如果通过ASM修改了类方法可能会需要重新计算max_stackmax_locals

    示例 - TestHelloWorld类Hello方法解析结果:

    1. {
    2. "access": 1,
    3. "name": "hello",
    4. "desc": "(Ljava/lang/String;)Ljava/lang/String;",
    5. "attributesCount": 1,
    6. "attributes": {
    7. "attributeName": "Code",
    8. "attributeLength": 88,
    9. "Code": {
    10. "maxStack": 2,
    11. "maxLocals": 3,
    12. "codeLength": 22,
    13. "opcodes": [
    14. "ldc #3 <Hello:>",
    15. "astore_2",
    16. "new #4 <java/lang/StringBuilder>",
    17. "dup",
    18. "invokespecial #5 <java/lang/StringBuilder.<init>>",
    19. "aload_2",
    20. "invokevirtual #6 <java/lang/StringBuilder.append>",
    21. "aload_1",
    22. "invokevirtual #6 <java/lang/StringBuilder.append>",
    23. "invokevirtual #7 <java/lang/StringBuilder.toString>",
    24. "areturn"
    25. ],
    26. "exceptionTable": {
    27. "exceptionTableLength": 0,
    28. "exceptionTableList": [ ]
    29. },
    30. "attributeLength": 88,
    31. "attributes": {
    32. "attributeName": "LocalVariableTable",
    33. "attributeLength": 32,
    34. "LineNumberTable": {
    35. "lineNumberTableLength": 2,
    36. "lineNumberTableList": [
    37. {
    38. "startPc": 0,
    39. "lineNumber": 21
    40. },
    41. {
    42. "startPc": 3,
    43. "lineNumber": 22
    44. }
    45. ]
    46. },
    47. "LocalVariableTable": {
    48. "localVariableTableLength": 3,
    49. "localVariableTableList": [
    50. {
    51. "startPc": 0,
    52. "length": 22,
    53. "name": "this",
    54. "desc": "Lcom/anbai/sec/bytecode/TestHelloWorld;",
    55. "index": 0
    56. },
    57. {
    58. "startPc": 0,
    59. "length": 22,
    60. "name": "content",
    61. "desc": "Ljava/lang/String;",
    62. "index": 1
    63. },
    64. {
    65. "startPc": 3,
    66. "length": 19,
    67. "name": "str",
    68. "desc": "Ljava/lang/String;",
    69. "index": 2
    70. }
    71. ]
    72. }
    73. }
    74. }
    75. }
    76. }

    解析Code的指令集时需要对照指令集映射表,然后根据不同的指令实现不一样的指令处理逻辑,指令列表和详细的描述请参考:JVM规范-指令