缓冲区溢出详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缓冲区溢出详解相关的知识,希望对你有一定的参考价值。

https://www.cnblogs.com/clover-toeic/p/3737011.html

1 缓冲区溢出原理

     缓冲区是一块连续的计算机内存区域,可保存相同数据类型的多个实例。缓冲区可以是堆栈(自动变量)、堆(动态内存)和静态数据区(全局或静态)。在C/C++语言中,通常使用字符数组和malloc/new之类内存分配函数实现缓冲区。溢出指数据被添加到分配给该缓冲区的内存块之外。缓冲区溢出是最常见的程序缺陷。

     栈帧结构的引入为高级语言中实现函数或过程调用提供直接的硬件支持,但由于将函数返回地址这样的重要数据保存在程序员可见的堆栈中,因此也给系统安全带来隐患。若将函数返回地址修改为指向一段精心安排的恶意代码,则可达到危害系统安全的目的。此外,堆栈的正确恢复依赖于压栈的EBP值的正确性,但EBP域邻近局部变量,若编程中有意无意地通过局部变量的地址偏移窜改EBP值,则程序的行为将变得非常危险。

     由于C/C++语言没有数组越界检查机制,当向局部数组缓冲区里写入的数据超过为其分配的大小时,就会发生缓冲区溢出。攻击者可利用缓冲区溢出来窜改进程运行时栈,从而改变程序正常流向,轻则导致程序崩溃,重则系统特权被窃取。

     例如,对于下图的栈结构:

技术分享图片 

     若将长度为16字节的字符串赋给acArrBuf数组,则系统会从acArrBuf[0]开始向高地址填充栈空间,导致覆盖EBP值和函数返回地址。若攻击者用一个有意义的地址(否则会出现段错误)覆盖返回地址的内容,函数返回时就会去执行该地址处事先安排好的攻击代码。最常见的手段是通过制造缓冲区溢出使程序运行一个用户shell,再通过shell执行其它命令。若该程序有root或suid执行权限,则攻击者就获得一个有root权限的shell,进而可对系统进行任意操作。

     除通过使堆栈缓冲区溢出而更改返回地址外,还可改写局部变量(尤其函数指针)以利用缓冲区溢出缺陷。

     注意,本文描述的堆栈缓冲区溢出不同于广义的“堆栈溢出(Stack OverFlow)”,后者除局部数组越界和内存覆盖外,还可能由于调用层次太多(尤其应注意递归函数)或过大的局部变量所导致。

 

2 缓冲区溢出实例

     本节给出若干缓冲区溢出相关的示例性程序。前三个示例为手工修改返回地址或实参,后两个示例为局部数组越界访问和缓冲区溢出。更加深入的缓冲区溢出攻击参见相关资料。

     示例函数必须包含stdio.h头文件,并按需包含string.h头文件(如strcpy函数)。

    【示例1】改变函数的返回地址,使其返回后跳转到某个指定的指令位置,而不是函数调用后紧跟的位置。实现原理是在函数体中修改返回地址,即找到返回地址的位置并修改它。代码如下:

技术分享图片
技术分享图片
 1 //foo.c
 2 void foo(void){
 3     int a, *p;
 4     p = (int*)((char *)&a + 12);  //让p指向main函数调用foo时入栈的返回地址,等效于p = (int*)(&a + 3);
 5     *p += 12;    //修改该地址的值,使其指向一条指令的起始地址
 6 }
 7 int main(void){
 8     foo();
 9     printf("First printf call\\n");
10     printf("Second printf call\\n");
11     return 0;
12 }
技术分享图片

     编译运行,结果输出Second printf call,未输出First printf call。

     下面详细介绍代码中两个12的由来。

     编译(gcc main.c –g)和反汇编(objdump a.out –d)后,得到汇编代码片段如下:

技术分享图片 

     从上述汇编代码可知,foo后面的指令地址(即调用foo时压入的返回地址)是0x80483b8,而进入调用printf("Second printf call“)的指令地址是0x80483c4。两者相差12,故将返回地址的值加12即可(*p += 12)。

     指令<804838a>将-8(%ebp)的地址赋值给%eax寄存器(p = &a)。可知foo()函数中的变量a存储在-8(%ebp)地址上,该地址向上8+4=12个单位就是返回地址((char *)&a + 12)。修改该地址内容(*p += 12)即可实现函数调用结束后跳转到第二个printf函数调用的位置。

     用gdb查看汇编指令刚进入foo时栈顶的值(%esp),如下所示:

技术分享图片 

     可见%esp值的确是调用foo后main中下条待执行指令的地址,而代码所修改的也正是该值。%eip则指向当前程序(foo)的指令地址。

以上是关于缓冲区溢出详解的主要内容,如果未能解决你的问题,请参考以下文章

缓冲区溢出漏洞实验

20165315 缓冲区溢出漏洞实验

20165302 缓冲区溢出漏洞实验

20165333 缓冲区溢出漏洞实验

20165318 缓冲区溢出漏洞实验

20199319 缓冲区溢出漏洞试验