Java逆向基础之操作数栈

本文参考:http://www.vuln.cn/7115

本地变量和操作数栈

本地变量数组(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 返回栈顶元素

原文地址:http://blog.51cto.com/7317859/2105402

时间: 2024-10-09 10:34:41

Java逆向基础之操作数栈的相关文章

Java逆向基础之数组

本文参考:http://www.vuln.cn/7116 本文参考:<Reverse Engineering for Beginners>Dennis Yurichev著 数组 简单的例子 创建一个长度是10的整型的数组,对其初始化 public class ArrayInit { public static void main(String[] args) { int a[] = new int[10]; for (int i = 0; i < 10; i++) a[i] = i; d

Java逆向基础之异常

本文参考:http://www.vuln.cn/7116 本文参考:<Reverse Engineering for Beginners>Dennis Yurichev著 异常 由之前月份处理修改的例子 //清单1IncorrectMonthException.java public class IncorrectMonthException extends Exception { private int index; public IncorrectMonthException(int in

Java逆向基础之JDB动态调试

本文参考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jdb.html JDB在有源代码的时候可以实现调试远程机器上的java程序,但是在逆向中源码很难分析出来,不过还是能够调试得到一部分信息的 本文主要获取两个信息 1.动态调用混淆类中的方法,因为实践过程中我们去手动补全相关代码比较难 2.打印调用栈 以下例子以调试ZKM.jar为例 JDB无源代码调试调用方法 启动Xdebug调试 java -Xdebug -X

Java逆向基础之函数

本文参考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 = 

Java逆向基础之条件跳转位运算循环

本文参考:http://www.vuln.cn/7117 条件跳转的例子,绝对值 public class abs {     public static int abs(int a)     {         if (a<0)             return -a;         return a;     } } 编译 javac abs.java 反编译 javap -c -verbose abs.class   public static int abs(int);     d

Java逆向基础之简单的补丁

本文参考:http://www.vuln.cn/7118 本文参考:<Reverse Engineering for Beginners>Dennis Yurichev著 本文需要用到IDA 简单的补丁 看一个例子 public class nag { public static void nag_screen() { System.out.println("This program is not registered"); }; public static void ma

Java逆向基础之动态生成类

为什么有这个东西,一方面时AOP框架的需要,另一方面是增加软件逆向的难度 动态生成类的技术目前大体上分为两类,一类是通过操作字节码框架如cglib/Javassist去实现,另一类就是JNI方式,调用dll/so库,内存中动态还原.这两种方式都能实现隐藏类 看一个Javassist动态生成类的例子 package com.vvvtimes; import java.lang.reflect.Modifier; import javassist.ClassPool; import javassis

Java逆向基础之初识javaagent

首先说一下javaagent是什么 javaagent是一种能够在不影响正常编译的情况下,修改字节码. 在逆向中javaagent可以完成对类的拦截和增强. 看一个例子 在Eclipse新建如下MyAgent结构的项目 MyAgent.java文件内容 package com.vvvtimes.demo.agent; import java.lang.instrument.Instrumentation; public class MyAgent {     public static void

Java逆向基础之导出内存中的类一

为什么需要这个,因为在之前的博文中提到,为了增加逆向的难度,部分软件会对部分关键方法和类进行隐藏,所以我们需要把这个类从内存中拿出来. 本文介绍使用javaagent的方法,下一篇介绍dumpclass,两种方法各有利弊. 本文需要用到第三方jar为:javassist-3.20.0-GA.jar,不过我们需要的是它的源码javassist-3.20.0-GA-sources.jar 新建名为DumpClassAgent的项目,项目结构如下 将下载到的源码复制到src目录下 DumpClassA