本文参考 http://www.vuln.cn/7115
本文提到的函数 (function) 和方法 (method) 为同一个意思
例子 1, 方法名的定义
- public class HalfRandom
- {
- public static double f()
- {
- return Math.random()/2;
- }
- }
编译
javac HalfRandom.java
反编译
javap -c -verbose HalfRandom.class
- ...
- major version: 52
- ...
- #2 = Methodref #16.#17 // java/lang/Math.random:()D
- #3 = Double 2.0d
- ...
- #12 = Utf8 ()D
- ...
- #16 = Class #20 // java/lang/Math
- #17 = NameAndType #21:#12 // random:()D
- #18 = Utf8 HalfRandom
- #19 = Utf8 java/lang/Object
- #20 = Utf8 java/lang/Math
- #21 = Utf8 random
- ...
- public static double f();
- descriptor: ()D
- flags: ACC_PUBLIC, ACC_STATIC
- Code:
- stack=4, locals=0, args_size=0
- 0: invokestatic #2 // Method java/lang/Math.random:()D
- 3: ldc2_w #3 // double 2.0d
- 6: ddiv
- 7: dreturn
- LineNumberTable:
- line 5: 0
invokestatic #2 调用常量 #2 定义的函数
函数名定义在常量池的 Methodref 中, 它定义类, 方法名, 方法返回类型
常量 #2 中, 可以看到它是由 #16.#17 拼接,#16 定义了类名 java/lang/Math,#17 是方法名和返回类型名 random:()D
常量 #17 中, 它是由 #21:#12 拼接,#21 定义了方法名 random,#12 定义了返回类型名 ()D
()D 的解释 , 括号内没有东西表示括号内无参数, D 表示返回类型为 double, 如果是 V 则为 void
这种方式
1)JVM 可以检查数据类型的正确性:
2)java 反编译器可以从被编译的类文件中修改数据类型.
再看 "hello,world!" 的例子
- public class HelloWorld
- {
- public static void main(String[] args)
- {
- System.out.println("Hello, World");
- }
- }
反编译
- ...
- major version: 52
- ...
- #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
- #3 = String #18 // Hello, World
- #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
- ...
- #16 = Class #23 // java/lang/System
- #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
- #18 = Utf8 Hello, World
- #19 = Class #26 // java/io/PrintStream
- #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
- ...
- #23 = Utf8 java/lang/System
- #24 = Utf8 out
- #25 = Utf8 Ljava/io/PrintStream;
- #26 = Utf8 java/io/PrintStream
- #27 = Utf8 println
- #28 = Utf8 (Ljava/lang/String;)V
- ...
- public static void main(java.lang.String[]);
- descriptor: ([Ljava/lang/String;)V
- flags: ACC_PUBLIC, ACC_STATIC
- Code:
- stack=2, locals=1, args_size=1
- 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #3 // String Hello, World
- 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 8: return
- LineNumberTable:
- line 5: 0
- line 6: 8
getstatic #2 获取 System.out 的引用入栈
ldc #3 将字符串 Hello, World 入栈
invokevirtual #4 调用 println() 方法, 这里需要传两个参数, 参数从栈中获取, 1 先将 Hello, World 出栈传入 (Ljava/lang/String;),2 将 System.out 的引用出栈传入 java/io/PrintStream 的引用
再看 beep 的函数调用 (输出计算机报警的蜂鸣声)
- public class bee
- {
- public static void main(String[] args)
- {
- java.awt.Toolkit.getDefaultToolkit().beep();
- }
- }
反编译
- ...
- major version: 52
- ...
- public static void main(java.lang.String[]);
- descriptor: ([Ljava/lang/String;)V
- flags: ACC_PUBLIC, ACC_STATIC
- Code:
- stack=1, locals=1, args_size=1
- 0: invokestatic #2 // Method java/awt/Toolkit.getDefaultToolkit:()Ljava/awt/Toolkit;
- 3: invokevirtual #3 // Method java/awt/Toolkit.beep:()V
- 6: return
- LineNumberTable:
- line 5: 0
- line 6: 6
- invokestatic #2 调用 java.awt.Toolkit.getDefaultToolkit() 方法并将返回结果的引用压入栈顶
- invokevirtual #3 调用 beep() 函数, 其中需要传引用, 把 objectref 从栈顶弹出传入 java/awt/Toolkit
来源: http://www.bubuko.com/infodetail-2568174.html