如何避免溢出(计算机)

请说下,如何避免溢出(计算机)
最新回答
笑死大姨妈

2024-11-30 08:09:22

内存溢出已经是软件开发历史上存在了近40年的“老大难”问题,象在“红色代码”病毒事件中表现的那样,它已经成为黑客攻击企业网络的“罪魁祸首”。

如在一个域中输入的数据超过了它的要求就会引发数据溢出问题,多余的数据就可以作为指令在计算机上运行。据有关安全小组称,操作系统中超过50%的安全漏洞都是由内存溢出引起的,其中大多数与微软的技术有关。
微软的软件是针对台式机开发的,内存溢出不会带来严重的问题。但现在台式机一般都连上了互联网,内存溢出就为黑客的入侵提供了便利条件。

如何解决溢出内存问题

下面讨论内存溢出问题的解决和预防措施。

1、改用受控代码
2002 年 2 月和 3 月,微软公司展开了 Microsoft Windows Security Push 活动。在此期间,我所在的小组一共培训了超过 8500 人,教授他们如何在设计、测试和文档编制过程中解决安全问题。在我们向所有程序设计人员提出的建议中,有一条就是:紧跟微软公司软件开发策略的步伐,将某些应用程序和工具软件由原先基于本地 Win32 的 C++ 代码改造成基于 .NET 的受控代码。我们的理由很多,但其中最根本的一条,就是为了解决内存溢出问题。基于受控代码的软件发生内存溢出问题的机率要小得多,因为受控代码无法直接存取系统指针、寄存器或者内存。作为开发人员,你应该考虑(至少是打算)用受控代码改写某些应用程序或工具软件。例如:企业管理工具就是很好的改写对象之一。当然,你也应该很清楚,不可能在一夜之间把所有用 C++ 开发的软件用 C# 之类的受控代码语言改写。

2、遵守黄金规则
当你用 C/C++ 书写代码时,应该处处留意如何处理来自用户的数据。如果一个函数的数据来源不可靠,又用到内存缓冲区,那么它就必须严格遵守下列规则:
必须知道内存缓冲区的总长度。

检验内存缓冲区。

提高警惕。

让我们来具体看看这些“黄金规则”:
(1)必须知道内存缓冲区的总长度。
类似这样的代码就有可能导致 bug :
void Function(char *szName) {
char szBuff[MAX_NAME];
// 复制并使用 szName
strcpy(szBuff,szName);
}
它的问题出在函数并不知道 szName 的长度是多少,此时复制数据是不安全的。正确的做法是,在复制数据前首先获取 szName 的长度:
void Function(char *szName, DWORD cbName) {
char szBuff[MAX_NAME];
// 复制并使用 szName
if (cbName < MAX_NAME)
strcpy(szBuff,szName);
}
这样虽然有所改进,可它似乎又过于信任 cbName 了。攻击者仍然有机会伪造 czName 和 szName 两个参数以谎报数据长度和内存缓冲区长度。因此,你还得检检这两个参数!
(2)检验内存缓冲区
如何知道由参数传来的内存缓冲区长度是否真实呢?你会完全信任来自用户的数据吗?通常,答案是否定的。其实,有一种简单的办法可以检验内存缓冲区是否溢出。请看如下代码片断:
void Function(char *szName, DWORD cbName) {
char szBuff[MAX_NAME];
// 检测内存
szBuff[cbName] = 0x42;
// 复制并使用 szName
if (cbName < MAX_NAME)
strcpy(szBuff,szName);
}
这段代码试图向欲检测的内存缓冲区末尾写入数据 0x42 。你也许会想:真是多此一举,何不直接复制内存缓冲区呢?事实上,当内存缓冲区已经溢出时,一旦再向其中写入常量值,就会导致程序代码出错并中止运行。这样在开发早期就能及时发现代码中的 bug 。试想,与其让攻击者得手,不如及时中止程序;你大概也不愿看到攻击者随心所欲地向内存缓冲区复制数据吧。
(3)提高警惕
虽然检验内存缓冲区能够有效地减小内存溢出问题的危害,却不能从根本上避免内存溢出攻击。只有从源代码开始提高警惕,才能真正免除内存溢出攻击的危胁。不错,上一段代码已经很对用户数据相当警惕了。它能确保复制到内存缓冲区的数据总长度不会超过 szBuff 的长度。然而,某些函数在使用或复制不可靠的数据时也可能潜伏着内存溢出漏洞。为了检查你的代码是否存在内存溢出漏洞,你必须尽量追踪传入数据的流向,向代码中的每一个假设提出质疑。一旦这么做了,你将会意识到其中某些假设是错误的;然后你还会惊讶地叫道:好多 bug 呀!
在调用 strcpy、strcat、gets 等经典函数时当然要保持警惕;可对于那些所谓的第 n 版 (n-versions) strcpy 或 strcat 函数 —— 比如 strncpy 或 strncat (其中 n = 1,2,3 ……) —— 也不可轻信。的确,这些改良版本的函数是安全一些、可靠一些,因为它们限制了进入内存缓冲区的数据长度;然而,它们也可能导致内存溢出问题!请看下列代码,你能指出其中的错误吗?
#define SIZE(b) (sizeof(b))
char buff[128];
strncpy(buff,szSomeData,SIZE(buff));
strncat(buff,szMoreData,SIZE(buff));
strncat(buff,szEvenMoreData,SIZE(buff));
给点提示:请注意这些字符串函数的最后一个参数。怎么,弃权?我说啊,如果你是执意要放弃那些“不安全”的经典字符串函数,并且一律改用“相对安全”的第 n 版函数的话,恐怕你这下半辈子都要为了修复这些新函数带来的新 bug 而疲于奔命了。呵呵,开个玩笑而已。为何这么说?首先,它们的最后一个参数并不代表内存缓冲区的总长度,它们只是其剩余空间的长度;不难看出,每执行完一个 strn... 函数,内存缓冲区 buff 的长度就减少一些。其次,传递给函数的内存缓冲区长度难免存在“大小差一”(off-by-one)的误差。你认为在计算 buff 的长度时包括了字符串末尾的空字符吗?当我向听众提出这个问题时,得到的肯定答复和否定答复通常是 50 比 50 ,对半开。也就是说,大约一半人认为计算了末尾的空字符,而另一半人认为忽略了该字符。最后,那些第 n 版函数所返回的字符串不见得以空字符结束,所以在使用前务必要仔细阅读它们的说明文档。

