https://www.1024do.com/?p=367
栈帧结构
含义:C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录,是编译器用来实现过程函数调用的一种数据结构。
从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等。实现上有硬件方式和软件方式(有些体系不支持硬件栈)首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。
注意:ebp指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;esp所指的栈帧顶部和系统栈的顶部是同一个位置。
首先画一个地址空间图给大家加深理解:
既然今天分析的是栈帧,那么肯定是在栈区展开研究,下面为大家总体上画一张栈帧结构图:
接下来,首先看一下变量压栈的次序以及对应的汇编代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> #include <windows.h> int fun(int x, int y) { int c = 0xcccccccc; printf("I am fun function!\n"); return c; } int main() { int a = 0xaaaaaaaa; int b = 0xbbbbbbbb; int ret = fun(a, b); printf("you should running here!\n"); system("pause"); return 0; } |
在main函数调用fun函数之前,也就是main的栈帧结构:main函数的栈底ebp,栈顶esp。从低地址esp到高地址ebp,就是main函数的栈帧:
现在开始调用fun函数:这里用到了一条汇编指令:call:调用函数。
call有两大功能:①保存当前指令的下一条指令②修改栈底指针ebp。换句话说:call指令的效果是将返回地址入栈,并跳转到被调用过程的起始点。返回地址是在程序中紧跟call后面的那条指令的地址,这样被调用过程返回时,执行会从此处开始。
同样的一点,我们首先看一下汇编代码:
从代码和图中不难看出,在main函数里面,地址为008B14A4 的call指令调用函数fun,其中指明了栈帧esp和程序计算器pc的值,call指令随即将返回地址008B14A9压栈,并跳转到fun的第一条指令。到此main栈帧结束。
Call命令出现预示着一个旧的栈帧的结束,也印证了新的栈帧的到来:(汇编代码过长,图中将取重要的点来说)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <stdio.h> #include <windows.h> int fun(int x, int y) { int c = 0xcccccccc; int *p = &x; printf("I am fun function!\n"); p++; printf("before: %x\n", y); *p = 0xdddddddd; printf("after : %x\n", y);
return c; }
int main() { int a = 0xaaaaaaaa; int b = 0xbbbbbbbb; int ret = fun(a, b); printf("you should running here!\n"); system("pause"); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | int fun(int x, int y) { 008B13A0 push ebp 008B13A1 mov ebp,esp 008B13A3 sub esp,0D8h 008B13A9 push ebx 008B13AA push esi 008B13AB push edi 008B13AC lea edi,[ebp-0D8h] 008B13B2 mov ecx,36h 008B13B7 mov eax,0CCCCCCCCh 008B13BC rep stos dword ptr es:[edi] int c = 0xcccccccc; 008B13BE mov dword ptr [c],0CCCCCCCCh int *p = &x; 008B13C5 lea eax,[x] 008B13C8 mov dword ptr [p],eax printf("I am fun function!\n"); 008B13CB mov esi,esp 008B13CD push offset string "I am fun function!\n" (8B575Ch) 008B13D2 call dword ptr [__imp__printf (8B82B8h)] 008B13D8 add esp,4 008B13DB cmp esi,esp 008B13DD call @ILT+305(__RTC_CheckEsp) (8B1136h) p++; 008B13E2 mov eax,dword ptr [p] 008B13E5 add eax,4 008B13E8 mov dword ptr [p],eax printf("before: %x\n", y); 008B13EB mov esi,esp 008B13ED mov eax,dword ptr [y] 008B13F0 push eax 008B13F1 push offset string "before: %x\n" (8B574Ch) 008B13F6 call dword ptr [__imp__printf (8B82B8h)] 008B13FC add esp,8 008B13FF cmp esi,esp 008B1401 call @ILT+305(__RTC_CheckEsp) (8B1136h) *p = 0xdddddddd; 008B1406 mov eax,dword ptr [p] 008B1409 mov dword ptr [eax],0DDDDDDDDh printf("after : %x\n", y); 008B140F mov esi,esp 008B1411 mov eax,dword ptr [y] 008B1414 push eax 008B1415 push offset string "after : %x\n" (8B573Ch) 008B141A call dword ptr [__imp__printf (8B82B8h)] 008B1420 add esp,8 008B1423 cmp esi,esp 008B1425 call @ILT+305(__RTC_CheckEsp) (8B1136h)
return c; 008B142A mov eax,dword ptr [c] } 008B142D pop edi 008B142E pop esi 008B142F pop ebx 008B1430 add esp,0D8h 008B1436 cmp ebp,esp 008B1438 call @ILT+305(__RTC_CheckEsp) (8B1136h) 008B143D mov esp,ebp 008B143F pop ebp 008B1440 ret |