Java逆向基礎之操作數棧
本地變量和操作數棧
本地變量數組(Local Variable Array)
本地變量的數組包括方法執行所需要的所有變量,包括 this 的引用,所有方法參數和其他本地定義的變量。對於那些方法(靜態方法 static method)參數是以零開始的,對於實例方法,零為 this 保留。
所有的類型都在本地變量數組中占一個槽(entry),而 long 和 double 會占兩個連續的槽,因為它們有雙倍寬度(64-bit 而不是 32-bit)。
操作數棧(Operand Stack)
操作數棧在執行字節碼指令的時候使用,它和通用寄存器在 native CPU 中使用的方式類似。大多數 JVM 字節碼通過 pushing,popping,duplicating,swapping,或生產消費值的操作使用操作數棧。
看一個運算的例子
public class calc { public static int half(int a) { return a/2; } }
編譯
javac calc.java
反編譯
javap -c -verbose calc.class
反編譯結果
... major version: 52 ... public static int half(int); descriptor: (I)I flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: iload_0 1: iconst_2 2: idiv 3: ireturn LineNumberTable: line 5: 0
iload_0 第0個變量(即變量a)壓入操作數棧
+-------+ | stack | +-------+ | a | +-------+
iconst_2 將2壓入操作數棧
+-------+ | stack | +-------+ | 2 | | a | +-------+
idiv 操作數棧中的前兩個int相除,並將結果壓入操作數棧頂
+-------+ | stack | +-------+ | result| +-------+
ireturn 返回棧頂元素
例子2,復雜一點的例子,處理雙精度的值
public class calc { public static double half_double(double a) { return a/2.0; } }
反編譯
... major version: 52 ... #2 = Double 2.0d ... public static double half_double(double); descriptor: (D)D flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=2, args_size=1 0: dload_0 1: ldc2_w #2 // double 2.0d 4: ddiv 5: dreturn LineNumberTable: line 5: 0
ldc2_w指令是從常量區裝載2.0d,另外,其他三條指令有d前綴,意思是他們使用double數據類型。
例子3,兩個參數
public class calc { public static int sum(int a, int b) { return a+b; } }
反編譯
... major version: 52 ... public static int sum(int, int); descriptor: (II)I flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=2 0: iload_0 1: iload_1 2: iadd 3: ireturn LineNumberTable: line 5: 0
iload_0 第0個變量(即變量a)壓入操作數棧
+-------+ | stack | +-------+ | a | +-------+
iload_1 第1個變量(即變量b)壓入操作數棧
+-------+ | stack | +-------+ | b | | a | +-------+
iadd 操作數棧中的前兩個int相加,並將結果壓入操作數棧頂
+-------+ | stack | +-------+ | result| +-------+
ireturn 返回棧頂元素
例子4,類型改為長整型
public class calc { public static long lsum(long a, long b) { return a+b; } }
反編譯
... major version: 52 ... public static long lsum(long, long); descriptor: (JJ)J flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=2 0: lload_0 1: lload_2 2: ladd 3: lreturn LineNumberTable: line 5: 0
可以看到壓入第二個參數的時候為lload_2,可見lload_0占了兩個槽(entry)
例子5,混合運算
public class calc { public static int mult_add(int a, int b, int c) { return a*b+c; } }
反編譯
... major version: 52 ... public static int mult_add(int, int, int); descriptor: (III)I flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=3 0: iload_0 1: iload_1 2: imul 3: iload_2 4: iadd 5: ireturn LineNumberTable: line 5: 0
iload_0 第0個變量(即變量a)壓入操作數棧
+-------+ | stack | +-------+ | a | +-------+
iload_1 第1個變量(即變量b)壓入操作數棧
+-------+ | stack | +-------+ | b | | a | +-------+
imul 操作數棧中的前兩個int相乘,並將結果壓入操作數棧頂
+-------+ | stack | +-------+ |result1| +-------+
iload_2 第2個變量(即變量c)壓入操作數棧
+-------+ | stack | +-------+ | c | |result1| +-------+
iadd 操作數棧中的前兩個int相加,並將結果壓入操作數棧頂
+-------+ | stack | +-------+ |result2| +-------+
ireturn 返回棧頂元素
Java逆向基礎之操作數棧