JVM8-Class文件格式解读及其修改

本文是以The Java® Virtual Machine Specification Java SE 8 Edition 文档中的内容为依据编写,算得上是一篇学习日记,如有错误,恳请指正。

0.一个简单的java类

public class TestC{
    private int cc = 13;
    public void printCC(){
        System.out.println(cc);
    }
}

下面的图是将上面的ava文件编译后的class文件的16进制内容,(使用的工具是Hex Editor)
记这个图为


图-0
JVM8-Class文件格式解读及其修改_第1张图片
下面的内容是使用 javap -verbose TestC命令生成的辅助内容,摘取了和class文件相关的内容贴出,记为 表-1(虽然是截图~)
JVM8-Class文件格式解读及其修改_第2张图片

图-0和表-0对后面的class文件格式解读比较重要,这里先贴出,后面会使用到这里的案例文件来辅助理解

1.Class文件格式图

下面就正式的进入Class文件的格式说明了

1.1Class文件基本信息


表-1.1

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];
}

简要说明;u1表示表示1个字节,u2表示2个字节,u4表示4个字节;class文件的内容排列就如同上表所示

1.1.1MagicNum-魔法数字

从表上看,class文件的前4个字节是一个magic,其实是一个标记,标记这个文件是一个class文件,这是class文件的安全验证之一(也就是说可以吧.class文件的后缀修改~~,只要不改这个标记就可以被虚拟机识别),这个magic有一个固定值 CAFEBABE(查看图-0);使用这个值据说是和某个coffee品牌有关~

1.1.2版本信息

表-1.1后面的四个字节记录的是class文件的版本号,这个class文件的版本号是0x00000034;minor_version是0,major_version是52,如果虚拟机的版本低于52,那么虚拟机会拒绝执行

1.1.3其他信息

表-1.1版本信息后面的两个字节表示常量表常量的数量。图-0中的值为0x001E;也就是说TestC.class文件的常量表中有30个常量,但是看表-0,Constant pool的数量却只有29个,这是为什么呢?这是设计者考虑到有这样一类值,不引用任何一个常量池项目,所以把0位置空出来,让这类值指向这个位置,所以常量池的技术是从1开始的!有别于数组等计数方式!!!
常量数

常量池之后就是类的结构信息,类的AccessFlag(public private static final 等修饰),其父类,实现的接口等信息
之后就是field和method,以及sourcefile等属性信息
这里对其他的信息做个简单的概括,后面会详细分析

1.2常量表

常量表格式,通用的格式如下
All constant_pool table entries have the following general format:

 cp_info {
    u1 tag;
    u1 info[]; 
    }

一个u1长度的tag表示这个常量的类型;
info的格式依据不同的常量类型而有所不同,但都可以用一个info数组来表示。
下表(表-1.2)展示tag值及其对应的常量类型


表-1.2

Constant Type value
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

1.2.1 CONSTANT_Methodref_info格式

我们先看Method这种常量类型的info格式

CONSTANT_Methodref_info {
       u1 tag;
       u2 class_index;
       u2 name_and_type_index;
   }

这是一个方法的info

tag 的值为10,
class_index是常量表中的位置,是一个class类型的值,表示这个方法所属于的类;
name_and_type_index,是方法的声明,包括方法名,方法参数和返回值。

看案例里的常量表里的第一个常量就是一个方法info;
先从图-0里看,其方法的字节为 0x0A00060010
CONSTANT_Methodref_info

tag = 0x0A; 10
class_index = 0x0006; // 值6,即指向了常量表的6号常量
name_and_type_index = 0x0010;// 值 16,指向常量表16号常量

我们看(表-0)里常量表的6 和16,发现6是一个class 其值指向 23(23是一个utf-8,值为java/lang/Object),说明类是java/lang/Object,
常量表中(从表-0看)的16号常量是NameAndType类型的,其值指向常量表的9和10
9和10是utf-8常量,值分别为 < init >和 ()V

