如何反编译.class文件及阅读java指令

时间:2022-12-08 19:58:56

有时候需要通过阅读java指令来学习java的某些语法的处理过程,因此就需要反编译.class文件。

java文件通过编译后生成.class,如果需要阅读java文件对应的java虚拟机指令,则需要通过javap来反编译.class文件。

比如,有一段代码如下:

public class Main {

public static void main(String[] args) {
if ((Boolean)getBoolean()) {
System.out.println("no exception");
}
}

private static Object getBoolean() {
return null;
}
}

经过编译后,生成Main.class文件,然后通过以下命令:

javap -c -p Main.class

可以得到以下结果:

public class com.kevin.TestJava.Main {
public com.kevin.TestJava.Main();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: invokestatic #16 // Method getBoolean:()Ljava/lang/Object;
3: checkcast #20 // class java/lang/Boolean
6: invokevirtual #22 // Method java/lang/Boolean.booleanValue:()Z
9: ifeq 20
12: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #32 // String no exception
17: invokevirtual #34 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return

private static java.lang.Object getBoolean();
Code:
0: aconst_null
1: areturn
}

下面拿main方法的汇编码来举例说明如何阅读java汇编码

{
0: invokestatic #16 // Method getBoolean:()Ljava/lang/Object;
}

通过查询java虚拟机指令集,得到关于invokestatic的解释:invokestatic的作用是调用类的静态方法。

文档中对应每个指令都有详细解释,以下解释下各个字段的含义:

Format
invokestatic
indexbyte1
indexbyte2

format表示指令的格式,比如:invokestatic后面两个字节,两个字节的含义可在Description中找到解释:

The unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class, where the value of the index is (indexbyte1 << 8) | indexbyte2.

所以,invokestatic指令后面的两个字节是用来构造常量池的索引(反汇编中的#16就是计算出来的索引,后面的// Method getBoolean:()Ljava/lang/Object;就是这个索引所对应的内容,此处是指向的一个Method,名字是getBoolean:()Ljava/lang/Object

Operand Stack
..., [arg1, [arg2 ...]] →
...

Operand Stack表示这个指令如何依赖栈及影响栈

  • 第一行,表示这个指令在栈中可能会0个或者多个参数,并且执行完这个指令后,该栈上的值就会被弹出。箭头代表栈的生长方向。
  • 第二行,表示这个指令会在栈上新增的内容。
Linking Exceptions  
...
Run-time Exceptions
...

这种Exceptions表示该指令在运行时,可能会抛出的异常类型。