多读书多实践,勤思考善领悟

Java逆向基础之三.函数

本文于2003天之前发表,文中内容可能已经过时。

本文提到的函数(function)和方法(method)为同一个意思

1. 例子1,方法名的定义

1
2
3
4
5
6
7
public class HalfRandom
{
public static double f()
{
return Math.random()/2;
}
}

编译

1
javac HalfRandom.java

反编译

1
javap -c -verbose HalfRandom.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
...
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反编译器可以从被编译的类文件中修改数据类型。

2. 再看”hello,world!”的例子

1
2
3
4
5
6
7
public class HelloWorld
{
public static void main(String[] args)
{
System.out.println("Hello, World");
}
}

反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
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的引用

3. 再看beep的函数调用(输出计算机报警的蜂鸣声)

1
2
3
4
5
6
7
public class bee
{
public static void main(String[] args)
{
java.awt.Toolkit.getDefaultToolkit().beep();
}
}

反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
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.vuln.cn/7115