从表-0里看后面的注释就很容易知道,这个方法具体是哪个方法
CONSTANT_Methodref_info
这第一个常量是一个方法,是类java/lang/Object.的,方法名”< init >”:签名()V

后面的其他常量格式我就不一一分析了,这里只是贴出其格式及其说明()

1.2.2 CONSTANT_Class_info格式

class属性常量

 CONSTANT_Class_info {
       u1 tag;
       u2 name_index;
   } 

tag的值为7,name_index的值是这个class所在常量表的位置

1.2.3 CONSTANT_Fieldref_info

字段属性常量 tag = 0x09

   CONSTANT_Fieldref_info {
       u1 tag;
       u2 class_index;
       u2 name_and_type_index;
   }

1.2.4 CONSTANT_InterfaceMethodref_info

接口方法属性常量 tag = 0x0B

 CONSTANT_InterfaceMethodref_info {
       u1 tag;
       u2 class_index;
       u2 name_and_type_index;
   }
The class_index item of a CONSTANT_Methodref_info structure must be a class type, not an interface type.
The class_index item of a CONSTANT_InterfaceMethodref_info structure must be an interface type.

这里需要注意的是CONSTANT_InterfaceMethodref_info里的class_index必须是个interface类型~,CONSTANT_Methodref_info的class_index必须是class类型;

1.2.5 CONSTANT_String_info

String类型常量 tag = 0x08;

  CONSTANT_String_info {
       u1 tag;
       u2 string_index;
   }

1.2.6 CONSTANT_Integer_info

tag = 0x03

 CONSTANT_Integer_info {
         u1 tag; 
         u4 bytes; 
    }

1.2.7 CONSTANT_Float_info

tag = 0x04

 CONSTANT_Float_info {
         u1 tag; 
         u4 bytes; 
    }

1.2.8CONSTANT_Long_info

tag = 0x05

   CONSTANT_Long_info {
       u1 tag;
       u4 high_bytes;
       u4 low_bytes;
   }

1.2.9 CONSTANT_Double_info

tag = 0x06

   CONSTANT_Double_info {
       u1 tag;
       u4 high_bytes;
       u4 low_bytes;
   }

注:CONSTANT_Long_info和CONSTANT_Double_info是有一些坑的,这里暂不填,有兴趣的可以去查看jvm8的技术文档

1.2.10 CONSTANT_NameAndType_info

tag = 0x0C ;通常是对方法和常量的声明说明

   CONSTANT_NameAndType_info {
       u1 tag;
       u2 name_index;
       u2 descriptor_index;
   }

1.2.11 CONSTANT_Utf8_info

tag = 0x01 ;这应该是常量表里最常见的了

   CONSTANT_Utf8_info {
       u1 tag;
       u2 length;
       u1 bytes[length];
   }

这里也有个小坑,从’\u0001’ to ‘\u007F’ 可以用单个字节表示就好,但是在 ‘\u0080’ to ‘\u07FF’的字符就必须使用2个字节表示

x: 1 1 0 bits 10 - 6
y: 1 0 bits 5-0

最后的值为:
((x & 0x1f) << 6) + (y & 0x3f)

当然还有更变态的字符需要用3个字节表示的:’\u0800’ to ‘\uFFFF’

x: 1 1 1 0 bits 15 - 12
y: 1 0 bits 11-6
z: 1 0 bits 5-0

