Win32 环境下的堆栈(二)
简介
在Win32环境下利用调试器调试应用程序的时候经常要和堆栈(Stack)打交道,尤其是在需要手工遍历堆栈(Manually Walking Stack)的时候我们需要对堆栈的工作过程有一个比较清晰的了解.接下来的这些文字将通过一个例子程序详细的讲解堆栈的工作过程.
关键字
调试 堆栈 Stack Stack-Frame
下面我们要花些篇幅详细的解释AFunc函数执行过程堆栈(主要是ESP,EBP)的变化情况:
29: int AFunc(int i,int j)
30: {
004010D0 push ebp
004010D1 mov ebp,esp
004010D3 sub esp,48h
004010D6 push ebx
004010D7 push esi
004010D8 push edi
;
; 上面几行代码叫做prolog,可以理解成”序曲,开始部分”,与之对应的叫epilog(结束曲,结束部分)对于
; prolog需要逐行解释一下:
;
; 004010D0 PUSH EBP
; 将进入AFunc函数之前的EBP的值入栈保存,这时候的EBP相当于是AFunc上级函数
; 的一个现场信息,所以需要保存起来,以便于AFunc返回后上级函数可以恢复EBP使其指向其调用
; AFunc之前的堆栈位置(当然,这还需要靠恢复ESP来协助达到这一目的),该语句执行完之后堆栈将
; 变成下面这个样子
;
图 2
; 在这里要解释一下什么时候”AFunc结束之后的返回地址”入栈了?导致它入栈的语句就是
; CALL @ILT+20(AFunc) (00401019)
; 也就是说是CALL指令干的
;
; 004010D1 MOV EBP,ESP
; ESP赋给EBP,这样EBP就可以拿来访问本函数的局部变量
图 3
; 004010D3 SUB ESP,48h AFunc函数中有两个int型的变量所以开辟的空间大小是
; 40+2*sizeof(int),我暂时还没有找到正式文档中对于此大小
; 计算的公式.注意:ESP-48h后开辟的新的堆栈中的这块空间就是
; 大名鼎鼎的Stack Frame.
; 004010D6 PUSH EBX 我们知道通用寄存器有时候在程序运算的时候可以用来存放
; 临时结果,如果此结果有必要的话也是需要作为现场信息被保存在
; 堆栈中的.
; 004010D7 PUSH ESI
; 004010D8 PUSH EDI
图 4
; 从上面的图解我们很容易看出在进入AFunc函数执行完prolog之后ESP和EBP指示出了堆栈中
; 存放的当前执行函数的信息(绿色部分,其上级函数的堆栈信息由亮绿色表示,呵呵,我可能有一点色
; 弱所以那到底是不是亮绿色我也不是很确定,夜深人静也没人可问…)
004010D9 lea edi,[ebp-48h]
004010DC mov ecx,12h
004010E1 mov eax,0CCCCCCCCh
004010E6 rep stos dword ptr [edi]
31: int m = 3;
004010E8 mov dword ptr [ebp-4],3 ; 函数的局部变量放置在EBP的负偏移处(Negative
; Offset)也就是向低地址方向(当然,当然,这是针对该函数使用
; 了标准的Stack Frame,如果代码被编译器作了优化了那么你
; 很可能就要遇到FPO这个概念,这可能需要另外写一篇文章
; 来解释,所以这里假设我们的函数使用的是标准的Stack
; Frame)
32: int n = 4;
004010EF mov dword ptr [ebp-8],4 ; 同上
图 5
33:
34: m = i;
004010F6 mov eax,dword ptr [ebp+8] ; 从上图中很容易看出来dword ptr [ebp+8]里面放的是
; 上级函数传给AFunc的第一个参数,这里用ebp+8来访问
; 参数说明上级传给下级函数的参数是放在下级函数
; 的EBP的正向偏移位置处(Positive Offset)
004010F9 mov dword ptr [ebp-4],eax ; 将参数的值赋给局部变量
35: n = j;
004010FC mov ecx,dword ptr [ebp+0Ch] ; 同上
004010FF mov dword ptr [ebp-8],ecx ; 同上
图 6
(未完待续)