天天看点

神奇的栈帧

C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。

先看张图对栈的位置有个基本的印象哈:

神奇的栈帧

首先必须明确一点也是非常重要的一点,栈是向下生长的,所谓向下生长是指从内存高地址->低地址的路径延伸,那么就很明显了,栈有栈底和栈顶,那么栈顶的地址要比栈低低。

寄存器ebp在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的。

寄存器esp是会随着数据的入栈和出栈移动的,也就是说,esp始终指向栈顶。

下面我们看一个有趣的程序:

神奇的栈帧

感兴趣的童鞋可以运行试试哈(先保证没有重要的临时文件在运行,害怕你到时砍我),整个程序运行起来你的电脑就会重启,很好看出来,重启的程序在fun函数中,可是整个过程看起来我们并没有调用fun,为什么它还是run起来了?

原因就在这栈帧之中,首先先说明一点,ELF格式的入口是_start而不是mian,也就是说在mian函数的栈帧之前还有一个_start函数,我们先转到汇编,看看函数的栈帧到底是怎么开辟的:

先是_start的函数栈帧:

神奇的栈帧

再看看main的:

神奇的栈帧

通过简单的图来表示下就是这样的:

神奇的栈帧

关键的部分是fun1的汇编,它具体给我们展示了到底为什么fun会被调用,下面看看吧:

神奇的栈帧

再来张示意图:

神奇的栈帧

结合上面两个图和注释可以看出,在fun1调用完后我们需要返回到之前保存的地址中去,而在fun1中我们将其返回的地址改成了fun函数的入口地址,所以在fun1结束后我们就跳到了fun函数,然后就悲催了。。。。。

如果我们了解了栈帧的原理,那可以做很多神奇的事情,比如在fun1中我们可以不使用b变量就将b的值改掉,很简单,通过上面的汇编可以看出只要将fun1中的p--,改成p++;就找到b变量的地址啦,然后解引用就可以修改啦,不信可以去试试喽。

继续阅读