其值为
((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)

最后还有超变态的字符U+FFFF (so-called supplementary characters) ,需要使用6个字节来表示,这就不具体解释了,平常基本不会用到==!

1.2.12 CONSTANT_MethodHandle_info

tag = 0x0F;

   CONSTANT_MethodHandle_info {
       u1 tag;
       u1 reference_kind;
       u2 reference_index;
   }

1.2.13 CONSTANT_MethodType_info

tag = 0x10;

   CONSTANT_MethodHandle_info {
       u1 tag;
       u2 descriptor_index;
   }

1.2.14 CONSTANT_InvokeDynamic_info

tag = 0x10;

  CONSTANT_InvokeDynamic_info {
       u1 tag;
       u2 bootstrap_method_attr_index;
       u2 name_and_type_index;
   }

1.3 class类描述

当前类的属性,常量池结束后,后面的两个字节表示访问标志:这个类是否是一个接口,public private,是否abstract,是否final等具体如下表(表-1.3)


表-1.3

标志名称 含义
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_FINAL 0x0010 Declared final; no subclasses allowed.
ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the invokespecial instruction. JDK1.0.2之后编译出来的这个值都为true
ACC_INTERFACE 0x0200 Is an interface, not a class.
ACC_ABSTRACT 0x0400 Declared abstract; must not be instantiated.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.(这个类并不是用户代码产生)
ACC_ANNOTATION 0x2000 Declared as an annotation type.
ACC_ENUM 0x4000 Declared as an enum type.

我们可以看到TestC的类的访问标志为 0x0021;说明是一个public 的类(我们反过来推导,依据这个类是个public的类,没有其他的访问标志,那么其访问标志依据上表得出是0021,在图0中搜索0021就知道常量表在哪里结束了,当然读者也可以一个个常量的查询过去直到常量表结束–!)
关于类的描述这里就不过多解释了,看下图(当前类是5,查看表-0,得知是com/pxr/jvm/TestC, 父类是 6,查看表-0,得知是java/lang/Object)
JVM8-Class文件格式解读及其修改_第3张图片

1.4字段表

字段表的格式如下:

   field_info {
       u2             access_flags;
       u2             name_index;
       u2             descriptor_index;
       u2             attributes_count;
       attribute_info attributes[attributes_count];
}

其中access_flags的属性值如表-1.4


表-1.4

标志名称 含义
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 Declared private; usable only within the defining class.
ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 Declared static.
ACC_FINAL 0x0010 Declared final; never directly assigned to after object construction (JLS §17.5).
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
ACC_TRANSIENT 0x0080 Declared transient; not written or read by a persistent object manager.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.(是否是编译器自动生成的)
ACC_ENUM 0x4000 Declared as an element of an enum.

name_index和descriptor_index则指向常量表中的某个值
这有个属性attribute_info,可见后面属性表的(1.6)分析
看当前案例中的字段属性值:


图-1.4

图-1.4

说明当前类中只有一个属性,其access_flag为0x0002(看表-1.4得知为 private),name为常量表中7,描述为常量表中的8;
查询表-0;可知这个属性是 private int cc; (I 就是int )

1.5 方法表

方法的格式如下


表-1.5

  method_info {
       u2             access_flags;
       u2             name_index;
       u2             descriptor_index;
       u2             attributes_count;
       attribute_info attributes[attributes_count];
}

其中access_flags的属性值如表-1.5


表-1.5.1

标志名称 含义
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 Declared private; accessible only within the defining class.
ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 Declared static.
ACC_FINAL 0x0010 Declared final; must not be overridden (§5.4.5).
ACC_SYNCHRONIZED 0x0020 Declared synchronized; invocation is wrapped by a monitor use.
ACC_BRIDGE 0x0040 A bridge method, generated by the compiler.
ACC_VARARGS 0x0080 Declared with variable number of arguments.方法是否接受不定参数
ACC_NATIVE 0x0100 Declared native; implemented in a language other than Java.
ACC_ABSTRACT 0x0400 Declared abstract; no implementation is provided.
ACC_STRICT 0x0800 Declared strictfp; floating-point mode is FP- strict.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.方法是否是由编译器产生的

看图-1.4,字段表后面就是方法表,第一个u2表示方法个数,0x0002说明有两个方法(很显然,TestC类中有一个默认的构造方法和printCC方法)

我们看第一个方法,截图中的灰色背景都是这个方法相关的,下面一个个解释


图-1.5
JVM8-Class文件格式解读及其修改_第4张图片
依据表-1.5

属性 说明
access_flags 0x0001 查看表-1.5.1,这是个public的方法
name_index 0x0009 方法的名称在常量表中指向第9个常量,其值为< init >,说明是构造方法~~
descriptor_index 0x000A 方法的说明指向常量表中的第10个常量,方法的描述 ()V
attributes_count 0x0001 该方法一个属性,0x000B是属性的类型,这是个code属性(详见1.6属性表),其长度为 0x00000027,即code属性长度为39个字节

1.6 属性表

属性表的通用格式如下


表-1.6

  attribute_info {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 info[attribute_length];
}

我们先看code属性表

1.6.1code属性表

code属性表也算比较复杂,其格式如下


表-1.6.1

 Code_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 max_stack;
       u2 max_locals;
       u4 code_length;
       u1 code[code_length];
       u2 exception_table_length;
       {   u2 start_pc;
           u2 end_pc;
           u2 handler_pc;
           u2 catch_type;
       } exception_table[exception_table_length];
       u2 attributes_count;
       attribute_info attributes[attributes_count];
}

