一、栈帧
每一次函数调用都是一个过程,这个过程称之为函数的调用过程。,这个过程要为函数开辟空间,用于用于本次函数的调用中临时变量的保存、现场保护,这块栈空间我们称之为函数栈帧。
在函数调用的过程中这ebp(栈底寄存器)和esp(栈顶寄存器)两个存器存放了维护这个栈的栈底和栈顶指针。
二、调用过程
以下面代码(修改变量)为例
#include <stdio.h>
#include <windows.h>
int myadd(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int a = 0xAAAAAAAA;
int b = 0xBBBBBBBB;
int c = myadd(a, b);
printf("you should run here!\n");
printf("result: %d\n", c);
system("pause");
return 0;
}
其中对于main函数而言
要将a与b压入栈中,变量a和b都在main函数的栈帧中,过程如图
其中a先入,b后入,即先给a开辟空间,所以a在高地址,b在低地址
接下来进入myadd函数,则过程如图
三、小结
1.形容实例化的临时变量在调用函数和被调用函数两者栈帧之间
2.形容实例化的顺序(main函数传参):从右往左(先生成后面的)
3.函数内部定一的变量具有临时性:调用函数时放在栈帧 ,结束时释放栈帧;临时变量放在栈帧中,有自己的生命周期
4.每个变量都有栈帧,函数通过调用esp,ebp形成自己的栈帧
5.常规情况下,函数的返回值都以寄存器的形式供我们返回
四、插入第三方函数
在main函数中强行插入bug
#include <stdio.h>
#include <windows.h>
void *main_ret = NULL;
int bug()
{
int first = 0;
int *p = &first;
p+=2;
*p = main_ret;
Sleep(1000);
printf("haha, I catch you!, I am a bug!\n");
Sleep(1000);
}
int myadd(int x, int y)
{
int *p = &x;
int z=0;
p--;
printf("begin myadd...\n");
main_ret = *p;
*p = bug;
z = x + y;
printf("end myadd...\n");
return z;
}
void mytest(int x, int y)
{
int z = 0;
int *p = &x;
int *q = &z;
p--;
q+=2;
printf("%#x, %#x\n", p, q);
}
int main()
{
int a = 0xAAAAAAAA;
int b = 0xBBBBBBBB;
int c = 0;
printf("begin main...\n");
c = myadd(a, b);
_asm{
sub esp,4
}
printf("you should run here!\n");
printf("result: %d\n", c);
printf("end main...\n");
system("pause");
return 0;
}
注:以上代码均在VC6.0中运行