所有可写和可执行程序内存段types

作者在“C和C ++安全编码”中提到,

“W ^ X策略允许内存段是可写或可执行的,但不能阻止覆盖目标,如atexit()所要求的目标,这些目标在运行时和可执行文件都需要都是可写的。

我有两个问题:

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

  2. 任何C / C ++专家都可以告诉我,还有什么其他types的API具有这个属性(在运行时和可执行文件中是可写的)? (让我们只限于Linux的范围)

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

在运行时动态生成代码的任何东西都需要可写和可执行的内存段:JIT,内核,可执行的解包器等等。对于这些中的每一个,都没有技术上要求段同时包含两个属性。 内存可以分配为可写第一,复制/生成的代码,并调用mprotect(),使其可执行(并删除可写属性)。 我可以看到的唯一情况是同时拥有这两个属性可能会受益于内存受限的环境(例如:解压缩可执行文件)。

请注意,某些平台不支持在用户空间中分配可执行内存:例如,Xbox360和PS3不支持JIT。 (内核/ api支持它,但是你将无法发布你的软件,微软和索尼将拒绝你的提交,因为这个功能只能用于开发。)