F4DE F4DE
首页
技术
笔记
随笔
关于
友链
归档

F4DE

Web Security
首页
技术
笔记
随笔
关于
友链
归档
  • Java安全

    • Tomcat

    • Shiro

    • 字节码

      • 字节码文件结构
        • Magic
        • minor/major_version
        • constantpoolcount
        • constant_pool
        • access_flags
        • this_class
        • super_class
        • interfaces_count
        • interfaces[interfaces_count]
        • fields_count
        • fields[fields_count]
        • methods_count
        • methods[methods_count]
        • attributes_count
        • attributes[attributes_count]
    • 反序列化

  • 技术
  • Java安全
  • 字节码
F4DE
2021-03-04

字节码文件结构

一篇关于Java字节码文件结构的学习笔记。

Chapter 4. The class File Format (oracle.com) (opens new window)

Java的字节码文件格式规定如下:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Class文件由无符号数和表组成:

  • 在JVM的的规范中,u1、u2、u4分别表示1、2、4字节的无符号数
  • 表是由多个无符号数或者其他表作为数据项的复杂数据类型,都习惯以_info结尾

img

下面以这个class文件为例:

interface TestInterface {
    void hello();
}

public class TestClass implements TestInterface {
    private int id;

    public TestClass() {
        this.id = 3;
    }

    @Override
    public void hello() {
        System.out.println("Hello" + id);
    }
}

# Magic

Magic(魔数)是class文件的标识,其有固定值 0xCAFEBABE ,JVM加载class文件的时候会先加载4字节的数据( u4 magic )来判断该文件是不是字节码文件。

image-20210302203842019

# minor/major_version

minor_version和major_version两部分组成了class文件的版本号,两者分别表示副版本号和主版本号,我们常说的Java1.8、Java11表示的就是主版本号,下表是版本号的对应关系:

JDK版本 十进制 十六进制 发布时间
JDK1.1 45 2D 1996-05
JDK1.2 46 2E 1998-12
JDK1.3 47 2F 2000-05
JDK1.4 48 30 2002-02
JDK1.5 49 31 2004-09
JDK1.6 50 32 2006-12
JDK1.7 51 33 2011-07
JDK1.8 52 34 2014-03
Java9 53 35 2017-09
Java10 54 36 2018-03
Java11 55 37 2018-09
Java12 56 38 2019-03
Java13 57 39 2019-09
Java14 58 3A 2020-03
Java15 59 3B 2020-09

image-20210302203933815

# constant_pool_count

常量池计数器 u2 constant_pool_count)表示的是常量池中的数量,其值为常量池中的数量 + 1,需要特别注意的是long和double类型的常量池对象占两个常量位,并且常量池计数是从1开始的。

image-20210302204056277

0X0036也就是54,表示常量池中有53个元素。

# constant_pool

常量池(constant_pool),cp_info constant_pool[constant_pool_count-1]是一种表结构,cp_info表示的是常量池对象。

常量池中主要存放字面量和符号引用:

  • 字面量:文本字符串、被声明为final的常量
  • 符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符

常量池中的每一个元素都是一个表,表的类型共有14种,它们都具有一个共同的特点:表开始的部分都是一个u1的标志位。

下图为常量池中14中数据类型的总表:

img

img

image-20210302211918990

常量池中第一个一个常量的标志为0A,也就是10,表示该常量为CONSTANT_Methodref_info,为类中方法的符号引用,按照上表,可以读出指向方法的类描述符的索引位置为0x0007,指向名称及类型描述符的索引位置为0x0015······

使用jclasslib插件可以快速查看常量池中的常量信息:

image-20210302212752980

# access_flags

访问标志(u2 access_flags)用于识别一些类或者接口层次的访问信息。

标志名 十六进制值 描述
ACC_PUBLIC 0x0001 声明为public
ACC_FINAL 0x0010 声明为final
ACC_SUPER 0x0020 废弃/仅JDK1.0.2前使用,1.0.2之后都为真
ACC_INTERFACE 0x0200 声明为接口
ACC_ABSTRACT 0x0400 声明为abstract
ACC_SYNTHETIC 0x1000 声明为synthetic,表示该class文件并非由Java源代码所生成
ACC_ANNOTATION 0x2000 标识注解类型
ACC_ENUM 0x4000 标识枚举类型

对于例子TestClass来说,它的标志应该是ACC_PUBLIC和ACC_SUPER,所以access_flags的值为0x0001 | 0x0020 = 0x0021。

image-20210302214109941

# this_class

u2 this_class表示的是当前类在常量池中的索引位置(CONSTANT_Class_info)。

image-20210302214614561

# super_class

u2 super_class表示的是当前类的父类在常量池中的索引位置。

# interfaces_count

u2 interfaces_count表示当前类实现的接口数量。

# interfaces[interfaces_count]

u2 interfaces[interfaces_count]表示所有接口在常量池中的索引位置。

image-20210302215302711

# fields_count

u2 fields_count表示类中成员变量的数量。

# fields[fields_count]

field_info fields[fields_count]是成员变量表(字段表)信息,字段表的结构如下:

field_info {
   u2 access_flags; //成员变量访问标志
   u2 name_index; //成员变量名称在常量池中的索引
   u2 descriptor_index; //成员变量的描述符在常量池中的索引
   u2 attributes_count; //成员变量属性数量
   attribute_info attributes[attributes_count]; //成员变量的属性信息
}

