《深入理解Java虚拟机》-(实战)练习修改class文件

这是一篇修改class文件的文章。注释并不完全,要抓住这次练习的目的:

boolean在虚拟机中是以何种方式解读的

好的,开始我的表演

1.安装asmtools.jar(本文尾部有步骤)

2.编写一个java文件,并编译,执行  2.1 Foo.java
《《深入理解Java虚拟机》-(实战)练习修改class文件》
《《深入理解Java虚拟机》-(实战)练习修改class文件》

 1 public class Foo {
 2   public static void main(String[] args) {
 3     boolean flag = true;
 4     if (flag)   {
 5         System.out.println("Hello, Java!");
 6     }
 7     if (flag == true) {
 8         System.out.println("Hello, JVM!");
 9     }
10   }
11 }       

View Code

 

2.2 编译并运行

[root@localhost tmp]# javac Foo.java
[root@localhost tmp]# java Foo
Hello, Java!
Hello, JVM!

 

3.查看编译后的java文件,class (注意看黄色部分的变化)

《《深入理解Java虚拟机》-(实战)练习修改class文件》
《《深入理解Java虚拟机》-(实战)练习修改class文件》

[root@localhost tmp]# javap -verbose Foo
Classfile /usr/local/asmtools-7.0-build/binaries/lib/tmp/Foo.class
  Last modified Aug 12, 2019; size 493 bytes
  MD5 checksum d51944604c5b4e45cb895501910347ea
  Compiled from "Foo.java"
public class Foo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #18.#19        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #20            // Hello, Java!
   #4 = Methodref          #21.#22        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #23            // Hello, JVM!
   #6 = Class              #24            // Foo
   #7 = Class              #25            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               StackMapTable
  #15 = Utf8               SourceFile
  #16 = Utf8               Foo.java
  #17 = NameAndType        #8:#9          // "<init>":()V
  #18 = Class              #26            // java/lang/System
  #19 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #20 = Utf8               Hello, Java!
  #21 = Class              #29            // java/io/PrintStream
  #22 = NameAndType        #30:#31        // println:(Ljava/lang/String;)V
  #23 = Utf8               Hello, JVM!
  #24 = Utf8               Foo
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (Ljava/lang/String;)V
{
  public Foo();
    descriptor: ()V
    flags: 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 1: 0


  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1    
         0: iconst_1                    //常量1入栈
         1: istore_1                    //把栈顶值存储到局部变量表下标为1的位置,即flag =1;  
         2: iload_1                     //取局部变量表中下标为1的变量压栈
         3: ifeq          14            //(jump if i == 0) 将栈顶值与0比较,如果相等,则跳入14步骤。
         6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #3                  // String Hello, Java!
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: iload_1                     //取局部变量表中下标为1的变量压栈
        15: iconst_1                    //常量1入栈
        16: if_icmpne     27            //比较栈顶两个值,如果不相等,则跳转到27   
        19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: ldc           #5                  // String Hello, JVM!
        24: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 6
        line 7: 14
        line 8: 19
        line 10: 27
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 14
          locals = [ int ]
        frame_type = 12 /* same */
}
SourceFile: "Foo.java"

View Code

 

4.使用asmtools.jar修改class文件

[root@localhost tmp]# java -jar ../asmtools.jar jdis Foo.class > Foo.jasm.1
[root@localhost tmp]# ls
Foo.class  Foo.jasm.1  Foo.java
[root@localhost tmp]# java -cp ../asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
[root@localhost tmp]# awk 'NR==1,/iconst_1/{sub(/iconst_1/,"iconst_2")} 1' Foo.jasm.1 > Foo.jasm
[root@localhost tmp]# ls
Foo.class  Foo.jasm  Foo.jasm.1  Foo.java
[root@localhost tmp]# java Foo
Hello, Java!
Hello, JVM!

 

5.再次编译,执行

[root@localhost tmp]# java -jar  ../asmtools.jar jasm Foo.jasm
[root@localhost tmp]# java Foo
Hello, Java!
[root@localhost tmp]# ls
Foo.class  Foo.jasm  Foo.jasm.1  Foo.java

 

6.查看修改后的class文件

[root@localhost tmp]# javap -verbose Foo
Classfile /usr/local/asmtools-7.0-build/binaries/lib/tmp/Foo.class
  Last modified Aug 12, 2019; size 431 bytes
  MD5 checksum 18cfb8b8b7d9d49e9ffce213e70c8898
  Compiled from "Foo.jasm"
public class Foo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = String             #10            // Hello, Java!
   #2 = String             #11            // Hello, JVM!
   #3 = Fieldref           #27.#12        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #8.#17         // java/lang/Object."<init>":()V
   #5 = Methodref          #13.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Utf8               (Ljava/lang/String;)V
   #7 = Utf8               out
   #8 = Class              #9             // java/lang/Object
   #9 = Utf8               java/lang/Object
  #10 = Utf8               Hello, Java!
  #11 = Utf8               Hello, JVM!
  #12 = NameAndType        #7:#23         // out:Ljava/io/PrintStream;
  #13 = Class              #15            // java/io/PrintStream
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               java/io/PrintStream
  #16 = Utf8               main
  #17 = NameAndType        #29:#20        // "<init>":()V
  #18 = Utf8               SourceFile
  #19 = Utf8               println
  #20 = Utf8               ()V
  #21 = Utf8               StackMapTable
  #22 = Utf8               Foo.jasm
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               Code
  #25 = Class              #26            // Foo
  #26 = Utf8               Foo
  #27 = Class              #28            // java/lang/System
  #28 = Utf8               java/lang/System
  #29 = Utf8               <init>
  #30 = NameAndType        #19:#6         // println:(Ljava/lang/String;)V
{
  public Foo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #4                  // Method java/lang/Object."<init>":()V
         4: return


  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_2                     //载入常量2入栈
         1: istore_1                     //把栈顶值存储到局部变量表下标为1的位置,即flag =2;
         2: iload_1                        //取局部变量表中下标为1的变量压栈
         3: ifeq          14
         6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #1                  // String Hello, Java!
        11: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: iload_1                    //取局部变量表中下标为1的变量压栈
        15: iconst_1                   //常量1入栈
        16: if_icmpne     27           //比较栈顶两个值,如果不相等,则跳转到27
        19: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: ldc           #2                  // String Hello, JVM!
        24: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 14
          locals = [ int ]
        frame_type = 12 /* same */
}
SourceFile: "Foo.jasm"

  

 

附:asmtools.jar的安装(centos 6/7)

0.先cd进入需要安装到的目录地址  1. Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现。

yum install hg

2. 版本是2.6.2,发现不是最新版,去官方下载centos 7最新版3.9.2

wget https://www.mercurial-scm.org/release/centos7/RPMS/x86_64/mercurial-3.9.2-1.x86_64.rpm

3.升级2.6.2 到3.9.2 (centos 6 我就没有用这一步。。)

rpm -Uvh mercurial-3.9.2-1.x86_64.rpm

4.安装ASMTOOLS.jar

hg clone http://hg.openjdk.java.net/code-tools/asmtools/ asmtools
cd asmtools/build/
yum install ant
ant //编译生成asmtools.jar

5.生成的jar位置:

[root@localhost lib]# ls
asmtools.jar
[root@localhost lib]# pwd
/usr/local/asmtools-7.0-build/binaries/lib

  

  

  

  

 

点赞