如何閱讀jvm字節碼

如何閱讀jvm字節碼

  1. jvm規範

虛擬機文檔:https://docs.oracle.com/javase/specs/index.html

jdk8虛擬機規範:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

  1. class文件的結構

在jvm中分別用u1、u2、u4、u8來表示1個字節、2個字節、4個字節和8個字節的無符號數

magic

u4類型的一個魔數,固定值cafe babe,jvm會根據這個值校驗是不是class文件,如果不是這個值開頭的文件,虛擬機會拒絕加載

minor_version,major_version 版本號

 

➜  bin ./java -version
java version "1.8.0_241"
Java(TM) SE Runtime Environment (build 1.8.0_241-b07)
Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)

 

jvm加載class只會校驗主版本的大小,拒絕加載被編譯的比當前jvm版本大的文件。

constant_pool_count:常量池中的個數,常量池中的個數會比常量表(constant table)中的數量多一個,constant_pool列表中沒有索引值爲0的入口,但缺失的這一入口也被constant_pool_count計數在內。例如,當constant_pool中有5項(索引值從1到5)時,constant_pool_count的值爲5.

constant_pool:常量池,記錄了類,接口,方法,字段的名字,以及相關常量。

cp_info{

u1 tag;

u1 info[];

}

info可選值:

 

 

ConstantType

 

Value

 

ConstantType

 

Value

 

CONSTNAT_Class

 

7

 

CONSTNAT_Long

 

5

 

CONSTNAT_Fieldref

 

9

 

CONSTNAT_Double

 

6

 

CONSTNAT_Methodref

 

10

 

CONSTNAT_NameAndType

 

12

 

CONSTNAT_InterfaceMethodref

 

11

 

CONSTNAT_Utf8

 

1

 

CONSTNAT_String

 

8

 

CONSTNAT_MethodHandler

 

15

 

CONSTNAT_Integer

 

3

 

CONSTNAT_MethodType

 

16

 

CONSTNAT_Float

 

4

 

CONSTNAT_InvokeDynamic

 

18

 

示例:

CONSTNAT_Class_info{

u1 tag;

u2 name_index;

}

CONSTNAT_Fieldref_info{

u1 tag;

u2 class_index;

u2 name_and_type_index;

}

CONSTNAT_InvokeDynamic_info{

u1 tag;

u2 bootstrap_method_attr_index;

u2 name_and_type_index;

}

 

access_flag:訪問標示

可以看到access_flag是一個u2類型的數據,那如何才能容納下這麼多的選項呢,例如一個類的access_flag反編譯過來之後16進制是 00 21 那。那這個0021是怎麼生成的呢,可以看到用0x0001和0x0020 做或操作可以生成0021,也就是0021是由ACC_PUBLIC和ACC_SUPER這兩個修飾符共同組成。

this_class:當前類,指向常量池中的某個索引

super_class:父類,如果沒有父類,那這個類一定要是Java.lang.Object,否則Java虛擬機就編譯不通過。

interface_count:接口數量

interfaces[]

所有指向的接口,每一個指都會指向一個常量池的值,需要是一個CONSTANT_Class_info的結構

fields_count:字段的數量,

fields[] 屬性值,是一個field_info_structure的結構,

method_count:方法個數,是method_info的一個結構體

method[] :每一個方法表中的內容是一個method_info的結構,方法表中的不包含從父類繼承過來的方法。

attributes_count:屬性表中的數量

attributes[]:屬性表,是一個attribute_info的結構。

 

  1. 虛擬機的限制

  • 接口或者類的常量池的個數,最大隻能有65535個,u2類型,16bit

  • field_count個數最多65535個,u2類型,16bit

  • 一個類或接口中的方法最多65535個,u2類型,16bit

  • 父類或接口的數量最多65535個,u2類型,16bit

  • 本地變量表中的數量對多65535個,u2類型,16bit

  • 方法的參數最多255個

  • 方法命名,類命名,字段命名的長度最多65535個字符,utf8類型,16bit

  • 數組的維度最多255維度

 

 

  1. 二進制字節碼實例

 

