首先我们了解一下内存结构
由上图可以知道,栈是由高地址到低地址,而函数的调用就是在栈上开辟一段空间供函数使用;其次我们要了解几个寄存器,
esp:寄存器存放当前线程的栈顶指针
ebp:寄存器存放当前线程的栈底指针
eip(pc):寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从eip寄存器中读取下一条指令的内存地址,然后继续执行。
接下来我们来了解一下函数调用过程中的栈帧结构。
#include <stdio.h> int main() { int a=0xAAAAAAAA; int b=0xBBBBBBBB; int ret=myadd(a,b); printf("you should run here!: %d\n",ret); return 0; } int myadd(int x,int y) { int z=x+y; return z; }
首先,我们发现,main()函数并不是第一个被调用的函数,实际上要先被mainCRTStartup()函数调用。
接下来将程序翻译成汇编
3: { 00401020 push ebp 00401021 mov ebp,esp 00401023 sub esp,4Ch 00401026 push ebx 00401027 push esi 00401028 push edi 00401029 lea edi,[ebp-4Ch] 0040102C mov ecx,13h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 4: int a=0xAAAAAAAA; 00401038 mov dword ptr [ebp-4],0AAAAAAAAh 5: int b=0xBBBBBBBB; 0040103F mov dword ptr [ebp-8],0BBBBBBBBh 6: int ret=myadd(a,b); 00401046 mov eax,dword ptr [ebp-8] 00401049 push eax 0040104A mov ecx,dword ptr [ebp-4] 0040104D push ecx 0040104E call @ILT+0(_myadd) (00401005) 00401053 add esp,8 00401056 mov dword ptr [ebp-0Ch],eax 7: printf("you should run here!: %d\n",ret); 00401059 mov edx,dword ptr [ebp-0Ch] 0040105C push edx 0040105D push offset string "you should run here!: %d\n" (0042201c) 00401062 call printf (004010e0) 00401067 add esp,8 8: return 0; 0040106A xor eax,eax 9: 10: } 0040106C pop edi 0040106D pop esi 0040106E pop ebx 0040106F add esp,4Ch 00401072 cmp ebp,esp 00401074 call __chkesp (00401160) 00401079 mov esp,ebp 0040107B pop ebp 0040107C ret
然后具体分析。
1、在栈中由高地址到低地址开辟空间,创建局部变量a,b
2、将形参实例化,然后压入栈中,先压b。
3、跳转至myadd函数内。
4、调用结束后弹出。
5、过程调用结束。
至此,通过图例演示了函数调用的过程,栈帧结构的形成和释放(即临时变量的形成和释放)。
小练习:了解了栈帧结构,我们是否可以通过a的地址去更改b的值和返回值的地址?
1、通过a的地址修改b的值
#include <stdio.h> #include <windows.h> int main() { int a=10; int b=15; int ret=0; ret=myadd(a,b); printf("you should run here!: %d\n",ret); system("pause"); return 0; } int myadd(int x,int y) { int z=0; int *p=&x; p++; *p=20; z=x+y; return z; }
2、修改返回值的地址
#include <stdio.h> #include <windows.h> void bug(); int main() { int a=10; int b=15; int ret=0; printf("main run here....\n"); ret=myadd(a,b); printf("you should run here!: %d\n",ret); system("pause"); return 0; } int myadd(int x,int y) { int z=0; int *p=&x; p--; *p=(int)bug; z=x+y; printf("myadd run here....\n"); return z; } void bug() { printf("i am here!!!"); system("pasue"); }
注:这里程序运行结束后会崩掉,为什么呢?
因为调用bug函数时,时指针跳转过去的,过程中,没有push,但返回的时候有pop,所以多了一次pop,栈结构不平衡了。