025-【X86-汇编语言】-过程--堆栈
从本节开始介绍关于【过程】的有关内容。
过程
在生活中我们通常会重复使用某些物品,比如锅、筷子、电脑、房屋、床、汽车等。这些物品给我们提供某些功能性的服务,这些物品只被制造一次,只要它们不发生损毁我们便可以重复使用它们。编程时我们也使用相同的策略,对于某些常用的功能我们只编写一次代码,并在需要的时候重复使用这些代码。在汇编中我们称被组织起来的可以被重复调用的代码为【过程】。
数据结构中的堆栈
在数据结构中,堆栈是一种线性结构,它的一端是固定的称为【堆栈底(Bottom)】,另一端是浮动的称为【堆栈顶(Top)】。存取和读取数据必须在【堆栈顶(Top)】进行。
向堆栈中存储数据的操作称为【PUSH】,意为把一个数据【推】进堆栈,此时堆栈的长度增一,Top指向新存入数据的位置。下图为一次PUSH操作前后的堆栈变化
从堆栈中读取数据的操作称为【POP】,意为堆栈把堆栈顶的数据【抛】出来,此时堆栈的长度减一,Top指向原Top之后的位置。下图为一次POP操作前后堆栈的变化
当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指向的内存地址
当POP时,先将ESP指向地址的值赋值给操作数,再将ESP的值增加,具体增加多少视操作数决定,32bit操作数则增加4,16bit操作数则增加2。
我们可以尝试将程序实例中的POP和PUSH指令颠倒,看看堆栈为空时直接POP会有什么结果。事实上程序是不会崩溃的,而是按照POP的规则执行了,因此可以看出程序执行时的堆栈并没有Bottom这个概念。有时黑客会利用这一点得到想要的内存信息!