是否可以使用缓冲区溢出覆盖%eax?

问题描述:

我知道一个程序栈看起来有点像这样(从高到低):是否可以使用缓冲区溢出覆盖%eax?

EIP | EBP | local variables 

但我在哪里可以找到%eax,以及其他通用寄存器?是否有可能使用缓冲区溢出覆盖它们?

更新:最后,我甚至不需要覆盖%eax,因为事实证明程序指出%eax在某些时刻用户输入。

+2

这取决于所使用的调用约定,并可能对编译器的优化决策。 – Mark 2014-11-03 10:27:34

+1

对于您的问题,更好的标题可能是“是否可以使用缓冲区溢出覆盖寄存器?”因为如前所述,它假定一个错误的前提,严格地说(尽管顺便说一句,至少在一种情况下它是正确的) – codenheim 2014-11-03 19:38:37

+0

你是对的,我改变了它。 – LonelyWebCrawler 2014-11-04 00:37:26

A 寄存器根据定义,不在RAM中。寄存器位于CPU中,没有地址,因此无法用缓冲区溢出覆盖它们。然而,寄存器非常少,所以编译器确实将它们用作最常用堆栈元素的一种缓存。这意味着虽然你不能溢出到严格意义上的寄存器,但RAM中覆盖的值迟早会被加载到寄存器中。

(在CPU的Sparc,寄存器,如堆栈缓存政策,即使硬连线)。

在您的架构,EIP和EBP是在堆栈上不;堆栈上相应的槽是这两个寄存器将被重新加载(在函数退出时)的区域。另一方面,EAX是一个通用寄存器,代码将在这里和那里使用,没有严格的约定。

+0

谢谢,我应该知道这一点。 – LonelyWebCrawler 2014-11-03 11:45:40

+0

+1“sensu stricto” – codenheim 2014-11-03 19:34:46

EAX可能永远不会出现在堆栈上。对于大多数x86编译器而言,EAX是32位的返回值寄存器,永远不会保存在堆栈中,也不会从堆栈中恢复(64位系统中的RAX)。

这并不是说缓冲区溢出不能用于通过将可执行代码放在堆栈上来改变EAX的内容;如果堆栈上的代码执行未被OS禁用,可以这样做,也可以在堆栈上强制一个伪造的返回地址,该地址将控制权转移给将值加载到EAX中的代码序列,但这些很难拔下来。同样,如果已知某个函数返回的值存储在局部变量中,则更改该变量的堆栈粉碎将会更改复制到EAX的值,但优化编译器可以随时更改堆栈布局,因此可以工作的漏洞在一个版本上可能会在新版本或修补程序上完全失败。

+0

感谢您的详细信息。这一切都是有道理的,除了这个:“对于大多数x86编译器EAX是32位返回值寄存器”。我认为EAX只是一个通用注册表。不是返回值存储在其他地方吗? – LonelyWebCrawler 2014-11-03 17:09:57

+1

不同的操作系统有不同的调用约定,但我从来没有见过没有使用EAX(或AX或RAX)来记录函数返回值的操作系统。执行CALL后,您通常会看到EAX中的值被检查或存储。 – 2014-11-04 00:53:35

+0

谢谢,我不知道。 – LonelyWebCrawler 2014-11-04 00:57:18

查看Thomas Pornin(+1)和flouder的(+1)答案,它们都很好。我想补充一个可能已经提到的补充答案,但没有具体说明,那就是注册溢出。

尽管原始问题的“where”(至少在措词上)似乎基于堆栈上%eax 的错误前提,并且是一个寄存器,但它不是堆栈的一部分在x86(虽然你可以模拟堆栈上的任何硬件寄存器集,并且一些架构实际上这样做,但这不相关),顺便说一句,寄存器经常从堆栈溢出/填充。因此,如果寄存器溢出到堆栈,就可以利用堆栈溢出来摧毁寄存器的值。这需要你知道特定编译器的溢出机制,并且对于那个函数调用,你需要知道%eax已经被溢出,溢出的位置以及堆栈的位置,以及下一次的时间从其内存副本中填充,它获得新的价值。尽管看起来不太可能,但这些攻击通常是通过阅读源代码,并了解有关编译器的问题而得到启发,因此它并不是那么遥不可及。

见本作更多的阅读对寄存器溢出

gcc argument register spilling on x86-64

https://software.intel.com/en-us/articles/dont-spill-that-register-ensuring-optimal-performance-from-intrinsics

+0

感谢您的信息,我会阅读它。这看起来就像我需要的一样。 – LonelyWebCrawler 2014-11-04 00:40:29