java入坑之字节码

一、概述

1.1简介

java入坑之字节码_第1张图片

.class文件:字节码(bytecode)文件

  • class文件是Java“一次编译,到处运行”的基础
  • class.文件具备平台无关性,由JVM执行
  • 每个class.文件包含了一个类或接口或模块的定义
  • class文件是一个二进制文件,由JVM定义lass文件的规范
  • 任何满足这种规范的class文件都会被JVM加载运行
  • class文件可以由其他语言编译生成,甚至不用程序语言直接生成
  • JDK版本不同,所编译出.class文件略有不同

java入坑之字节码_第2张图片

注意:

1.低版本JDK编译得到的class文件,可在高版本JDK上运行。
2.高版本JDK编译得到的class.文件,不可在低版本JDK上运行。

 1.2class文件构成

Java中的class文件是一种二进制文件格式,包含Java程序的字节码和元数据信息。它由四部分组成:魔数、版本号、常量池和类信息。

java入坑之字节码_第3张图片

采用类似于C语言结构体的结构来表示数据
包括两种数据类型

  • 定长数据:无符号数,u1,u2,u4(分别代表1个字节、2个字节、4个字节的无符号数)
  • 不定长数据:由多个无符号数组成,通常在数据的前面给出其长度

魔数 

魔数是class文件的前四个字节,它的值是0xCAFEBABE,这个值是固定的,用于标识这是一个有效的Java class文件。

Java虚拟机在读取class文件时,会首先读取魔数,用于确定文件的有效性。如果魔数不正确,则虚拟机会拒绝加载该类。

因此,魔数在class文件中起着关键的作用,它可以确保Java虚拟机只加载有效的class文件,而不是任意的二进制文件。

java入坑之字节码_第4张图片 版本号

 每个Java类文件都有一个版本号。版本号由两个整数组成,主版本号和次版本号。主版本号描述了Java编译器使用的类文件格式,其中较新的编译器使用更高的主版本号。次版本号用于向后兼容性。如果更新的编译器仅增加了次版本号,则应该可以使用旧的Java虚拟机来运行该类文件。

java入坑之字节码_第5张图片 常量池

常量池是class文件的一个重要组成部分,用于保存类中使用到的常量、变量、方法和类的信息。

常量池是一个表,它包含了各种不同类型的常量,比如字符串、数字、字段和方法引用等。常量池中的项目都是按照索引编号排列,以1开始计数。

当Java虚拟机加载class文件时,它会先读取常量池中的所有项目,并且将它们放入内存中。然后,在执行字节码时,虚拟机就可以从内存中读取常量池中的项目,进行相关的操作。

常量池是Java虚拟机执行字节码的重要组成部分,因此必须按照规范来使用。在编写Java代码时,开发者必须注意常量池的使用,避免出现不规范的问题。

java入坑之字节码_第6张图片

java入坑之字节码_第7张图片

类索引、父类索引与接口索引集合
java入坑之字节码_第8张图片

字段表

字段表就是其中的一个部分,它记录了类中所有的字段(变量)的信息。字段表中包含了以下内容:

1. 字段数量:记录类中总共有多少个字段。

2. 字段信息:每个字段都有一个对应的字段信息块,其中包含了字段的访问标志、字段的名称、字段的描述符以及与该字段相关的属性信息。

3. 属性信息:对于每个字段,可能会有多个属性信息与之相关,例如常量值、注解等。

字段表的作用是用于描述类中的变量信息,并在类的加载和运行时使用。在类加载时,虚拟机需要读取字段表中的信息,根据其中的描述符和属性信息确定字段的类型、访问权限以及其他相关信息,并为这些字段在内存中分配空间。在运行时,虚拟机需要通过字段表中的信息对字段进行访问,例如获取或修改字段的值。

java入坑之字节码_第9张图片 

方法表

方法表是一个记录了该类中所有方法的数据结构,包括方法的名称、返回类型、参数类型和访问权限等信息。它可以帮助JVM在运行时调用类中的方法。

具体来说,方法表包含以下信息:

  • 方法的访问标志,包括public、private等
  • 方法的名称
  • 方法的参数列表,包括参数类型和参数名称
  • 方法的返回类型
  • 方法的异常表,包括可能抛出的异常和对应的处理程序
  • 方法的代码实现,包括字节码和常量池等

方法表对于Java的运行时来说非常重要,因为它是JVM在执行方法时寻找和调用对应方法的重要依据。同时,方法表也为Java反射提供了重要的支持,在运行时动态获取类的信息和调用其方法。

java入坑之字节码_第10张图片附加属性

