Java逆向基础之操作数栈
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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逆向基础之操作数栈的主要内容,如果未能解决你的问题,请参考以下文章