如何阅读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/

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