【问题标题】:All writable and executable program memory segment types所有可写和可执行的程序内存段类型
【发布时间】:2015-04-18 18:20:06
【问题描述】:

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

“W^X 策略允许内存段是可写或可执行的,但不能两者兼而有之。该策略无法防止覆盖目标,例如 atexit() 要求的目标,这些目标需要在运行时可写和可执行。”

我有两个问题:

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

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

【问题讨论】:

  • 惰性 JITers 可能会使用 W|X 段。他们不应该,但他们很懒。
  • 我同意你关于退出处理程序的观点。 指向 退出处理函数的指针列表需要是可写的,但不需要是可执行的。指向的函数需要是可执行的,但它们不需要在可写段中。

标签: c++ c linux security memory


【解决方案1】:

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

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

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

【讨论】:

    猜你喜欢
    • 2017-05-15
    • 2012-08-06
    • 2012-06-20
    • 2015-11-22
    • 2013-03-31
    • 2010-12-30
    • 2017-05-29
    • 2012-02-29
    相关资源
    最近更新 更多