美文网首页
缓冲区溢出的原因及危害

缓冲区溢出的原因及危害

作者: fooboo | 来源:发表于2016-06-27 23:12 被阅读2248次

    这篇文章主要介绍缓冲区溢出的一些问题及应对策略。

    什么是缓冲区溢出?大意就是为某特定的数据结构分配内存,然后修改内存时没有控制好截止条件,使得修改了边界之外的内存;比如在栈上分配的内存数组int iData[100],然后修改数组元素:

    for (int i = 0; i <= 100; ++i) {//todo},如果在32位机器上,可能分配多于100*sizeof(int)个字节的空间,然后iData[0]在低地址,iData[1]....在高地址,理论上这里修改iData[100]可能不会影响其他变量或调用函数时需要保存的参数;再比如

    char szToBuf[1024];

    char szFromBuf[2048];

    memcpy(szFromBuf, szToBuf, strlen(szFromBuf))就非常可能改写ebp,返回地址等引起严重的问题。这些都会因为编码问题或者不完全了解底层的实现,导致出现缓冲区溢出,进而引起可被利用的漏洞。以下列举比较隐晦的可能导致缓冲区溢出的情况:

    1由整数漏洞引起的问题,如溢出,截断,隐式转换等;以下分别列举相应的例子:

    整数溢出:一般指的是有符号整数溢出,结果值不能用该类型表示时就会发生,这种情况是未定义的行为,如int16_t i = INT16_MAX;i ++;那么在内存中原来i的位模式是011111***11111,然后加1后的补码为1000***0000是最小的负数了,那么有下面的代码:

    int16_t i = 0;

    while (i <= INT16_MAX)

    {

        iData[i] = 100;

     i ++;

    }

    那么这里就会发生溢出了,非法访问了iData[0]前面的内存,这段代码也是无限循环,这个例子有点特殊。

    截断:比如32的int,赋值给16位的int16_t,那么就取低16位,这里截断后的位模式不变,不管赋值给有符号的还是无符号的16位的类型,只是告诉编译器如何解释这些位模式。

    这里列举无符号的截断,如

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

    {

        unsigned short int iLen = 0;

        iLen = strlen(argv[1]) + strlen(argv[2]) + 1;

        char * pBuff = (char *)malloc(iLen);

        if (pBuff == NULL) { return -1; }

        strcpy(pBuff, argv[1]);

        strcat(pBuff, argv[2]);

    }

    这段代码的意思是以两个串的长度加1,开辟总的内存空间,然后拷贝两个串至pBuff中,外加个空白符结尾。这里的问题是当strlen(argv[1]) + strlen(argv[2])的和超过unsigned short int所能表示的最大值时,发生截断,最后iLen的值是小于min(strlen(argv[1], strlen(argv[2])))的,那么在拷贝时就会发生缓冲区溢出。

    再比如大学写的有问题的二分查找,有这么一行:

    int iMid = (iLeft + iRight) / 2;而这可能因为视iLeft和iRight的类型而定,如果都为无符号类型,那么可能两个数的和导致回绕,使得(iLeft + iRight)/2后的值小于min(iLeft,iRight),那么就取不到[iLeft,iRight]索引处的数据了;正确的写法应该是:int iMid = iLeft + (iRight-iLeft) / 2;如果为有符号,可能导致溢出,两个数的和为负数了,导致引用错误的内存地址;

    有/无符号的隐式转换:如把无符号赋值给有符号,有符号赋值给无符号,假设类型大小一致的情况下[大小不一致需要扩展],转换是位模式不变,变的是解释这些位的方式,值可能会改变。比如:

    求和:

    int computeSum(int iArray[],unsigned int iLen)

    {

           int iSum = 0;

      for (int i = 0; i <= iLen -1; ++i)

           {

                 //TODO

           }

          return iSum;

    }

    如果这样调用computeSum(***, 0),因为iLen为无符号,那么0-1为-1,有符号转换成无符号为INT32_MAX,就出现了非法内存访问;

    其他情况:如没有完全检测边界值:

    int * pValue = new[10];

    int insertValue(int iPos, int iValue)

    {

        if (iPos > 9)  //should be if (iPos > 9 || iPos < 0)

        {

            return -1;

        }

        pValue[iPos] = iValue;

       return 0;

    }

    如果iPos不小心是负数的话,代码块pValue[iPos]被编译器转换为如下形式:((char*)pValue + sizeof(int) * iPos),那么写入就会发生在pValue地址前面而引起一些问题;

    像很多C字符串库函数,比如strcpy,sprintf等都没有控制要操作的字节数,可能会导致多拷贝字节,而缓冲区溢出往往导致栈溢出,覆盖重要的ebp和函数返回地址,和其他变量,破坏栈。

    下篇会介绍下网络编程方面的知识。

    相关文章

      网友评论

          本文标题:缓冲区溢出的原因及危害

          本文链接:https://www.haomeiwen.com/subject/cwvgdttx.html