cafe babe 0000 0034 0010 0a00 0300 0d07
000e 0700 0f01 0006 3c69 6e69 743e 0100
0328 2956 0100 0443 6f64 6501 000f 4c69
6e65 4e75 6d62 6572 5461 626c 6501 0012
4c6f 6361 6c56 6172 6961 626c 6554 6162
6c65 0100 0474 6869 7301 002a 4c63 6f6d
2f65 7861 6d70 6c65 2f73 6865 6e6d 752f
6465 6d6f 732f 6a76 6d2f 4d79 436c 6173
7344 656d 6f3b 0100 0a53 6f75 7263 6546
696c 6501 0010 4d79 436c 6173 7344 656d
6f2e 6a61 7661 0c00 0400 0501 0028 636f
6d2f 6578 616d 706c 652f 7368 656e 6d75
2f64 656d 6f73 2f6a 766d 2f4d 7943 6c61
7373 4465 6d6f 0100 106a 6176 612f 6c61
6e67 2f4f 626a 6563 7400 2100 0200 0300
0000 0000 0100 0100 0400 0500 0100 0600
0000 2f00 0100 0100 0000 052a b700 01b1
0000 0002 0007 0000 0006 0001 0000 0006
0008 0000 000c 0001 0000 0005 0009 000a
0000 0001 000b 0000 0002 000c          

 

 

  1. Javap命令

j

 

javap --help
用法: javap <options> <classes>
其中, 可能的選項包括:
  -? -h --help -help               輸出此幫助消息
  -version                         版本信息
  -v  -verbose                     輸出附加信息
  -l                               輸出行號和本地變量表
  -public                          僅顯示公共類和成員
  -protected                       顯示受保護的/公共類和成員
  -package                         顯示程序包/受保護的/公共類
                                   和成員 (默認)
  -p  -private                     顯示所有類和成員
  -c                               對代碼進行反彙編
  -s                               輸出內部類型簽名
  -sysinfo                         顯示正在處理的類的
                                   系統信息 (路徑, 大小, 日期, MD5 散列)
  -constants                       顯示最終常量
  --module <模塊>, -m <模塊>       指定包含要反彙編的類的模塊
  --module-path <路徑>             指定查找應用程序模塊的位置
  --system <jdk>                   指定查找系統模塊的位置
  --class-path <路徑>              指定查找用戶類文件的位置
  -classpath <路徑>                指定查找用戶類文件的位置
  -cp <路徑>                       指定查找用戶類文件的位置
  -bootclasspath <路徑>            覆蓋引導類文件的位置

 

  1. 實例-Javap反編譯閱讀

 

javap -v MyClassDemo   
警告: 文件 ./MyClassDemo.class 不包含類 MyClassDemo
Classfile /Users/wangtengfei/DemoIdeaProjects/target/classes/com/example/shenmu/demos/jvm/MyClassDemo.class
  Last modified 2020年10月31日; size 721 bytes
  MD5 checksum 3d507cf075b94e5025c6a9f39b05a2b4
  Compiled from "MyClassDemo.java"
public class com.example.shenmu.demos.jvm.MyClassDemo
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #6                          // com/example/shenmu/demos/jvm/MyClassDemo
  super_class: #7                         // java/lang/Object
  interfaces: 0, fields: 1, methods: 3, attributes: 1
Constant pool:
   #1 = Methodref          #7.#25         // java/lang/Object."<init>":()V
   #2 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Fieldref           #6.#28         // com/example/shenmu/demos/jvm/MyClassDemo.sayHi:Ljava/lang/String;
   #4 = Methodref          #29.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #31            // Hello
   #6 = Class              #32            // com/example/shenmu/demos/jvm/MyClassDemo
   #7 = Class              #33            // java/lang/Object
   #8 = Utf8               sayHi
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Lcom/example/shenmu/demos/jvm/MyClassDemo;
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               args
  #20 = Utf8               [Ljava/lang/String;
  #21 = Utf8               MethodParameters
  #22 = Utf8               <clinit>
  #23 = Utf8               SourceFile
  #24 = Utf8               MyClassDemo.java
  #25 = NameAndType        #10:#11        // "<init>":()V
  #26 = Class              #34            // java/lang/System
  #27 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #28 = NameAndType        #8:#9          // sayHi:Ljava/lang/String;
  #29 = Class              #37            // java/io/PrintStream
  #30 = NameAndType        #38:#39        // println:(Ljava/lang/String;)V
  #31 = Utf8               Hello
  #32 = Utf8               com/example/shenmu/demos/jvm/MyClassDemo
  #33 = Utf8               java/lang/Object
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (Ljava/lang/String;)V
{
  public com.example.shenmu.demos.jvm.MyClassDemo();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/shenmu/demos/jvm/MyClassDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: getstatic     #3                  // Field sayHi:Ljava/lang/String;
         6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         9: return
      LineNumberTable:
        line 10: 0
        line 11: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  args   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args

  static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #5                  // String Hello
         2: putstatic     #3                  // Field sayHi:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 7: 0
}
SourceFile: "MyClassDemo.java"

 

  1. 一些工具推薦

1、mac 下查看二進制文件

vi -b datafile
:%!xxd

2、二進制工具:Hey Fiend

3、十六進制轉字符串 https://www.bejson.com/convert/ox2str/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章