我知道程序堆栈看起来有点像这样(从高到低):
EIP | EBP | local variables
但我在哪里可以找到%eax
和其他通用寄存器?是否可以使用缓冲区溢出覆盖它们?
更新:最后,我甚至不必覆盖%eax
,因为事实证明该程序在某个时刻将%eax
指向用户输入。
答案 0 :(得分:6)
根据定义,寄存器不在RAM中。寄存器位于CPU中,没有地址,因此不能使用缓冲区溢出覆盖它们。但是,寄存器很少,因此编译器确实将它们用作最常用堆栈元素的缓存。这意味着虽然您无法溢出到 stricto sensu 寄存器中,但RAM中覆盖的值迟早会被加载到寄存器中。
(在Sparc CPU中,寄存器堆栈缓存策略甚至是硬连线。)
在您的架构中,EIP和EBP在堆栈上不;堆栈上相应的插槽是重新加载这两个寄存器的区域(在函数退出时)。另一方面,EAX是一个通用的寄存器,代码将在这里和那里使用,没有严格的约定。
答案 1 :(得分:2)
EAX可能永远不会出现在堆栈中。对于大多数x86编译器,EAX是32位返回值寄存器,永远不会保存在堆栈中,也不会从堆栈中恢复(64位系统中的RAX)。
这并不是说缓冲区溢出不能通过将可执行代码放在堆栈上来改变EAX的内容。如果操作系统没有禁用堆栈上的代码执行,可以这样做,或者你可以在堆栈上强制一个伪造的返回地址,它将控制转移到一个将值加载到EAX中的代码序列,但是这些很难拔下来。类似地,如果已知函数返回的值存储在局部变量中,则更改该变量的堆栈粉碎将更改复制到EAX的值,但优化编译器可以随心所欲地更改堆栈布局,因此可以使用的漏洞利用在一个版本上可能会在新版本或修补程序上完全失败。
答案 2 :(得分:2)
见Thomas Pornin(+1)和flouder(+1)的答案,他们很好。我想补充一个可能已被提及但未明确说明的补充答案,那就是注册溢出。
虽然"其中"最初的问题(至少在措辞上)似乎是基于错误的前提,即%eax 是在堆栈上,并且是一个寄存器,它不是{的堆栈的一部分{1}}(虽然你可以模拟堆栈上的任何硬件寄存器集,而某些架构实际上是这样做的,但这并不相关),顺便说一下,寄存器经常从堆栈中溢出/填充。因此,如果寄存器已经溢出到堆栈,则可以使用堆栈溢出来粉碎寄存器的值。这将要求您了解特定编译器的溢出机制,并且对于该函数调用,您需要知道%eax已被溢出,它已被溢出到哪里,并且踩踏该堆栈位置,以及何时它是下一个从其内存副本中填充,它获取新值。尽管看起来不太可能,但这些攻击通常都是通过阅读源代码并了解有关编译器的一些内容来激发的,所以它实际上并不是那么遥远。
有关寄存器溢出的更多信息,请参阅此内容