C语言 – 访问数组越界有多危险?

访问其边界之外的数组有多危险(在C中)?有时候我会从数组外部读取(我现在理解我然后访问我的程序的某些其他部分使用的内存,甚至超出了它)或者我试图将值设置为数组之外的索引。该程序有时会崩溃,但有时只是运行,只会产生意想不到的结果。

现在我想知道的是,这真的有多危险?如果它损坏了我的程序,那就不是那么糟糕了。另一方面,如果它打破了我的程序之外的东西,因为我以某种方式设法访问一些完全不相关的内存,那么它是非常糟糕的,我想。我读了很多“任何可能发生的事情”,“细分可能是最不好的问题”,“你的硬盘可能变成粉红色,独角兽可能会在你的窗下唱歌”,这很好,但真正的危险是什么?

我的问题:

  1. 除了我的程序之外,从数组外部读取值会损坏任何内容吗?我会想象只是看事情不改变任何东西,或者它会改变我碰巧触及的文件的’last time opened’属性?
  2. 除了我的程序之外,在数组外部设置值可以破坏任何东西吗?从这个 Stack Overflow问题我收集到可以访问任何内存位置,没有安全保证。
  3. 我现在从XCode中运行我的小程序。这是否为我的程序提供了一些额外的保护,它无法到达自己的内存之外?它会伤害XCode吗?
  4. 有关如何安全地运行我的固有错误代码的任何建议?

我使用OSX 10.7,Xcode 4.6。


就ISO C标准(语言的官方定义)而言,访问其边界之外的数组具有“ 未定义的行为 ”。字面意思是:

行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准不对此要求

非规范性说明扩展了这一点:

可能的未定义行为包括完全忽略不完整结果的情况,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息)。

这就是理论。现实是什么?

在“最佳”的情况下,你会获得一些片的那要么你当前运行的程序(这可能会导致程序无法正常运作)拥有的内存,或者某个被你的当前运行程序所拥有(这可能会导致你的程序像分段错误一样崩溃)。或者您可能尝试写入程序拥有的内存,但标记为只读; 这可能也会导致程序崩溃。

这假设您的程序在一个操作系统下运行,该操作系统试图保护彼此同时运行的进程。如果您的代码在“裸机”上运行,比如说它是操作系统内核或嵌入式系统的一部分,那么就没有这样的保护; 你行为不端的代码应该提供保护。在这种情况下,损坏的可能性要大得多,包括在某些情况下对硬件(或附近的物品或人)的物理损坏。

即使在受保护的操作系统环境中,保护也不总是100%。例如,存在允许非特权程序获得根(管理)访问的操作系统错误。即使具有普通用户权限,故障程序也会消耗过多的资源(CPU,内存,磁盘),可能会导致整个系统崩溃。许多恶意软件(病毒等)利用缓冲区溢出来获得对系统的未授权访问。

(一个历史的例子:我听说在一些带有核心内存的旧系统中,在紧密循环中重复访问单个内存位置可能会导致大量内存融化。其他可能性包括破坏CRT显示和移动读取/写入驱动器柜的谐波频率的磁盘驱动器的磁头,使其走过一张桌子并掉到地板上。)

天网总是担心。

底线是:如果你可以写一个程序做坏事故意,这至少在理论上有可能是一个错误的程序可以做同样的事情意外

在实践中,这是非常不可能的MacOS X的系统上运行你的错误的程序会做什么比暴跌更加严重。但是不可能完全防止错误的代码做坏事。


通常,今天的操作系统(无论如何都是流行的)使用虚拟内存管理器在受保护的内存区域中运行所有应用程序。事实证明,简单地读取或写入已分配/分配给您的进程的区域之外的REAL空间中存在的位置并不是非常简单(按照说)。

直接答案:

1)读取几乎不会直接损坏另一个进程,但如果您碰巧读取用于加密,解密或验证程序/进程的KEY值,它可能会间接损坏进程。如果您根据正在阅读的数据做出决策,那么读取越界可能会对您的代码产生一些不利/意外的影响

2)通过写入可通过内存地址访问的loaction来实现损坏的唯一方法是,您写入的内存地址实际上是硬件寄存器(实际上不是用于数据存储而是用于控制某个部分的位置)硬件)不是RAM位置。事实上,除非你正在写一些不可重写的可编程位置(或那种性质的东西),否则你通常不会损坏某些东西。

3)通常从调试器内运行在调试模式下运行代码。在调试模式下运行TEND(但并不总是)在您完成某些未经考虑的事情或完全非法的情况下更快地停止代码。

4)永远不要使用宏,使用已经内置数组索引边界检查的数据结构等….

另外 我应该补充一点,上述信息实际上仅适用于使用带内存保护窗口的操作系统的系统。如果为嵌入式系统甚至是使用没有内存保护窗口(或虚拟寻址窗口)的操作系统(实时或其他)的系统编写代码,那么在读取和写入内存时应该更加谨慎。同样在这些情况下,应始终采用SAFE和SECURE编码实践来避免安全问题。


不检查边界可能导致丑陋的副作用,包括安全漏洞。其中一个丑陋的是任意代码执行。在经典示例中:如果您有一个固定大小的数组,并且用于strcpy()在其中放置用户提供的字符串,则用户可以为您提供一个溢出缓冲区的字符串并覆盖其他内存位置,包括CPU在您的函数时应返回的代码地址饰面。

这意味着您的用户可以向您发送一个字符串,该字符串将使您的程序基本上调用exec("/bin/sh"),这会将其转换为shell,在您的系统上执行他想要的任何操作,包括收集所有数据并将您的计算机转换为僵尸网络节点。

有关如何完成此操作的详细信息,请参阅Smashing The Stack for Fun和Profit

Tags:, ,

添加评论

友情链接:蝴蝶教程