java入坑之字节码_第11张图片

1.3反汇编工具javap

java入坑之字节码_第12张图片

1.4字节码指令分类

class文件被JVM加载后,就执行其代码每一个,是一个byte数字,也有一个对应的助记符(opcode)目前总数200多个

 Java字节码指令是Java程序经过编译后生成的中间代码,它是Java虚拟机可以理解和执行的指令集,包含了一系列操作码(opcode)和操作数(operand)。Java字节码指令可以用16进制数表示,每个指令由一个字节(8位)或两个字节(16位)组成。

JVM的指令集是基于栈而不是寄存器-字节码指令控制的是VM操作数栈

下面列出一些常用的Java字节码指令:

1. 加载和存储指令
  * aload: 将引用类型变量从局部变量表中加载到操作数栈中
  * astore: 将操作数栈中的引用类型值存储到局部变量表中
  * iload: 将整型变量从局部变量表中加载到操作数栈中
  * istore: 将操作数栈中的整型值存储到局部变量表中java入坑之字节码_第13张图片

2. 运算指令
  * iadd: 将两个整型值相加
  * imul: 将两个整型值相乘
  * idiv: 将两个整型值相除
  * irem: 计算两个整型值的余数java入坑之字节码_第14张图片

3. 转换指令
  * i2l: 将整型值转换为长整型
  * l2i: 将长整型值转换为整型
  * f2d: 将单精度浮点型值转换为双精度浮点型
  * d2f: 将双精度浮点型值转换为单精度浮点型

4. 控制指令
  * goto: 无条件跳转
  * ifeq: 如果栈顶值为0,则跳转
  * if_icmpeq: 如果两个整型值相等,则跳转
  * return: 返回方法调用处java入坑之字节码_第15张图片

5. 对象创建和操作指令
  * new: 创建一个新的对象
  * dup: 复制栈顶值
  * invokevirtual: 调用对象的实例方法
  * putfield: 给对象的实例变量赋值

这些指令可以组合在一起形成更加复杂的Java程序。

java入坑之字节码_第16张图片java入坑之字节码_第17张图片

1.5字节码操作ASM

java入坑之字节码_第18张图片

 ASM是Java字节码操作框架,可以通过ASM API对Java字节码进行操作、修改和生成。

 ASM API

java入坑之字节码_第19张图片

ASM 核心类

java入坑之字节码_第20张图片

 1.6字节码增强

字节码增强(bytecode enhancement)是指在Java字节码层面上对Java类进行修改和增强,以达到特定的功能或优化目的。字节码增强技术可以在不改变源代码的情况下改变Java程序的行为,它可以用来实现诸如AOP、ORM、代码注入、快速反射、事务管理、安全检测、性能监控等一系列功能。

字节码增强一般涉及两个主要步骤:

1.利用字节码编辑器对Java字节码进行修改和增强;

2.通过类加载器把修改后的Java字节码加载到JVM中运行。

常用的字节码增强工具包括:ASM、Javassist、ByteBuddy等。其中,ASM是最为著名的字节码增强工具,它提供了底层的字节码处理器和访问器,可以实现高度定制化的字节码增强;而Javassist则提供了更为方便的API,可以更快速地实现字节码增强。ByteBuddy则是一个相对较新的字节码增强工具,它基于代码生成的方式实现字节码增强,性能较好,且易于使用。

Java Instrument

java入坑之字节码_第21张图片
java入坑之字节码_第22张图片

java入坑之字节码_第23张图片 

类替换的注意事项

  • 可以修改方法体和常量池
  • 不可以增加、修改成员变量/方法定义
  • 不可以修改继承关系
  • 未来版本还会增加限制条件

1.7字节码的弱点和混淆

java入坑之字节码_第24张图片 

字节码保护

  • 字节码加密
    • 对字节码进行加密,不再遵循VM制定的规范
    • JVM加栽之前,对字节码解密后,再加栽
  • 字节码混淆
    • 被混淆的代码依然遵循VM制定的规范
    • 变量命名和程序流程上进行等效替换,使得程序的可读性变差
    • 代码难以被理解和重用,达到保护代码的效果

ProGuard

java入坑之字节码_第25张图片
java入坑之字节码_第26张图片

java入坑之字节码_第27张图片 

ProGuard注意事项

  • 反射调用类或者方法,可能失败
  • 对外接口的类和方法,不要混淆,否则调用失败
  • 嵌套类混淆,导致调用失败
  • native的方法不要混淆
  • 枚举类不要混淆

 

你可能感兴趣的:(JAVA入坑,java,开发语言)