一段程序,认识栈帧

1、认识栈帧linux

先来看一段神奇的代码:windows

wKiom1dioATxGyU3AACN-rpzxJg163.png

 

(windows下,代码以下)ide

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
void fun()
{
 printf("You Are Done\n");
 Sleep(2000);
 printf("Suppose The Computer Will Shut Down~~~~\n");  
 //上面这行若是换成system("reboot")之类的呢?
 Sleep(2000);
 exit(1);
}
int fun1(int a, int b)
{
 int *p = &a;
 p--;
 *p = fun;
 int c = 0xcccc;
 return c;
}
int main()
{
 printf("begin run...\n");
 int a = 0;
 int b = 1;
 fun1(a, b);
 printf("you should run here\n");
 return 0;
}

执行结果为:函数

    linux下:工具

wKioL1dio5ygvWH0AAA2LLJsos8997.png

 

    win下:spa

wKioL1dio8ei0JdJAAAHh3YMUQk709.png

 

结果彷佛都和咱们预期的不同啊3d

按理说,程序从main开始执行指针

中间调用fun1函数调试

调用完毕后应该继续执行下面的printform

而后输出:

    you should run here

 

而实际上,程序最终却进入了fun函数

之因此这样,是由于栈帧的缘故。

若是你对上面发生的事情感到好奇,能够接着往下看

 

2、原理解释

关于栈帧,百度百科是这样解释的:

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

 

也就是说,上面的代码,在内存方面能够这样理解:

wKioL1diqh_ivWi8AABFlbFXhPw385.png

 

简单解释一下,

咱们知道C语言中函数中定义的变量是在栈上开辟的,这张图片就表示栈内存,

其地址从上往下表示从大到小

 

main函数中,前后将a b 入栈,

而后调用fun1(a, b)

图片中的这个fun1() 其实不许确,它应该是 返回地址

这个 返回地址 就是表示:执行fun1(a, b)完毕后,应该返回到这个地方接着执行main函数中剩下的代码。

此外,在fun1() 和 b 之间,还应该存放一个东西:栈指针ebp(图上没有表示出来)

 

而后参数 a b 是局部变量,也分别入栈,

借助调试工具,能够看到,a b 的地址分别为图中所示

而后定义一个指针p指向a

接下来p--,这时p指向的地址为0x0018fc20,也就是 刚刚说的 返回地址

 

这时候,应该能发现,返回地址已经变了, 变成了fun的地址

也就是说,当执行完fun1()后,程序并不会返回到 main函数中调用它的地方,而是接着调用fun函数

wKiom1diq8ix2p3tAABJXz8Vme0189.png

 

这就致使程序难以想象地进入了咱们没有预想到的地方,调用了咱们本不想调用的函数,

并且因为这个返回地址的丢失,在调用完毕fun后,程序也会由于找不到返回地址而挂掉。

(我在代码中执行了  exit(1);  这句话强行终止了程序)

 

以上就是代码的原理解释了。

接下来,利用刚刚所get到的栈帧方面的知识,能够作一个事情:

 

 

3、修改b的值

要求:不要直接修改a、b变量,而经过栈帧,实现修改a、b变量的值

 

代码:

void fun1(int a, int b)
{
 int *p = &a;
 p -= 2;
 int ReturnAddr = *p;   //返回值
 //修改main中的a
 p = ReturnAddr - 4 * 2;
 *p = 11111;
 //修改main中的b
 p = ReturnAddr - 4 * 5;
 *p = 12345;
}
int main()
{
 printf("begin run...\n");
 int a = 0;
 int b = 1;
 fun1(a, b);
 printf("you should run here\n");
 printf("%d\n", a);
 printf("%d\n", b);
 return 0;
}

以前画的内存图比较简单,要想实现这个要求,必须进一步了解栈帧是怎么存放的了,下面是比较详细的栈内存图:

wKioL1diu7WA992LAAA_PuUQFy0463.png

 

 

linux代码:

wKiom1dizxajNDYRAAB2f4Y7YMw939.png

运行结果

wKiom1dizzaQHhPPAAAP5GW3DOg271.png

(gcc 和 vs编译的程序的栈帧存放规则不一样)

相关文章
相关标签/搜索