以及预防措施。
const int MAX_DATA_LENGTH = 32;
void DataCopy (char *szSrcData)
{
char szDestData[MAX_DATA_LENGTH];
strcpy(szDestData,szSrcData);
// processing codes
...
}
似乎这段代码不存在什么问题,但是细心的读者还是会发其中的危险。如果数据源szSrcDatar的长度不超过规定的长度,那么这段代码确实没什么问题。 strcpy()不会在乎数据来源,也不会检查字符串长度,唯一能让它停下来的只有字符串结束符'\0'。不过,如果没有遇到这个结束符,它就会一个字节 一个字节地复制szSrcData的内容,在填满32字节的预设空间后,溢出的字符就会取代缓冲区后面的数据。如果这些溢出的数据恰好覆盖了后面 DataCopy函数的返回地址,在该函数调用完毕后,程序就会转入攻击者设定的“返回地址”中,乖乖地进入预先设定好的陷阱。为了避免落入这样的圈套, 给作恶者留下可乘之机,当C/C++代码处理来自用户的数据时,应该处处留意。如果一个函数的数据来源不可靠,又要用到内存缓冲区,那么必须提高警惕,必 须知道内存缓冲区的总长度,并检验内存缓冲区。
const int MAX_DATA_LENGTH = 32;
void DataCopy (char *szSrcData, DWORD nDataLen)
{
char szDestData[MAX_DATA_LENGTH];
if(nDataLen < MAX_DATA_LENGTH)
strcpy(szDestData,szSrcData);
szDestData[nDataLen] = '\0'; // 0x42;
// processing code
...
}
首先,要获得szSrcData的长度,保证数据长度不大于最大缓冲区长度MAX_DATA_LENGTH;其次,要保证参数传来的数据长度真实有效,方 法就是向内存缓冲区的末尾写入数据。因为,当缓冲区溢出时,一旦向其中写入常量值,代码就会出错,终止运行。与其落入阴谋家的陷阱,还不如及时终止程序运 行。虽然上述方法能够有效地降低内存溢出的危害,却不能从根本上避免对内存溢出的攻击。所以在调用 strcpy、strcat、gets 等经典函数时,你要从源代码开始就提高警惕,尽量追踪传入数据的流向,向代码中的每一个假设提出质疑,包括对那些所谓相对安全可靠的改良版N- Versions(strncpy 或 strncat)也不可轻信。访问边界数据同样可能引起缓冲区溢出。在这种情况下的内存溢出不会像第一种那么危险,但同样令人讨厌。就如下面的代码片段:
const int DATA_LENGTH = 16;
Int data[16] = {1,9,8,4,0,9,1,7,1,9,8,7,0,3,0,9};
void PrintData()
{
for(int i=0; data[i]!=0&&i<DATA_LENGTH; i++)
{
cout<<data[i])<<endl;
}
}
这也是一个隐藏很深、难以发现的问题:当i==16的时候,在判断i< DATA_LENGTH的同时需要判断data[16]。而data[16]已经访问到了非法区域,可能引起缓冲区溢出。正确的方式应该是不要将索引号i 与数据本身data[i]的判断放在一起,而是将判断条件分成两句:
const int DATA_LENGTH = 16;
int data[16] = {1,9,8,4,0,9,1,7,1,9,8,7,0,3,0,9};
void PrintData()
{
for(int i=0; i<DATA_LENGTH; i++)
{
if(data[i]!=0)
cout<<data[i])<<endl;
}
}
C语言中的字符串库没有采用相应的安全保护措施,所以在使用时要特别小心。例如,在执行strcpy、strcat等函数操作时没有检查缓冲区大小,就会很容易引起安全问题。
现在分析下面的代码片段:const int MAX_DATA_LENGTH = 32;
void DataCopy (char *szSrcData)
{
char szDestData[MAX_DATA_LENGTH];
strcpy(szDestData,szSrcData);
// processing codes
...
}
似乎这段代码不存在什么问题,但是细心的读者还是会发其中的危险。如果数据源szSrcDatar的长度不超过规定的长度,那么这段代码确实没什么问题。 strcpy()不会在乎数据来源,也不会检查字符串长度,唯一能让它停下来的只有字符串结束符'\0'。不过,如果没有遇到这个结束符,它就会一个字节 一个字节地复制szSrcData的内容,在填满32字节的预设空间后,溢出的字符就会取代缓冲区后面的数据。如果这些溢出的数据恰好覆盖了后面 DataCopy函数的返回地址,在该函数调用完毕后,程序就会转入攻击者设定的“返回地址”中,乖乖地进入预先设定好的陷阱。为了避免落入这样的圈套, 给作恶者留下可乘之机,当C/C++代码处理来自用户的数据时,应该处处留意。如果一个函数的数据来源不可靠,又要用到内存缓冲区,那么必须提高警惕,必 须知道内存缓冲区的总长度,并检验内存缓冲区。
const int MAX_DATA_LENGTH = 32;
void DataCopy (char *szSrcData, DWORD nDataLen)
{
char szDestData[MAX_DATA_LENGTH];
if(nDataLen < MAX_DATA_LENGTH)
strcpy(szDestData,szSrcData);
szDestData[nDataLen] = '\0'; // 0x42;
// processing code
...
}
首先,要获得szSrcData的长度,保证数据长度不大于最大缓冲区长度MAX_DATA_LENGTH;其次,要保证参数传来的数据长度真实有效,方 法就是向内存缓冲区的末尾写入数据。因为,当缓冲区溢出时,一旦向其中写入常量值,代码就会出错,终止运行。与其落入阴谋家的陷阱,还不如及时终止程序运 行。虽然上述方法能够有效地降低内存溢出的危害,却不能从根本上避免对内存溢出的攻击。所以在调用 strcpy、strcat、gets 等经典函数时,你要从源代码开始就提高警惕,尽量追踪传入数据的流向,向代码中的每一个假设提出质疑,包括对那些所谓相对安全可靠的改良版N- Versions(strncpy 或 strncat)也不可轻信。访问边界数据同样可能引起缓冲区溢出。在这种情况下的内存溢出不会像第一种那么危险,但同样令人讨厌。就如下面的代码片段:
const int DATA_LENGTH = 16;
Int data[16] = {1,9,8,4,0,9,1,7,1,9,8,7,0,3,0,9};
void PrintData()
{
for(int i=0; data[i]!=0&&i<DATA_LENGTH; i++)
{
cout<<data[i])<<endl;
}
}
这也是一个隐藏很深、难以发现的问题:当i==16的时候,在判断i< DATA_LENGTH的同时需要判断data[16]。而data[16]已经访问到了非法区域,可能引起缓冲区溢出。正确的方式应该是不要将索引号i 与数据本身data[i]的判断放在一起,而是将判断条件分成两句:
const int DATA_LENGTH = 16;
int data[16] = {1,9,8,4,0,9,1,7,1,9,8,7,0,3,0,9};
void PrintData()
{
for(int i=0; i<DATA_LENGTH; i++)
{
if(data[i]!=0)
cout<<data[i])<<endl;
}
}
类似的问题还有可能发生在访问未初始化指针或失效指针时。未初始化的指针和失效后未置NULL的指针指向的是未知的内存空间,所以对这样的指针进行操作很有可能访问或改写未知的内存区域,也就可能引起缓冲区溢出的问题了。
原文:http://blog.csdn.net/baliguan163/article/details/11476535