栈溢出解决办法(栈溢出攻击的原理是什么)

数组越界指的是访问地址已经超出了当前数组的最大下标值,所以会产生溢出。C 语言对于数组引用不进行任何边界检查,并且会把【局部变量和返回地址】都存放在栈中。
我们先声明一个字符串,然后栈上会为它创建一定的空间,字符串本质上是字符数组,由一系列字符组成,并以特殊的终止符 \\’\\\\0\\’ 结束。
缓冲区溢出的原因
当字符串的长度超出了为数组分配的空间,就会产生缓冲区溢出。
我们先声明一个字符串,在栈中存储为【字符数组】,然后我们通过某个函数来增加字符串的长度。这种方法会存在一个问题,就是我们无法确认分配给字符串的栈空间是否足够容纳整个字符串。
因为栈空间大小是在编译时确定的,无法根据后续字符串的长度的变化而动态调整。所以我们将更大的字符串写入栈中,会导出缓冲区溢出。
缓冲区溢出会导致严重的安全漏洞和不可预测行为,例如覆盖其它变量、修改程序控制流、执行恶意代码等。
到底发生了什么
上面我们提到过,我们首先定义一个字符串,其次我们会执行一个函数,这个时候栈会添加一些内容用来维护函数的执行和返回地址(函数执行完后,返回到哪个地址,以便于继续执行下一个),包括但不限于:返回地址、局部变量、临时变量、函数参数等。
当字符串赋值时,超过栈空间大小后,栈溢出可能会破坏其他保存在栈上的状态信息,比如函数调用的上下文、局部变量和其他寄存器的值。这样的破坏可能导致程序执行出现不可预测的结果,例如修改了关键变量的值、错误的计算结果等。
这个漏洞有什么危险?
我们输入给程序一个字符串,这个字符串包含一些可执行代码的字节编码,称为攻击代码,另外,还有一些字节会用一个指向攻击代码的指针覆盖返回地址。由于输入的字符串长度超过了定义的长度,所以会溢出,多余的数据会覆盖栈上的返回地址。
所以最终在执行完子函数后,回到父函数的执行地址继续执行,这个时候就会跳转到攻击代码,因为它把【返回地址】更改了,这是一种常见的通过计算机网络攻击系统安全的方法。
幸运的是,现代的编译器和操作系统实现了很多限制,来对抗缓冲区溢出:
栈随机化
攻击者插入了攻击代码并不是最关键的,最关键的是它插入了执行这段代码的指针,也就是说,它能调用这个攻击函数,而产生这个指针,必须要知道当前字符串放置的栈地址。
在不同的机器之间,程序的栈位置是相当固定的,因此攻击者可以确定一个常见的 web 服务器所使用的栈空间,就可以设计出一个在许多机器上都能实施的攻击,这称为【安全单一化】。
栈随机化的思想,使得栈的位置在程序每次运行时都有变化,许多机器都运行同样的代码,但他们的栈地址是不同的,就是在程序开始的时候,我们在栈上分配一段 0 – n 字节之间的随机大小,空间程序不使用这段空间,但它会导致程序每次执行后续的栈位置发生变化。
分配的范围,必须够大才能获得足够多的栈地址变化,但又要足够小,不至于浪费程序太多空间。即使是这样,一个执着的攻击者也是能够用蛮力克服随机化,他可以反复用不同的地址进行攻击。
栈破坏检测
我们没有可靠的方法来防止对数组的越界写,但是我们能够知道当发生越界写,并造成任何有害结果之前检测到它。
这个思想是:在栈中的任意局部缓冲区与栈状态之间存储一个特殊的【金丝雀值】,在每一次程序运行时随机产生的,恢复寄存器状态和从函数返回之前,程序检查金丝雀值是否被改变了,如果是,则程序异常终止。
将金丝雀值放在一个特殊的段中标识为只读,这样攻击者就不能覆盖存储的金丝雀值了,在恢复寄存器状态和返回前,函数将存储在栈位置处的值与金丝雀值作比较,如果两个数相同,按正常方式完成,如果检测到被修改过,那代码就会调用一个错误处理。
限制可执行代码区域
这个防御方法是为了消除攻击者向系统中插入可执行代码的能力。
限制哪些内存区域能够存放可执行代码,在典型的程序中,只在保存编译器产生的代码的那部分内存才需要是可执行的,其它部分可以被限制为只允许读和写。
虚拟内存在逻辑上是被分成了页,典型每页是 2048 或 4096 个字节,许多系统允许控制三种访问形式:
读:从内存读数据;
写:存储数据到内存;
执行:将内存的内容看作机器级代码;
读和执行访问控制合并成一个 1 位的标志,这样被标记可读的页也都是可执行的。栈必须是既可读又可写的,因而栈上的字节也都是可执行的,这样对于栈的可控性太小,并且会有性能影响。
后来,在新的内存保护中引入了【NX = No-Execute】不执行位,将读和执行访问模式分开,有了这个特性后,栈可以被标记为可读可写,但是不执行。限制可执行的代码,这样也可以规避掉缓冲区溢出。

原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34571.html

(0)
小道研究的头像小道研究
上一篇 2024年4月19日
下一篇 2024年4月19日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注