Windows 写在ESP下面有效吗?

Windows 写在ESP下面有效吗?,windows,assembly,x86,callstack,abi,red-zone,Windows,Assembly,X86,Callstack,Abi,Red Zone,对于32位windows应用程序,在不显式减少ESP的情况下,将ESP下的堆栈内存用于临时交换空间是否有效 考虑一个在ST(0)中返回浮点值的函数。例如,如果我们的值当前在EAX中 PUSH EAX FLD [ESP] ADD ESP,4 // or POP EAX, etc // return... 或者在不修改ESP寄存器的情况下,我们可以: MOV [ESP-4], EAX FLD [ESP-4] // return... 在这两种情况下都会发生相同的事情

对于32位windows应用程序,在不显式减少ESP的情况下,将ESP下的堆栈内存用于临时交换空间是否有效

考虑一个在
ST(0)
中返回浮点值的函数。例如,如果我们的值当前在EAX中

PUSH   EAX
FLD    [ESP]
ADD    ESP,4  // or POP EAX, etc
// return...
或者在不修改ESP寄存器的情况下,我们可以:

MOV    [ESP-4], EAX
FLD    [ESP-4]
// return...
在这两种情况下都会发生相同的事情,除了在第一种情况下,我们注意在使用内存之前减少堆栈指针,然后在使用内存之后增加堆栈指针。在后一种情况下,我们没有

尽管确实需要在堆栈上保留该值(可重入性问题、推送和读回值之间的函数调用等),但是否有任何根本原因导致这样写入ESP下面的堆栈无效

一般(与任何操作系统无关);在以下情况下书写ESP是不安全的:

  • 代码有可能被中断,中断处理程序将以相同的权限级别运行。注意:对于“用户空间”代码,这通常是不可能的,但是对于内核代码,这是极有可能的

  • 调用任何其他代码(被调用例程使用的
    调用
    或堆栈可能会破坏存储在ESP下的数据)

  • 其他一些取决于“正常”堆栈使用。这可能包括信号处理、(基于语言的)异常解除、调试器、“堆栈崩溃保护器”

写在下面是安全的,特别是在“不安全”的情况下

请注意,对于64位代码,在RSP下写入是内置于x86-64 ABI(“红色区域”)中的;通过在工具链/编译器和其他所有工具中对它的支持,它变得安全了。

在一般情况下(x86/x64平台)-可以随时执行中断,这会覆盖内存波纹管堆栈指针(如果它在当前堆栈上执行)。正因为如此,即使是临时保存堆栈指针下面的内容,在内核模式下也是无效的-中断将使用当前内核堆栈。但在用户模式下,另一种情况——windows构建中断表(IDT),即当出现中断时,它将始终在内核模式和内核堆栈中执行。因此,用户模式堆栈(堆栈指针下方)将不受影响。并且可能临时使用它指针下面的一些堆栈空间,直到您不执行任何函数调用为止。如果异常将是(比如访问无效地址)-也会覆盖下面的堆栈指针-cpu异常当然会在内核模式和内核堆栈中开始执行,但在用户空间中通过
ntdll.KiDispatchExecption在当前堆栈空间上执行回调。所以一般来说,这在windows用户模式下是有效的(在当前的实现中),但是您需要很好地理解您在做什么。然而,我认为这是非常罕见的


当然,在windows用户模式下,我们可以在堆栈指针下写入的注释中指出的正确程度,只是当前的实现行为。这没有记录或保证

但这是非常基本的——不太可能改变:中断总是只在特权内核模式下执行。内核模式将仅使用内核模式堆栈。用户模式上下文根本不受信任。如果用户模式程序设置了不正确的堆栈指针,会发生什么?说
mov-rsp,1
mov-esp,1
?在这个指令之后,中断将被触发。如果它开始在这种无效的esp/rsp上执行,会发生什么?所有的操作系统都崩溃了。正是因为此中断将仅在内核堆栈上执行。并且不会覆盖用户堆栈空间

还需要注意的是,堆栈空间有限(即使在用户模式下),在1页(4Kb)以下访问它已出错(需要逐页进行堆栈探测,以便向下移动保护页)

最后,真的没有必要通常访问EAX,EAX,在什么问题上先减少ESP?即使我们需要访问循环中的堆栈空间,大量的时间递减堆栈指针只需要一次额外的指令(不在循环中),性能或代码大小都不会发生任何变化

因此,尽管这在windows用户模式下是正确的工作方式,但更好地(而且不需要)使用它


当然,正式文件说明:

RSP当前地址之外的所有内存都被认为是易失性的

但这是常见情况,包括内核模式。我写了关于用户模式和基于当前实现的内容



可能在未来的windows中添加“直接”apc或一些“直接”信号-一些代码将在线程进入内核后通过回调执行(在通常的硬件中断期间)。在此之后,以下所有esp将被取消定义。但直到这种情况不存在。直到此代码将始终正常工作(在当前版本中)

创建线程时,Windows会为线程堆栈保留一个可配置大小(默认值为1 MB)的连续虚拟内存区域。最初,堆栈如下所示(堆栈向下增长):

ESP
将指向提交页面内的某个位置。保护页用于支持自动堆栈增长。保留页区域确保请求的堆栈大小在虚拟内存中可用

考虑问题中的两个说明:

MOV    [ESP-4], EAX
FLD    [ESP-4]
有三种可能性:

  • 第一条指令成功执行。没有任何东西可以使用用户模式堆栈在两条指令之间执行。因此,第二条指令将使用正确的值(@RbMm在其答案下的注释中说明了这一点,我同意)
  • 第一条指令引发异常,异常处理程序不会返回
    exception\u CONTINUE\u EXECUTION
    。只要第二条指令紧跟在第一条指令之后(它不在异常处理程序中或放在它之后),那么第二条指令就不会执行
    MOV    [ESP-4], EAX
    FLD    [ESP-4]