成员变量访问标志表:

权限名称 值 描述
ACC_PUBLIC 0x0001 public
ACC_PRIVATE 0x0002 private
ACC_PROTECTED 0x0004 protected
ACC_STATIC 0x0008 static,静态
ACC_FINAL 0x0010 final
ACC_VOLATILE 0x0040 volatile,不可和ACC_FIANL一起使用
ACC_TRANSIENT 0x0080 在序列化中被忽略的字段
ACC_SYNTHETIC 0x1000 由编译器产生,不存在于源代码中
ACC_ENUM 0x4000 enum

image-20210302220418964

  • 0x0002表示字段的访问标志为ACC_PRIVATE
  • 0x0009表示变量名称在常量池中的索引位置为9
  • 0x000A表示变量描述符在常量池中的索引位置为10
  • 0x0000表示属性数量为0

# methods_count

u2 methods_count表示类的方法数量。

# methods[methods_count]

method_info methods[methods_count]是方法表,结构和字段表是一样的。

method_info {
   u2 access_flags; //方法访问标志
   u2 name_index; //方法名称在常量池中的索引
   u2 descriptor_index; //方法的描述符在常量池中的索引
   u2 attributes_count; //方法属性数量
   attribute_info attributes[attributes_count]; //方法的属性信息
}

attribute_info attributes[attributes_count]是一个很复杂的存储结构,存储着各种属性信息:

attribute_info {
   u2 attribute_name_index; //属性名称在常量池中的索引
   u4 attribute_length; //属性长度
   u1 info[attribute_length]; //属性信息,不同属性的结构不同
}

值得注意的是,方法代码逻辑是放在code属性中的,是以Java虚拟机指令的形式存储的。

code属性的结构:

类型 名称 含义
u2 attribute_name_index 属性名称索引
u4 attribute_length 属性长度
u2 max_stack 操作数栈深度的最大值
u2 max_locals 局部变量表所需的存储空间
u4 code_length 字节码长度
u1 code[code_length] 存储字节码指令的一系列字节流
u2 exception_table_length 异常表长度
exception_info exception_table 异常表
u2 attributes_count 属性长度
attribute_info attributes[attributes_count] 属性表

image-20210302223659435

接着来分析code属性:

  • 000D:code属性在常量池中的索引为13
  • 0000 003C:属性长度为60字节
  • 0002:max_stack为2
  • 0001:max_locals为1
  • 0000 000A:code_length为10,表示还有10个字节来存储指令
  • 2AB7 0001 2A06 B500 02B1:存储JVM指令的字节流
  • 0000:没有异常抛出
  • 0002:表示code属性还有2个属性

code属性中主要包含的属性为:LineNumberTable和LocalVariableTable。

LineNumberTable的结构如下:

LineNumberTable_attribute {
    u2 attribute_name_index;        //属性名称索引
    u4 attribute_length;            //属性长度
    u2 line_number_table_length;
    {   u2 start_pc;            //字节码行号
        u2 line_number;         //java源码行号
    } line_number_table[line_number_table_length];
}
  • 000E:属性索引位置在常量池中的索引位置为14

  • 0000 000E:属性长度为14个字节

  • 0003:line_number_table_length为3

  • 0000 000A:前两个字节表示字节码行号,后两个字节表示对应的Java代码行号

  • ······

具体属性结构可以参考:Chapter 4. The class File Format (oracle.com) (opens new window)

# attributes_count

u2 attributes_count表示当前class文件的属性表的元素个数。

# attributes[attributes_count]

Java15中的属性表:

属性名称 章节
ConstantValue Attribute §4.7.2
Code Attribute §4.7.3
StackMapTable Attribute §4.7.4
Exceptions Attribute §4.7.5
InnerClasses Attribute §4.7.6
EnclosingMethod Attribute §4.7.7
Synthetic Attribute §4.7.8
Signature Attribute §4.7.9
SourceFile Attribute §4.7.10
SourceDebugExtension Attribute §4.7.11
LineNumberTable Attribute §4.7.12
LocalVariableTable Attribute §4.7.13
LocalVariableTypeTable Attribute §4.7.14
Deprecated Attribute §4.7.15
RuntimeVisibleAnnotations Attribute §4.7.16
RuntimeInvisibleAnnotations Attribute §4.7.17
RuntimeVisibleParameterAnnotations Attribute §4.7.18
RuntimeInvisibleParameterAnnotations Attribute §4.7.19
RuntimeVisibleTypeAnnotations Attribute §4.7.20
RuntimeInvisibleTypeAnnotations Attribute §4.7.21
AnnotationDefault Attribute §4.7.22
BootstrapMethods Attribute §4.7.23
MethodParameters Attribute §4.7.24
Module Attribute §4.7.25
ModulePackages Attribute §4.7.26
ModuleMainClass Attribute §4.7.27
NestHost Attribute §4.7.28
NestMembers Attribute §4.7.29

属性表是动态的,每一种属性都有自己独有的数据结构,在读取到属性名称之后还要根据属性的不同类型来解析不同属性表中的值。

在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
CommonsBeanUtils

← 在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链 CommonsBeanUtils→

最近更新
01
在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
04-19
02
CommonsBeanUtils
04-19
03
基于Tomcat全局存储进行回显
04-16
更多文章>
Theme by Vdoing | Copyright © 2020-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×