函数的每一次调用,都会为该函数开辟空间,用于本次函数的调用中变量的保存,现场保护。这块栈空间被称之为 函数栈帧
对这样一段代码进行调试发现,不仅仅有main函数,main函数被_tmainCRTStartup函数调用,而_tmainCRTStartup函数被mainCRTStartup函数调用
在函数栈帧中有两个至关重要的寄存器
ebp:存放指向当前函数的栈底的地址
esp:维护栈底,存放指向函数栈顶的地址,每当一次push操作完成,esp的地址更新为当前栈顶
对这段代码对应的汇编代码分析:
执行call指令时按F11,执行jmp指令,进行地址的跳转
可以发现通过jmp指令的跳转,刚好来到了Add函数内部
Add函数的栈帧与main函数整体思路相同
剩下就是函数的返回部分,则根据之前压栈的顺序出栈
ret指令会使得出栈一次,并将出栈的内容当作地址,将程序跳转到该地址处
根据上面分析的压栈顺序画出整个压栈过程,如下:
诡码分析
理解了上面整个函数栈帧的过程便可以理解以下代码
#include <stdio.h>
void fun()
{
int tmp = 10;
int *p = (int *)(*(&tmp+1));
*(p-1) = 20;
}
int main()
{
int a = 0;
fun();
printf("a = %d\n", a);
return 0;
}
通过函数栈帧分析就会发现一段诡异的代码就变得不再诡异。