使用单独的寄存器来存储返回地址?

时间:2017-07-26 00:25:41

标签: security assembly hardware exploit

我正在阅读漏洞利用是如何工作的,似乎很多漏洞通过覆盖堆栈上的返回地址来运行。为了使这更困难(堆栈金丝雀,ASLR,DEP等)已经付出了很多努力,但在我看来,硬件生产者更容易添加一个寄存器,只能通过调用和返回指令访问,这将保留返回地址。这样,根据定义,缓冲区溢出不能覆盖返回地址。因为call和ret仍然存在并且仍然像今天的CPU一样运行(唯一的区别在于它们存储返回地址),在我看来,兼容性问题不会太多。由于您使用寄存器而不是RAM来访问该地址,因此性能影响可能是正面的(尽管不显着)。

为了安全起见,英特尔显然有空间分配更多寄存器,因为MPX正在实施,尽管需要两个额外的寄存器。那么他们为什么不添加一个特殊的寄存器来存储返回地址呢?

1 个答案:

答案 0 :(得分:2)

这已经存在了。我知道三种架构和一种语言具有这样的功能:

  • SPARC有一个名为register windows的东西,其中CPU基本上保存并恢复寄存器一个函数调用/返回。按照惯例,返回地址存储在函数调用的寄存器o7中,然后在被调用者建立其堆栈帧时将其旋转到i7。当被调用者调用另一个函数时,该地址被旋转到内部寄存器堆栈中,不会被危险代码触及。
  • Knuth的MMIX具有类似的设计,但返回地址直接存储在函数调用中的大多数无法访问的寄存器堆栈中,因此非常符合您的要求。
  • ARM和ARM64只有一个链接寄存器。在函数调用中,返回地址存储在链接寄存器中,函数返回只是间接跳转到链接寄存器中的地址。这并不是真正做你想要的,因为链接寄存器的内容必须在嵌套函数调用中存储在堆栈中,除了叶函数(即不调用其他函数的函数)之外的其他任何东西都会失去额外的安全性。
  • Forth编程语言设计了一个用于值的堆栈和一个用于返回地址的单独堆栈。两个堆栈都可以由程序自由操作,但在操作返回堆栈时需要小心。实际上,这是通过使用一个用于返回堆栈的体系结构寄存器和一个用于数据堆栈的寄存器来实现的。这也解决了你提到的问题,但是一个足够聪明的程序员仍然可以通过允许错误的输入来粉碎返回堆栈而陷入困境。