Win32 环境下的堆栈(三)
简介
在Win32环境下利用调试器调试应用程序的时候经常要和堆栈(Stack)打交道,尤其是在需要手工遍历堆栈(Manually Walking Stack)的时候我们需要对堆栈的工作过程有一个比较清晰的了解.接下来的这些文字将通过一个例子程序详细的讲解堆栈的工作过程.
关键字
调试 堆栈 Stack Stack-Frame
步骤3.现在AFcun函数要调用BFunc了…
这是调用前的准备工作:
a.参数被压栈;
b.CALL指令导致返回地址0040110F入栈;
37: BFunc(m,n);
00401102 mov edx,dword ptr [ebp-8]
00401105 push edx
00401106 mov eax,dword ptr [ebp-4]
00401109 push eax
0040110A call @ILT+25(BFunc) (0040101e)
0040110F add esp,8
图 7
; 这和一开始wWinMain调用AFunc是差不多的过程
38:
39: return 8;
00401112 mov eax,8
40: }
00401117 pop edi
00401118 pop esi
00401119 pop ebx
0040111A add esp,48h
0040111D cmp ebp,esp
0040111F call __chkesp (00401220)
00401124 mov esp,ebp
00401126 pop ebp
00401127 ret
步骤4.进入BFcun函数之后堆栈的变化…
老规矩,我们先通篇看看BFunc在VC6++中的汇编代码:
18: int BFunc(int i,int j)
19: {
00401090 push ebp
00401091 mov ebp,esp
00401093 sub esp,48h
00401096 push ebx
00401097 push esi
00401098 push edi
00401099 lea edi,[ebp-48h]
0040109C mov ecx,12h
004010A1 mov eax,0CCCCCCCCh
004010A6 rep stos dword ptr [edi]
20: int m = 1;
004010A8 mov dword ptr [ebp-4],1
21: int n = 2;
004010AF mov dword ptr [ebp-8],2
22:
23: m = i;
004010B6 mov eax,dword ptr [ebp+8]
004010B9 mov dword ptr [ebp-4],eax
24: n = j;
004010BC mov ecx,dword ptr [ebp+0Ch]
004010BF mov dword ptr [ebp-8],ecx
25:
26: return m;
004010C2 mov eax,dword ptr [ebp-4]
27: }
004010C5 pop edi
004010C6 pop esi
004010C7 pop ebx
004010C8 mov esp,ebp
004010CA pop ebp
004010CB ret
; 先看看BFunc的prolog:
18: int BFunc(int i,int j)
19: {
00401090 push ebp
00401091 mov ebp,esp
00401093 sub esp,48h
00401096 push ebx
00401097 push esi
00401098 push edi
图 8
; 这个时候BFunc的堆栈信息也搭建好了(灰色部分)
20: int m = 1;
004010A8 mov dword ptr [ebp-4],1 ; 没什么新意的操作,和AFunc中发生的一模一样
21: int n = 2;
004010AF mov dword ptr [ebp-8],2 ; 没新意
图 9
22:
23: m = i;
004010B6 mov eax,dword ptr [ebp+8] ; 没新意
004010B9 mov dword ptr [ebp-4],eax
24: n = j;
004010BC mov ecx,dword ptr [ebp+0Ch] ; 没新意
004010BF mov dword ptr [ebp-8],ecx
25:
26: return m;
004010C2 mov eax,dword ptr [ebp-4] ; 函数的返回值是放在EAX里面返回的,如果说一个个函
; 数之间是行星的话EAX就是神5那载着杨天人的返回
; 舱了.
27: }
; 我们把重点放在BFunc函数返回时执行的这些指令上(epilog)
004010C5 pop edi
004010C6 pop esi
004010C7 pop ebx
004010C8 mov esp,ebp
004010CA pop ebp
004010CB ret
图 10
图 11
; 此时你会发现图11与图 7时的堆栈情况完全(ESP,EBP的值相同)一样,也就是说调用完BFunc函数后
; 堆栈恢复到了调用前的状态.
0040110F add esp,8 ; 注意BFunc执行完返回AFunc后AFunc将通过改变ESP将先前传给BFunc
; 的参数出栈,但不清空.
图12
就此AFunc调用BFunc函数结束了,接下来堆栈继续重演着:父函数调用子函数,子函数执行结束后返回.然后父函数又作为别人的子函数,执行结束,返回…..
附录
注解1
因为默认C/C++函数的调用约定是__cdecl,这种调用约定参数是从右到左压栈的,Windows提供的函数大部分是__stdcall的调用约定,符合该约定的函数在传参数的时候也是从右到左压栈.
参考书目
[1] Jeffrey Richter,”Programming Applications for Microsoft Windows 4rd”.( Microsoft Press,1999)
[2] Intel Architecture Software Developer Manual
[3] Randy Kath. “The Win32 Debugging API.” MSDN