assembly - 让调用堆栈向上增长会使缓冲区溢出更安全吗?

标签 assembly x86 stack intel buffer-overflow

每个线程都有自己的堆栈来存储局部变量。但是栈也习惯了store return addresses调用函数时。

在 x86 汇编中,esp指向最近分配的堆栈端。今天,大多数 CPU 的堆栈都呈负增长。此行为通过溢出缓冲区并覆盖保存的返回地址来启用任意代码执行。如果堆栈正向增长,则此类攻击将不可行。

让调用堆栈向上增长是否更安全?为什么 Intel 设计 8086 时堆栈向下增长?他们能否在任何后来的 CPU 中改变一些东西,让现代 x86 拥有向上增长的堆栈?

最佳答案

有趣的一点;大多数缓冲区溢出确实会超过结尾,而不是在开头之前,因此这几乎肯定会有所帮助。编译器可以将局部数组放在堆栈帧中的最高地址,因此不会有任何标量局部变量要覆盖位于数组之后的位置。

但是,如果将本地数组的地址传递给另一个函数,仍然存在危险。因为被调用函数的返回地址将位于数组末尾之后。

unsafe() {
    char buf[128];
    gets(buf);      // stack grows upward: exploit happens when gets executes `ret`
    // stack grows down: exploit happens when the `ret` at the end of *this* function executes.
}

因此,可能仍然存在大量缓冲区溢出 .当不安全的数组写入代码被内联时,这个想法只会阻止缓冲区溢出,因此溢出发生在数组之上没有任何重要的东西。

然而,缓冲区溢出的一些其他常见原因很容易被内联,例如 strcat . 向上增长的堆栈有时会有所帮助。

安全措施不一定要万无一失才能有用,因此有时这肯定会有所帮助。对于想要改变像 x86 这样的现有架构的人来说可能还不够,但是 新架构的有趣想法 .不过,堆栈增长几乎是 CPU 中的通用标准。有什么东西使用向上增长的调用堆栈吗?有多少软件实际上取决于该假设?希望不多...

传统的布局为堆和/或栈的增长留出了空间,只有当它们在中间相遇时才会出现问题。

可预测的代码/数据地址比可预测的堆栈地址更重要,因此具有更多 RAM 的计算机可以将堆栈与数据/代码放置得更远,同时仍然在恒定地址加载代码/数据。 (这是非常手动的。我认为自己很幸运没有写过实际的 16 位程序,并且只学习了但没有使用过分段。也许还记得 DOS 的人可以在这里阐明为什么它可以很好地使用堆栈在高地址,而不是段底部的向上增长堆栈和顶部的数据/代码。例如,使用“小”代码模型,其中所有内容都在一个段中)。

改变这种行为的唯一真正机会是使用 AMD64 ,这是 x86 第一次真正打破向后兼容性。现代 Intel CPU 仍然支持 8086 未记录的操作码,例如 D6 : SALC (Set AL from Carry Flag) , 限制了 ISA 扩展的编码空间。 (例如 SSSE3 and SSE4 instructions would be 1 byte shorter 如果英特尔放弃了对未记录的操作码的支持。

即便如此,它也只适用于新模式; AMD64 CPU 仍然必须支持传统模式,当处于 64 位模式时,它们必须将长模式与兼容模式混合使用(通常从 32 位二进制文​​件运行 32 位用户空间进程)。

AMD64 可能会添加一个堆栈方向标志,但这会使硬件更加复杂。正如我上面所说,我认为这不会对安全有很大好处。否则,也许 AMD 架构师会考虑它,但仍然不太可能。他们肯定的目标是将侵入性降至最低,并且不确定它会流行起来。如果世界上大多只是继续运行 32 位操作系统和 32 位代码,他们不想被额外的负担困住以保持其 CPU 中的 AMD64 兼容性。

真可惜,因为他们本来可以做很多小事,而执行单元中可能不需要太多额外的晶体管。 (例如,在长模式下,将 setcc r/m8 替换为 setcc r/m32 )。

关于assembly - 让调用堆栈向上增长会使缓冲区溢出更安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39187276/

相关文章:

assembly - RISC-V 汇编语法中的混合目标/源操作数顺序

c - %p 在 printf 中的用法?

c - 未找到输入符号

x86 - x86 CPU 上中断延迟的估计

c++ - 存储指令是否在高速缓存未命中时阻止后续指令?

汇编 addq 澄清

c++ - 从 char 到 string 的无效转换

你能在堆栈上定义一个数组并将指针传递给全局变量吗?

assembly - 为什么我的引导加载程序的堆栈段位于 0x3FF(实模式 IVT 的末尾)?

c - 段错误内联汇编