3、编译选项 /GS
“/GS”是 Visual C++ .NET 新引入的一个编译选项。它指示编译器在某些函数的堆栈帧(stack-frames) 中插入特定数据,以帮助消除针对堆栈的内存溢出问题隐患。切记,使用该选项并不能替你清除代码中的内存溢出漏洞,也不可能消灭任何 bug 。它只是亡羊补牢,让某些内存溢出问题隐患无法演变成真正的内存溢出问题;也就是说,它能防止攻击者在发生内存溢出时向进程中插入和运行恶意代码。无论如何,这也算是小小的安全保障吧。请注意,在新版的本地 Win32 C++ 中使用 Win32 应用程序向导创建新项目时,默认设置已经打开了此选项。同样,Windows .NET Server 环境也默认打开了此选项。关于 /GS 选项的更多信息,请参考 Brandon Bray 的《Compiler Security Checks In Depth》一书。

所谓定点数溢出是指定点数的运算结果的绝对值大于计算机能表示的最大数的绝对值。浮点数的溢出又可分为“上溢出”和“下溢出”两种,“上溢出”与整数、定点数的含义相同,“下溢出”是指浮点数的运算结果的绝对值小于机器所能表示的最小数绝对值,此时将该运算结果处理成机器零。若发现溢出(上溢出),运算器将产生溢出标志或发出中断请求,当溢出中断未被屏蔽时,溢出中断信号的出现可中止程序的执行而转入溢出中断处理程序。<BR><BR>
例如:有两个数0.1001111和0.1101011相加,其结果应为1.0111010。由于定点数计算机只能表示小于1的数,如果字长只有8位,那么小数点前面的1,会因没有触发器存放而丢失。这样,上述两个数在计算机中相加,其结果变为0.0111010。又如,有两个数0.0001001和0.00001111相乘,其结果应为0.00000000111111,若字长只有8位,则结果显示为0.0000000,后面的1个0和6个1全部丢失,显然这个结果有误差。计算机的任何运算都不允许溢出,除非专门利用溢出做判断,而不使用所得的结果。所以,当发生和不允许出现的溢出时,就要停机或转入检查程序,以找出产生溢出的原因,做出相应的处理。
暖妹

2024-11-30 08:49:58

  1. 指针的位置要指对

  2. 数组大小开的要合适

  3. 大数据进行运算时考虑分布式运算