1. 程式人生 > >Java逆向系列-基礎指令:函式

Java逆向系列-基礎指令:函式

例子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

本文參考:逆向工程權威指南.下冊.pdf 和http://blog.51cto.com/7317859/2105269