注意表-1.6中是一个属性表的统一表现形式。
表-1.6中的

u1 info[attribute_length];

相当于 code 属性表中的

u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{   u2 start_pc;
    u2 end_pc;
    u2 handler_pc;
    u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];

下面我们就以案例代码分析一下code属性表


图-1.6
JVM8-Class文件格式解读及其修改_第5张图片
按照表1.6.1的规则 构造方法中max_stack为2,max_locals为1,code_length为 0x0000000B = 10 个字节,code 代码为 2AB700012A100DB50002B1;后面的2个字节 0000 说明code属性表中没有异常表, 0001说明code属性表中还有一个属性,该属性的name_index 为000C(查看表0得知为LineNumberTable属性,该属性长度为0x0000000A,属性为 00020000 00030004 0005)

所有属性里以code属性最为复杂,所以这里就只分析这个code属性,其他属性把格式列出,以供参考

1.6.2 ConstantValue属性

常量属性

ConstantValue_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 constantvalue_index;
}

1.6.3 StackMapTable属性

StackMapTable_attribute {
       u2              attribute_name_index;
       u4              attribute_length;
       u2              number_of_entries;
       stack_map_frame entries[number_of_entries];
}

1.6.4 Exceptions属性

   Exceptions_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 number_of_exceptions;
       u2 exception_index_table[number_of_exceptions];
}

1.6.5 InnerClasses属性

  InnerClasses_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 number_of_classes;
       {   u2 inner_class_info_index;
           u2 outer_class_info_index;
           u2 inner_name_index;
           u2 inner_class_access_flags;
       } classes[number_of_classes];
   }

inner_class_access_flags的对应值表如下

属性 说明
ACC_PUBLIC 0x0001 Marked or implicitly public in source.
ACC_PRIVATE 0x0002 Marked private in source.
ACC_PROTECTED 0x0004 Marked protected in source.
ACC_STATIC 0x0008 Marked or implicitly static in source.
ACC_FINAL 0x0010 Marked final in source.
ACC_INTERFACE 0x0200 Was an interface in source.
ACC_ABSTRACT 0x0400 Marked or implicitly abstract in source.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
ACC_ANNOTATION 0x2000 Declared as an annotation type.
ACC_ENUM 0x4000 Declared as an enum type.

1.6.6 EnclosingMethod属性

  EnclosingMethod_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 class_index;
       u2 method_index;
   }

1.6.7 Synthetic属性

  Synthetic_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
}

1.6.8 Signature属性

  Signature_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 signature_index;
}

1.6.9 SourceFile属性

SourceFile_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 sourcefile_index;
}  

1.6.10 SourceDebugExtension属性

  SourceDebugExtension_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 debug_extension[attribute_length];
}

