透析栈帧——栈帧的应用
程序员文章站
2024-01-01 21:48:58
...
应用一:不调用变量本身,而修改该变量的数据
代码如下:
#include <stdio.h>
#include <windows.h>
int myadd(int x, int y)
{
int *p = &x;
p++;
printf("before: %d\n",*p);
*p = 30;
printf("after: %d\n", y);
}
int main()
{
int a = 10;
int b = 20;
myadd(a, b);
printf("main:you should running here!\n ");
system("pause");
return 0;
}
首先,让指针p指向x的地址,p++后指针向上移动,此时p指向y的地址,故而*P==y==20; 当给*p赋值30后,此时p仍指向y的地址,相当于给y赋值,所以y==30。
运行结果如下:
应用二:非法调用第三方函数
代码如下:
#include <stdio.h>
#include <Windows.h>
void *ret = NULL;
void bug()
{
int x;
int *q = &x;
q += 2;
*q = ret;
printf("bug: I am bug!\n");
Sleep(2000);
}
int myadd(int x, int y)
{
int *p = &x;
p--;
ret = *p;
*p = bug;
printf("myadd: myadd is called,begin return...\n");
}
int main()
{
int a = 10;
int b = 20;
int c = myadd(a, b);
__asm{
sub esp,4
}
printf("main:you should running here!\n ");
system("pause");
return 0;
}
在栈帧结构中,函数调用返回时,是利用函数被调用之前所保存call指令的下一条指令的地址而回到main函数,当我们改写这个地址时,此时函数便可强行跳转至另一个函数中去。
对于上面这个代码而言,函数的调用过程为:main函数—>myadd函数—>bug函数—>main函数
在main函数中
执行到c=myadd(a,b)语句时,程序跳转至myadd函数
在myadd函数中
(1)指针p先指向x的地址(2)执行p--找到main函数的返回地址(3)将该地址保存(ret=*p)(4)将p指向的地址改为bug函数的返回地址(5)执行输出语句(6)myadd函数结束,程序跳转至bug函数
在bug函数中
(1)定义变量x,定位bug函数(2)指针q先指向x的地址(3)执行q+=2语句找到执行完bug函数后所要返回的地址(4)执行*q=ret语句将该地址改为main函数的返回地址(5)执行输出语句(6)休眠2s(7)bug函数结束,程序跳转至main函数
在main函数中
执行__asm{ sub esp,4} 语句,在c语言中插入汇编代码,sub esp,4 就是 esp-4 ,目的是平衡栈帧。每一个函数在被调用时都是通过call指令调用的,此时执行push指令将地址入栈,在调用完返回时是通过ret指令返回的,此时执行pop指令将地址出栈,一入一出,栈帧刚好平衡。在调用bug函数时,我们是通过修改地址的方式调用的,没有执行push指令,但在返回时,我们通过ret指令返回,执行了pop指令,无入有出,此时栈帧结构不平衡,所以我们要执行esp-4这步语句来使栈帧结构达到平衡。
运行结果如下: