在您最喜欢的搜索引擎上进行简单搜索,您将很快看到有多少计算机安全损害和/或故障是由所谓的缓冲区溢出引起的。例如,以下摘自2000年11月3日风险第21卷第9期:
日期:Thu,2000年11月2日17:57:09 PST 来自:“Peter G. Neumann”<[email protected]> 主题:空中交通管制的困境 2000年10月19日,数百架飞机因为一架飞机而着陆或延误 洛杉矶空中交通管制系统中的软件问题。原因 被归因于墨西哥控制器输入9(而不是5)个字符 飞行描述数据,导致缓冲区溢出。 2000年10月23日,在Fremont区域中心的计算机故障, 加利福尼亚州,导致北加利福尼亚州所有飞行计划的损失 和内华达西部; 系统在维护后无法工作 晚上前。 因此,联邦航空局暂停了 在ATC系统中安装新的软件升级,直到另行通知。 [资料来源:各种来源的各种新闻]
这是一个很好的例子,因为我将告诉你如何简单的 像这样是为了防止-差的程序员就是不能抽出时间来彻底。首先,我将尝试解释什么是非技术术语中的缓冲区溢出。然后我将演示一些简单的C代码。
当程序员编写代码时,他们经常必须分配缓冲区来保存传入数据。该数据可以来自磁盘上的文件,或者来自操作者从键盘输入。他们必须正式分配这样的内存的原因是程序不能简单地占用他们需要的任何内存; 他们必须从主机“请求”许可。计算机负责内存管理,因此与一个程序关联的内存不会从另一个程序“跳跃”内存。这不会是好的。
当编写这样的代码时,程序员必须花费时间思考这些数据的大小,以及需要多少内存。在很多情况下,他们的工作可以通过让他们的代码动态分配内存来缓解。这通常是用工具如完成malloc()
。如果程序员不认为他们的缓冲区相对于他们的数据的大小,然后缓冲区溢出可能发生。
简单地说,这时,一个缓冲区溢出可能会当去往特定缓冲区中的数据比分配给该缓冲区的内存较大的进行。
Getting a bit more language specific...
在C语言中,缓冲区溢出会导致所谓的未定义的行为。这正是它的声音:任何可能发生。没有人知道会发生什么。有时,它可能会崩溃的程序。其他时候,它可能根本不会产生你期望的结果。而其他时候,它可能导致数百个航班接地,而ATC系统重新启动。很多时候,在简单的程序中,没有什么会发生。但关键点是要明白的是,行为是未定义的 - 任何事情都可能发生。现在让我们来看看一些示例代码:
#include <stdio.h> #define BUFFER_SIZE 16 int 主(空) { char fName [BUFFER_SIZE]; printf(“请输入您的名字:”); scanf(“%s”,fName); printf(“Hello,%s \ n”,fName); return 0; }}
这是有效的ANSI C代码,将在各种平台上编译。让我们来看看程序的核心部分,并讨论它的功能:
char fName [BUFFER_SIZE];
这定义了大小为BUFFER_SIZE的字符串,在这种情况下为16.为了正确,此字符串将最多包含15个字符和一个NULL字符,因为一个字符串被定义为NULL终止。
printf(“请输入您的名字:”);
这条线只是提示操作者输入他/她的名字。
scanf(“%s”,fName);
这将接受操作员提供的输入并将其填充到我们先前定义的字符串中。
printf(“Hello,%s \ n”,fName);
最后,我们只打印一个问候语给操作员。
Trying out the example
这里是一个运行我们的示例程序的模拟:
$ ./name 请输入您的名字:john 你好,约翰 $
工作伟大,对吧?使用这种方式,这个程序将无缺陷地工作,只要你需要它。但是如果一个好奇的运营商来了,并决定他的名字需要‘lkasdjfklasjdfkljsadfiwojehfioajhdsofjaklsjdf‘的一天?让我们来看看:
$ ./name 请输入您的名字:lkasdjfklasjdfkljsadfiwojehfioajhdsofjaklsjdf 您好:lkasdjfklasjdfkljsadfiwojehfioajhdsofjaklsjdf 分段故障(堆芯转储) $
发生了什么?好了,我们告诉程序,我们将提供一个不超过16个字符(实际上,15加一个NULL)的字符串。我们刚刚提供了45个字符。简单的数学告诉我们,45绝对比16,我们刚刚创建了一个缓冲区溢出 -我们的数据溢出我们已经分配给它的缓冲区。回想一下这种行为在C中是未定义的。我们很幸运 - 我们的程序简单地经历了分段错误(segfault)。在Unix世界中,这通常意味着我们访问了我们没有分配给我们的内存。在另一台计算机或平台上,可能发生了许多其他事情,因为行为是未定义的。
So what can a programmer do?
那么,一个直接的想法是简单地增加 BUFFER_SIZE
。如果采取一看 /usr/include/stdio.h
,有一个BUFSIZ 1024
供您使用。你应该使用吗?也许不是,作为一个人,无辜或不,可能会溢出,在某一点。由于没有对输入进行检查,这几乎可以保证最终发生。
幸运的是,C程序员有一个在ANSI C标准中定义的工具来解决这个特殊的问题:enter fgets()
的功能fgets()
被设计为防止延伸超过分配给它的存储器的输入。通过设计,它保证了这一点。让我们使用我们的新函数来看看我们修改后的代码:
#include <stdio.h> #define BUFFER_SIZE 16 int 主(空) { char fName [BUFFER_SIZE]; printf(“请输入您的名字:”); fgets(fName,sizeof(fName),stdin); printf(“Hello,%s \ n”,fName); return 0; }}
通过使用fgets()
,我们保证不会溢出我们的缓冲区。让我们来看一个示例运行:
$ ./fname 请输入您的名字:kildallA Little Bit about Buffer Overflows 你好,XX $
注意,输出中有两个空行。这是因为 fgets()
保留了换行符。摆脱它留给程序员一个练习。无论如何,让我们来看看我们好奇的运算符会发生什么:
$ ./fname 请输入您的名字:laksdfjklajdfkljdsafkljasdklfjkwjeifjwio 您好,laksdfjklajdfkl $
这次我们传入了一个40个字符的名字。如所承诺的, fgets()
只花了15个字符加上空-这正是我们想要的东西。
Summary
我希望我已经能够证明在一个编写程序不好的计算机上造成各种各样的破坏是多么容易。我希望我也展示了用简单的编程技术来防止这样的事情是多么容易。
如果你看看安全相关的网站,你会发现,相当多的安全妥协是由缓冲区溢出造成的。有些是这么简单 - 参考本文顶部的ATC关机。我猜想在这种情况下溢出只是因为程序员没有想到以某种方式验证输入。我确信,墨西哥控制器意味着没有伤害,但想象如果/有人有不良的意图在这里工作,可能会发生什么。其他情况由与例如网络服务守护程序上的套接字编程相关的缓冲区溢出引起。网络服务守护程序的示例可能是Web服务器。涉及此类程序的漏洞是特别危险的,因为它们通常导致特权和/或管理帐户被盗用。事实上,这个确切的漏洞是什么原因造成的1987年互联网蠕虫名叫罗伯特·莫里斯一个家伙打造一个流氓程序在利用此 fingerd
程序。然后他能够在主机上获得root访问权限。