1.6.11 LineNumberTable 属性

LineNumberTable_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 line_number_table_length;
       {   u2 start_pc;
           u2 line_number;
       } line_number_table[line_number_table_length];
}

1.6.12 LocalVariableTable属性

     LocalVariableTable_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 local_variable_table_length;
       {   u2 start_pc;
           u2 length;
           u2 name_index;
           u2 descriptor_index;
           u2 index;
       } local_variable_table[local_variable_table_length];
   }

1.6.13 LocalVariableTypeTable属性

 LocalVariableTypeTable_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 local_variable_type_table_length;
       {   u2 start_pc;
           u2 length;
           u2 name_index;
           u2 signature_index;
           u2 index;
       } local_variable_type_table[local_variable_type_table_length];
   }  

1.6.14 Deprecated属性

 Deprecated_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
}

1.6.15 RuntimeVisibleAnnotations属性

 RuntimeVisibleAnnotations_attribute {
       u2         attribute_name_index;
       u4         attribute_length;
       u2         num_annotations;
       annotation annotations[num_annotations];
}  
annotation的属性列表
annotation {
       u2 type_index;
       u2 num_element_value_pairs;
       {   u2            element_name_index;
           element_value value;
       } element_value_pairs[num_element_value_pairs];
}

衍生的 element_value属性格式
   element_value {
       u1 tag;
       union {
           u2 const_value_index;
           {   u2 type_name_index;
               u2 const_name_index;
           } enum_const_value;
           u2 class_info_index;
           annotation annotation_value;
           {   u2            num_values;
               element_value values[num_values];
           } array_value;
       } value;
}

1.6.16 RuntimeInvisibleAnnotations属性

  RuntimeInvisibleAnnotations_attribute {
       u2         attribute_name_index;
       u4         attribute_length;
       u2         num_annotations;
       annotation annotations[num_annotations];
}

1.6.17 RuntimeVisibleParameterAnnotations属性

 RuntimeVisibleParameterAnnotations_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 num_parameters;
       {   u2         num_annotations;
           annotation annotations[num_annotations];
       } parameter_annotations[num_parameters];
   }

1.6.18 RuntimeInvisibleParameterAnnotations属性

RuntimeInvisibleParameterAnnotations_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 num_parameters;
       {   u2         num_annotations;
           annotation annotations[num_annotations];
       } parameter_annotations[num_parameters];
   }

1.6.19 RuntimeVisibleTypeAnnotations属性

  RuntimeVisibleTypeAnnotations_attribute {
       u2              attribute_name_index;
       u4              attribute_length;
       u2              num_annotations;
       type_annotation annotations[num_annotations];
}

1.6.20 RuntimeInvisibleTypeAnnotations属性

 RuntimeInvisibleTypeAnnotations_attribute {
       u2              attribute_name_index;
       u4              attribute_length;
       u2              num_annotations;
       type_annotation annotations[num_annotations];
}

1.6.21 AnnotationDefault属性

1.6.22 BootstrapMethods属性

 BootstrapMethods_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u2 num_bootstrap_methods;
       {   u2 bootstrap_method_ref;
           u2 num_bootstrap_arguments;
           u2 bootstrap_arguments[num_bootstrap_arguments];
       } bootstrap_methods[num_bootstrap_methods];
}

1.6.23 MethodParameters属性

 MethodParameters_attribute {
       u2 attribute_name_index;
       u4 attribute_length;
       u1 parameters_count;
       {   u2 name_index;
           u2 access_flags;
       } parameters[parameters_count];
   }

基本上这些属性很多都不会用到,在本文中,需要使用的就是code属性。

2.修改class文件

这一步的目的就是修改class文件,当调用TestC的printCC方法时,不仅仅打印出13这个数字,还要打印出另一个字符串。

2.1修改常量表

修改常量表要注意两点:

