所有可写和可执行的程序内存段类型

All writable and executable program memory segment types

作者在"Secure coding in C and C++"中提到,

"The W^X policy allows a memory segment to be writable or executable, but no both. This policy cannot prevent overwriting targets such as those required by atexit() that need to be both writable at runtime and executable. "

我有两个问题:

  1. atexit需要通过函数指针作为参数来注册一个函数。函数指针指向的函数要么在当前程序中定义,链接器将在其中找到定义,要么运行时加载器将找到函数体。无论哪种情况,我们都会知道函数定义。然后它只需要是可执行的。那么为什么 atexit() 的内存段需要在运行时可写和可执行?

  2. 任何 C/C++ 专家都可以告诉我还有哪些其他类型的 API 具有此 属性(可在运行时写入和可执行)? (让我们将范围限制为仅 linux)

从根本上说,可以写入和执行的内存很容易被调和,并且可以更容易地导致漏洞利用,因为不需要使用 ROP 或其他花哨的方法,你可以简单地在段中的任何地方编写代码执行并分支到它。 在您的引用中,此上下文中目标的含义很可能是退出时调用的函数指针列表。根据 C API,列表本身需要 writable/mutable。这些函数指向的代码只需要是可执行的。同样,由于该列表是可变的,您可以通过插入指向您的代码的指针来修改此列表并强制程序退出以执行您的代码,从而利用程序。在这种情况下,保持所有内存段可写或可执行不会拯救你,因为这里使用了 2 个不同的段(一个可写函数指针列表,另一个可执行代码)。

任何在运行时动态生成代码的东西都需要可写和可执行内存段:JIT、内核、可执行解包程序等。对于其中的每一个,没有技术要求段同时拥有这两个属性.可以首先将内存分配为可写,代码 copied/generated 并调用 mprotect(),使其可执行(并删除可写 属性)。我能看到同时拥有这两个属性的唯一场景可能是在内存受限的环境中(例如:就地解压缩可执行文件)。

请注意,某些平台不支持在用户 space 中分配可执行内存:例如 Xbox360 和 PS3 不支持 JIT。 (kernel/api 支持,但您将无法发布您的软件,Microsoft 和 Sony 将拒绝您的提交,因此该功能只能在开发中使用。)