反汇编系列——堆栈篇
Posted 牧秦丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反汇编系列——堆栈篇相关的知识,希望对你有一定的参考价值。
要反汇编程序,不可避免要接触到堆栈,你首先得会查看堆栈,知道堆栈在某一时刻的确切内容。首先,我们讲述一些与堆栈相关的基础知识。
1、堆栈基础
汇编语言中的“堆栈”的含义与数据结构中堆栈的含义不同,尽管从操作上来说,它们都是“后进先出”,这个不用赘述。汇编中有一个寄存器esp指向当前栈顶,而栈底的位置是不变的,整个程序运行过程中,通过操作esp来操作堆栈,进行堆栈的压入、弹出及平衡操作。
一些堆栈操作术语:
- 压入:将一个变量压入到堆栈;
- 弹出:将一个变量从堆栈中弹出;
- 平衡堆栈:当函数调用完成后,进行局部变量的释放、返回值的正确处理等;
2、堆栈类型
根据架构的不同,有两种基本的堆栈类型:向上生长和向下生长的堆栈。
- 向上生长堆栈:堆栈向高地址增长,当向栈中压入元素时,esp增加。栈顶地址 >= 栈底地址。
- 向下生长堆栈:堆栈向低地址增长,当向栈中压入元素时,esp减小。栈顶地址 <= 栈底地址。
3、函数调用 堆栈主要用于函数调用,我们知道,一个函数调用时的例程如下:
- 将函数参数入栈;
- 将返回处的代码地址入栈(段内调用一般将eip入栈,段间调用将 cs、eip 依次入栈);
- jmp到被调函数代码地址处开始执行;
- 被调函数分配局部变量内存;
- 执行被调函数代码,存好返回值;
- 平衡堆栈。
int f1()
cout<<"In f1()"<<endl;
return 1;
int f2()
cout<<"In f2()"<<endl;
return 2;
int foo(int a, int b)
// ...
foo(f1(), f2());
像这段代码,我们不知道先执行
f1
还是先执行
f2
,因为不同的编译器编译出的结果可能不同。用
Microsoft Visual C++
编译出的代码,先执行
f2
,后执行
f1
,也就是说,
MSVC
编出的代码,参数从右往左依次入栈。
4、调用约定 谈到函数调用,就不可避免的谈到函数调用约定。主流的调用约定有:PASCAL、__cdecl、__stdcall等:
- PASCAL:参数从左往右入栈,被调用者平衡堆栈;
- __cdecl:即 C 调用约定,参数从右往左入栈,调用者平衡堆栈;
- __stdcall:即标准调用约定,参数从右往左入栈,被调者平衡堆栈;
5、结语 我们详述了堆栈相关的基本知识,在后续的篇幅中,这些知识将非常关键。下一章将结合一个实际实例介绍IDA Pro逆向出的代码,从而对反汇编有一个基本的了解。
以上是关于反汇编系列——堆栈篇的主要内容,如果未能解决你的问题,请参考以下文章
Windows 逆向OD 调试器工具 ( OD 附加进程 | OD 调试器面板简介 | 反汇编窗口 | 寄存器窗口 | 数据窗口 | 堆栈窗口 )