1.修改常量表头数量值
2.新增的常量从常量表尾部添加,否则容易出错

2.1.1往常量表中添加一个字符串 “aa”

需要注意的是我们加入的是一个String类型的常量,但是看1.2.5 CONSTANT_String_info结构的时候发现,

string_index
The value of the string_index item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Utf8_info structure (§4.4.7) representing the sequence of Unicode code points to which the String object is to be initialized.

string_index 它是指向常量表的一个位置,实际上所有的字符串在常量表中都是以 utf-8存储的。
所以往常量表里添加字符串至少需要添加两个常量,一个String类型,一个Utf-8类型
String类型的值 08 001F;(因为当前的常量表里已经有了29个常量了,所以这个String类型的常量是第30个,而存储这个String的值的Utf-8类型常量的常量池编号就是31 也就是 0x001F)
utf-8类型的值 01 0002 6161 (01类型,0002 长度,6161 值)查询asc-2码表 a对应的16进制值是 0x61

修改后的TestC.class文件内容如下


图-2.1
JVM8-Class文件格式解读及其修改_第6张图片

运行javap -verbose TestC 查看class文件内容发现,aa字符串已经加入到常量表里了


图-2.2
JVM8-Class文件格式解读及其修改_第7张图片

2.2 修改code属性

1.修改code属性表中的 attribute_length和code_length
2.编写需要加入的代码( System.out.println("aa") )的字节码

2.2.1虚拟机字节码指令集

我们按照code属性表那一节的方法先把printCC的核心code字节码挖出来,如下图所示
JVM8-Class文件格式解读及其修改_第8张图片
实际上printCC的执行字节码就是

B20003 2AB40002 B60004B1

查询字节码指令集
B2:获取指定类的静态域,并将其压入栈顶,
所以 B2 0003就是获取常量表中编号3的常量,查询图-2.2发现就是 java/lang/System.out:这个静态域

2A:将第一个引用类型本地变量推送到栈顶

B4 0002:获取0002的值,并将其压入栈顶,查询图-2.2 常量表0002的值就是 cc 字段

B6 0004:B6 调用实例方法(0004,println方法)
B1: 返回

所以B20003 2AB40002 B60004B1这段字节码就是 System.out.println(cc);

当然也可以通过javap -verbose TestC 查看详细:
JVM8-Class文件格式解读及其修改_第9张图片

2.2.2编写字节码

编写System.out.println(cc)字节码:
实际上把 B20003 2AB40002 B60004 直接拿过来,把 B4 0002 压入栈顶的值替换成我们的String 0x001E 就行了,不是么?

B200032AB4001EB60004

我们把这段代码加入之后,注意还需要修改code 和 attribute的长度 ,这段字节码长度是 10(B1是返回,所以不需要重复加入),所以做如下修改
JVM8-Class文件格式解读及其修改_第10张图片

运行发现出错了。。

仔细检查发现,这里调用的是println(int )方法,所以还需要在常量池中吧这个方法给声明出来,这一步暂时留给读者完成
JVM8-Class文件格式解读及其修改_第11张图片

3 后续篇

上节说到的一个错误,这节来修正了
1.需要加入字符串
2.需要加入方法
3.插入代码

这3个步骤我就不重复了,插入的代码需要查询虚拟机字节码指令表
插入代码这块需要有个坑
jvm8对每个成员方法都加入了localVariableTable这个属性,记录每个变量的作用域
通常会有个this指针,作用域是方法的长度,所以修改了方法长度,这个长度也要同步修改,具体修改参见下图(注:因昨日生成的文件未保存,所以这个新的class文件是使用jvm8编译的,内容可能不大一样)
JVM8-Class文件格式解读及其修改_第12张图片
图中,常量表的修改我就不标注了,因为比较简单。

运行结果:
JVM8-Class文件格式解读及其修改_第13张图片

大功告成!

你可能感兴趣的:(jvm)