[旧事物]我可以随便使用我的堆栈吗?

在Windows上,堆栈从大地址增加到小地址。 有时它是在体系结构上确定的,有时它只是一个公认的协议。 堆栈指针(处理器寄存器)的值是指向堆栈顶部值的指针。 并且位于堆栈深处的值分别位于大地址处。 但是,位于小于堆栈指针的地址处的数据会怎样?



某些(但不是全部)体系结构的约定定义了红色区域,红色区域是堆栈指针下的存储区域,但仍对应用程序有效。



对于Windows,红色区域的大小取决于硬件体系结构,并且通常为零。


建筑学红色区域大小
x860字节
x640字节
安腾16个字节*
阿尔法AXP0字节
MIPS320字节
动力电脑232字节**
ARM328字节
的ARM6416字节

* Itanium平台值得一提的功能是:红色区域位于堆栈指针上方 ,而不是下方。
**对于PowerPC,红色区域是通话协议的副作用


红色区域(位于堆栈下方)后面的任何内存均被视为易失性 ,操作系统可以随时对其进行更改。


但是说真的,为什么操作系统会关心我对堆栈的处理? 我的意思是,这是我的筹码! 操作系统没有告诉我如何处理通过VirtualAlloc分配的内存。 是什么使堆栈与任何其他内存不同?


考虑以下x86平台代码


  MOV [esp-4], eax ;  eax    MOV ecx, [esp-4] ;     ecx CMP ecx, eax ;  ? JNZ panic ; N:  -  

JNZ指令的注释说明

汇编编码约定说,如果分支完成,则对分支指令的注释应描述结果。 在上面的示例中,CMP指令询问问题“它们是否相同?”。 如果JNZ指令不相等,则跳转。 因此,注释以“ N:”开头,这意味着如果上一个问题的答案为“否”,则转换将完成,其余注释将描述执行转换时的情况。


汇编程序编码约定?

是的,我们有汇编程序的编码约定。


是否可以实施条件转换?


由于x86上没有红色区域,因此可以随时覆盖相对于堆栈指针具有负偏移的内存。 因此,对于以上代码,可以转换为panic标签。


调试器可以使用红色区域后面的内存作为方便的位置来存储其数据。 例如,如果使用.call命令 ,调试器将在同一堆栈上进行嵌套调用,并且可能会使用此堆栈空间的一部分来保存寄存器,以便可以在从被调用函数返回后将其还原。 因此,存储在红色区域之外的所有数据都将被破坏。


即使在正常运行期间,操作系统也可以随时覆盖红色区域之外的数据。 例如,这是怎么发生的:


假设您的线程在红色区域后面保存数据后立即运行其时隙。 在线程等待恢复执行时,内存管理器会暂时从代码中取出物理页面。 最后,您的线程再次获得控制权,并且内存管理器尝试换回代码页(页面切入)。 哦,不,分页期间发生I / O错误! 操作系统将STATUS_IN_PAGE_ERROR的异常帧推送到堆栈中,这将导致您保存在红色区域后面的数据损坏。


操作系统调度此异常。 它访问向量异常处理程序( VEH ),它是程序的另一部分。 该处理程序是专门为处理可能直接从CD-ROM或不可靠的网络FS启动程序而引起的特殊情况而安装的。 该程序显示一个请求,要求用户再次插入CD,并提出再次尝试的要求。 如果用户说需要重复什么,向量异常处理程序将返回EXCEPTION_CONTINUE_EXECUTION ,操作系统将重新启动发生异常的指令。


这次,重新启动成功,因为存在CD-ROM(并且已读取)并且可以将代码成功地泵入内存。 执行以下语句,将红色区域之外的值加载到ecx 。 但这与前STATUS_IN_PAGE_ERROR语句保存的值不同,因为STATUS_IN_PAGE_ERROR异常将其覆盖。 比较表明数据不同,因此我们进入了panic标签。


如果要将数据保存在堆栈上,请正确放置在其中:首先减少堆栈指针,然后将值保存在堆栈的有效部分中。 不要将数据隐藏在红色区域的后面,此存储可以在您不知情的情况下随时更改。

Source: https://habr.com/ru/post/zh-CN435814/


All Articles