2017-07-26 247 views
4

我在阅读关于漏洞利用的工作方式,似乎很多漏洞都是通过覆盖堆栈中的返回地址来操作的。为了使这变得更加困难(堆栈金丝雀,ASLR,DEP等)已经付出了很多努力,但在我看来,硬件制造商添加寄存器会更容易,只能通过调用和ret指令访问,那将保存返回地址。这样,根据定义,返回地址不会被缓冲区溢出覆盖。因为call和ret仍然存在,并且仍然像当今的CPU一样运行(唯一的区别是它们存储返回地址的地方),所以在我看来,兼容性问题不会太多。由于您使用的是寄存器而不是RAM来访问地址,因此性能影响可能是正面的(尽管无关紧要)。使用单独的寄存器来存储返回地址?

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

+0

这不是一个完全不合理的想法,我已经知道了它的地方(不幸的是我不记得在哪里)。房间里的大象是我们需要一个堆栈结构来允许任意嵌套的函数调用。一个单独的调用/ ret堆栈(这是我之前提到的想法)似乎更强大。兼容性问题是不可避免的,有些工具依赖于函数调用期间堆栈的填充情况,但我相信我们可以负担(又一个)过渡期。 –

+0

那么你会如何解决嵌套调用? (除了将值存储到内存中,然后可以通过代码以任何方式操作) – Ped7g

+1

具有片上返回地址的性能优势可以通过[返回地址缓存](https://priorart.ip。)来实现。 com/IPCOM/000108056)来预测RET指令的结果。 –

回答

2

这已经存在了。我知道三种架构和一个语言,这样的特点:

  • SPARC有一种叫做register windows其中基本CPU保存和恢复寄存器中的一个函数调用/返回。按照惯例,函数调用将返回地址存储在寄存器o7中,然后在被调用者建立其堆栈帧时将其返回至i7。当被调用者调用另一个函数时,该地址将被转移到内部寄存器堆栈中,不会被危险代码触发。
  • Knuth的MMIX也有类似的设计,但返回地址直接存储在函数调用大部分是人迹罕至,寄存器堆栈,所以几乎你想要什么。
  • ARM和ARM64只有一个链接寄存器。在函数调用时,返回地址存储在链接寄存器中,函数返回只是间接跳转到链接寄存器中的地址。由于链接寄存器的内容必须以嵌套函数调用的形式存储在堆栈中,因此这并没有真正达到你想要的效果,从而在除了叶函数(即不调用其他函数的函数)之外的任何其他安全性中失败。
  • Forth编程语言在设计上有一个用于值的堆栈和一个用于返回地址的单独堆栈。这两个堆栈都可以通过程序自由操作,但在操作返回堆栈时需要小心。在实践中,这是通过使用体系结构的寄存器之一来实现的,并且一个用于数据堆栈。这也解决了你提到的问题,但一个足够聪明的程序员仍然可以通过允许错误的输入来粉碎返回栈来搞砸。
+0

注册窗口只是一个硬件功能,它阻碍了硬件流水线以及更好的编译器优化......它们不是您正在寻找的功能。他们是Sparc必须忍受的一个缺陷。 – cb88

+0

@ cb88他们如何阻碍流水线? – fuz

+0

最初是因为编译器太笨,无法很好地安排寄存器加载/存储指令,然而这已经改变,并且注册窗口实际上比一个好的编译器更慢,更省电......硬件中的注册窗口也使得管道变得更加复杂,并增加了失速和气泡的可能性,具体取决于它的实施方式......现在没有其他处理器出于正当理由这样做了。而Sparc只会这样做,因为他们必须因为它在规范中。 – cb88