Java面试笔记-JVM

修改Java字节码

在OpenJDK里有一个AsmTools项目,用来生成正确的或者不正确的java .class文件,主要用来测试和验证。

AsmTools引入了两种表示.class文件的语法:

  • JASM

    用类似java本身的语法来定义类和函数,字节码指令则很像传统的汇编。

  • JCOD

    整个.class用容器的方式来表示,可以很清楚表示类文件的结构。

重要的是两种语法的文件都是可以和.class互相转换的。

在这里主要使用JASM语法。

生成asmtools.jar

如果你的机器没有安装 Mercurial,请先安装(Mericurial 类似 Git 和 SVN,是一个分布式版本控制系统,使用 Python 编写,OpenJDK 就托管在 Mercurial 平台上)

然后执行以下命令:

// Clone代码
hg clone ://hg.openjdk.java.net/code-tools/asmtools

// 编译
cd asmtools/build
ant

即可得到asmtools.jar文件。

准备一个Example

Foo.java

public class Foo {
 public static void main(String[] args) {
  boolean flag = true;
  if (flag) System.out.println("Hello, Java!");
  if (flag == true) System.out.println("Hello, JVM!");
 }
}

javac Foo.java命令生成Foo.class文件,使用JD-GUI打开内容如下:

import java.io.PrintStream;

public class Foo
{
  public static void main(String[] paramArrayOfString)
  {
    int i = 1;
    if (i != 0) {
      System.out.println("Hello, Java!");
    }
    if (i == 1) {
      System.out.println("Hello, JVM!");
    }
  }
}

java Foo命令运行Foo.class文件输出结果:

Hello, Java!
Hello, JVM!

由class文件生成jasm文件

如下命令将class文件中的内容转换成对应的jasm语法:

java -jar asmtools.jar jdis Foo.class

上面输出的内容会直接打印在终端,当然你也可以把它输入到文件中:

java -jar asmtools.jar jdis Foo.class > Foo.jasm

Foo.jasm 文件内容如下:

super public class Foo
    version 52:0
{


public Method "<init>":"()V"
    stack 1 locals 1
{
        aload_0;
        invokespecial   Method java/lang/Object."<init>":"()V";
        return;
}

public static Method main:"([Ljava/lang/String;)V"
    stack 2 locals 2
{
        iconst_1;
        istore_1;
        iload_1;
        ifeq    L14;
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Hello, Java!";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
    L14:    stack_frame_type append;
        locals_map int;
        iload_1;
        iconst_1;
        if_icmpne   L27;
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Hello, JVM!";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
    L27:    stack_frame_type same;
        return;
}

} // end Class Foo

为了将 Foo.class 中的 int i = 1; 修改成 int i = 2;,我们需要替换 Foo.jasm 文件中的 iconst_1iconst_2

由jasm文件生成class文件

执行如下命令:

java -jar asmtools.jar jasm Foo.jasm

这时再用 JD-GUI 打开 Foo.class 文件,内容如下:

import java.io.PrintStream;

public class Foo
{
  public static void main(String[] paramArrayOfString)
  {
    int i = 2;
    if (i != 0) {
      System.out.println("Hello, Java!");
    }
    if (i == 1) {
      System.out.println("Hello, JVM!");
    }
  }
}

看到 i 的确由 1 变成 2 了。

java Foo 输出内容如下:

Hello, Java!

总结

其实就两个命令:

  • 由 class 文件生成 jasm 文件:java -jar asmtools.jar jdis Foo.class > Foo.jasm
  • 由 jasm 文件生成 class 文件:java -jar asmtools.jar jasm Foo.jasm

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!