如果我们用符号表示堆栈:
0xBFFFFFFF ---------------------
| |
| . |
| . |
| . |
| . |
| etc |
| env/argv pointer. |
| argc |
|-------------------|
| |
| stack |
| |
| | |
| | |
| V |
/ /
\ \
| |
| ^ |
| | |
| | |
| |
| heap |
|-------------------|
| bss |
|-------------------|
| initialized data |
|-------------------|
| text |
|-------------------|
| shared libraries |
| etc. |
0x8000000 |-------------------|
_* STACK *_
|
堆栈在基本术语里面是一个数据结构,你们都可以从你们的数据结构课程记起来。它有同样的基本操作。它是一个LIFO(后进,先出)的数据结构。它的处理过程通过一些特殊的指令象PUSH和POP由CPU直接控制。你PUSH一些数据到堆栈里面,又POP一些其它的数据。不论谁最后到,它将是最先出来的那个。因此,用专业术语说,第一个将被从堆栈中推出来的是最后一个被推进去的。
在CPU中注册的SP(堆栈指针)包括将要从堆栈中推出来的数据地址。不论SP指向最后的数据还是堆栈中最后数据的后面数据是CPU-specific的;然而,我们的目标ix86结构,SP指向堆栈中最后数据的地址。在ix86保护模式(32位/双字)下,PUSH和POP指令在4字节单元中完成。在这里要说的另外一个重要的细节是堆栈向下增长,也就是,如果SP是0xFF,执行 PUSH EAX指令后,SP将变成0xFC并且EAX的值将被放到0xFC地址里。
PUSH指令将从ESP(回顾一下上面的图)中减去4个字节,并且将推入一个双字到堆栈,放置双字到ESP寄存器所指的地址中。另一方面,POP指令,读取ESP寄存器中的地址,POP掉堆栈地址所指的值,并且加4到ESP(加4到ESP寄存器中的地址)。假设ESP初始化为0x1000,让我们观察下面的汇编代码:
PUSH dword1 ;value at dword1: 1, ESP's value: 0xFFC (0x1000 - 4)
PUSH dword2 ;value at dword2: 2, ESP's value: 0xFF8 (0xFFC - 4)
PUSH dword3 ;value at dword3: 3, ESP's value: 0xFF4 (0xFF8 - 4)
POP EAX ;EAX' value 3, ESP's value: 0xFF8 (0xFF4 + 4)
POP EBX ;EBX's value 2, ESP's value: 0xFFC (0xFF8 + 4)
POP ECX ;ECX's value 1, ESP's value: 0x1000 (0xFFC + 4)
|
当堆栈被用做动态变量的临时存储的时候,它被用来存储一些调用存储临时变量函数的地址和在函数间传递参数。而且,当然,这也是邪恶的来场。
共3页: 上一页 [1] 2 [3] 下一页
|