025-【X86-汇编语言】-过程--堆栈

从本节开始介绍关于【过程】的有关内容。

过程

在生活中我们通常会重复使用某些物品,比如锅、筷子、电脑、房屋、床、汽车等。这些物品给我们提供某些功能性的服务,这些物品只被制造一次,只要它们不发生损毁我们便可以重复使用它们。编程时我们也使用相同的策略,对于某些常用的功能我们只编写一次代码,并在需要的时候重复使用这些代码。在汇编中我们称被组织起来的可以被重复调用的代码为【过程】。

数据结构中的堆栈

在数据结构中,堆栈是一种线性结构,它的一端是固定的称为【堆栈底(Bottom)】,另一端是浮动的称为【堆栈顶(Top)】。存取和读取数据必须在【堆栈顶(Top)】进行。

025-【X86-汇编语言】-过程--堆栈

向堆栈中存储数据的操作称为【PUSH】,意为把一个数据【推】进堆栈,此时堆栈的长度增一,Top指向新存入数据的位置。下图为一次PUSH操作前后的堆栈变化

025-【X86-汇编语言】-过程--堆栈                              025-【X86-汇编语言】-过程--堆栈

从堆栈中读取数据的操作称为【POP】,意为堆栈把堆栈顶的数据【抛】出来,此时堆栈的长度减一,Top指向原Top之后的位置。下图为一次POP操作前后堆栈的变化

                                                                          

025-【X86-汇编语言】-过程--堆栈                                         025-【X86-汇编语言】-过程--堆栈

当Top和Bottom相同时,表示堆栈为空,即堆栈中没有数据。

堆栈--过程调用

程序执行时【堆栈】指的是一块特殊的内存空间。这块内存空间的使用方法与【数据结构中的堆栈】完全一致,而这块内存空间与【过程】调用有着极大的关系。

程序实例

为了更直观地介绍【堆栈】,我们直接上程序

.386
.model stdcall,flat
.stack 4069 ;设置堆栈大小

ExitProcess PROTO, dwExitCode:DWORD

.data
.code
MAIN PROC
	MOV EAX,01020304h
	PUSH EAX		;把EAX的值PUSH入堆栈
	POP  EBX		;堆栈把Top的值抛到EBX中
	INVOKE ExitProcess,0
MAIN ENDP
END MAIN

之前的所有程序实例中都会包含一条代码【.stack 4096】,意思是指定程序运行时堆栈的大小为4096个字节。

在程序执行时寄存器ESP始终保存着Top的地址,在【MOV EAX,1h】处打断点,然后查看ESP的值,此时ESP的值为ESP = 0020F9D4(每次执行值会发生变化)下面执行【PUSH EAX】,将EAX的值存入堆栈,执行之后ESP的值为ESP = 0020F9D0,可以发现ESP的值减少了4。再执行【POP EBX】,将堆栈顶的值抛到EBX中,执行后ESP的值为ESP = 0020F9D4,我们发现ESP的值增加了4。**堆栈内存中的值大家可以自行查看。

由ESP值的变化规律,我们可以得出结论,程序的堆栈是向下增长的,即Bottom在内存的高地址,Top在内存的低地址。

当PUSH时ESP的值减少,具体减少多少视操作数决定,32bit操作数则减少4,16bit操作数则减少2。然后将值存入ESP指向的内存地址

025-【X86-汇编语言】-过程--堆栈

当POP时,先将ESP指向地址的值赋值给操作数,再将ESP的值增加,具体增加多少视操作数决定,32bit操作数则增加4,16bit操作数则增加2。

025-【X86-汇编语言】-过程--堆栈

我们可以尝试将程序实例中的POP和PUSH指令颠倒,看看堆栈为空时直接POP会有什么结果。事实上程序是不会崩溃的,而是按照POP的规则执行了,因此可以看出程序执行时的堆栈并没有Bottom这个概念。有时黑客会利用这一点得到想要的内存信息!