尽管系统上运行了许多函数,但堆栈指针(ESP)的值怎么可能每次都相同,对于堆栈上运行的每个函数(因为每个函数都有不同的地址)????尤其是当 ASLR 关闭时????
2 回答
实际上打开 ASLR 可能会影响每个单独线程的堆栈基地址。这是来自“Windows Internals, Sixth Edition, Part 2”, pp.249-250 的引述:
“ASLR 的下一步是随机化初始线程堆栈的位置(以及随后的每个新线程的位置)。除非
StackRandomizationDisabled
为进程启用了标志并且首先选择 32 个可能的堆栈位置之一,否则启用此随机化由 64 KB 或 256 KB 分隔。通过找到第一个适当的空闲内存区域,然后选择第 x 个可用区域来选择此基地址,其中 x 再次根据当前处理器的 TSC 生成并被移位并掩码为 5 位值(允许 32 个可能的位置)。一旦选择了这个基地址,就会计算一个新的 TSC 派生值,这个值长 9 位。然后将该值乘以 4 以保持对齐,这意味着它可以大到 2,048 字节(半页)。它被添加到基地址以获得最终的堆栈基数”。
上面提到的标志(StackRandomizationDisabled
)驻留在内核空间结构EPROCESS
中,不能从用户空间显式设置。但是,如果关闭 ASLR,则应用程序中主线程的堆栈基地址将与不同的重新运行相同。实际上,这意味着如果您每次运行应用程序时都在同一行的同一函数中,那么 ESP/RSP 将是相同的。
一些例子展示了这个概念。
请注意,操作系统中的每个进程都有自己的虚拟地址空间,但它与物理地址空间不同。因此,如果您同时运行应用程序的两个实例(ASLR 关闭)并在同一个地方执行,您将看到相同的 ESP/RSP 值。每个值都属于自己进程的虚拟地址空间,与其他进程没有关联。例如,您可以参考此链接以获取有关内存布局的更多信息。
每个函数的堆栈指针的值都不相同,并且不依赖于 ASLR 设置。堆栈指针在创建执行线程时设置在某个位置,并且函数在程序执行时递增或递减它以用于数据存储。特别是,函数代码通常不存储在堆栈中;指向功能代码的指针是。
如果没有 ASLR,一旦遇到堆栈缓冲区溢出错误,您就可以插入一个“代码块”来调用被利用应用程序中的其他函数,因为您知道这些函数在哪里。使用 ASLR,您不能轻易做到这一点,因为您不知道该代码在哪里。