mprotect整个程序,运行危险的代码

时间:2011-11-13 13:28:57

标签: c linux posix sandbox mprotect

我有一个小程序,可以编写具有潜在危险的可执行代码(使用PROT_EXEC),调用prctl(PR_SET_SECCOMP, 1)然后执行此mmap代码。这一切都很好,并允许我通过将mmap'd区域同步到磁盘来“保存”评估状态,并在以后重新加载它(最有可能在另一台机器上进行负载平衡)。但是,这种技术并不总是有效 - 因为此代码可能已对不在mmap'd区域中的程序进行了更改,并且此信息将丢失。

所以我想做的是,在调用代码之前,将所有内容(除了这个mmap'd区域之外)设置为只读。这样我就可以保证可执行代码不能改变mmap'd区域以外的任何其他状态,我可以随意序列化/反序列化。

BTW这是Linux on x86_64

由于

1 个答案:

答案 0 :(得分:4)

首先,观察:没有任何内容表明您必须mmap()将机器指令放入内存或将其保存回文件。 read()write()也可以这样做,只需注意您应该为此目的制作可写和可执行的私有映射。

显然,你不能可靠地禁止写入将要调用你将加载的可执行代码的堆栈区域,如果它要在同一进程中执行,因为这将使堆栈无法使用。您可以通过注释变量或使用汇编来解决此问题。

您的下一个选项是fork()。您可以将子进程exec转换为特殊的包装器可执行文件,允许对恶意可执行代码进行最小程度的损坏和内省(仅提供加载/转储),或者您可以通过让子进程将其自身修改为相同的效果来执行相同的操作。这仍然不是100%安全。

Proposal0

  • 创建一个独立的二进制文件,它与最小的库(-nodefaultlibs)链接。
  • 在孩子中forkptrace(PTRACE_TRACEME)后(以便您可以可靠地读取内存内容并进行其他干预),并关闭除管道之外的所有句柄(仅在{{1}中)为简单起见)。 stdin进入前面提到的包装二进制文件。

在包装器二进制文件中:

  • exec()具有写入和执行权限的已知位置的私有区域。或者,如果大小固定,您可以静态分配此区域。
  • 将管道内容读入该区域。
  • 关闭管道。现在这个过程没有打开句柄。
  • mmap。现在唯一有效的系统调用是prctl(PR_SET_SECCOMP, 1)_exit。由于该流程无法sigreturnraise应该没有任何有用的效果。
  • 从主堆栈中删除写入权限(应该是唯一的堆栈)。由于您无意返回,并且之后会立即跳转,因此您不需要再次触摸堆栈。
  • 跳转到区域内的起始位置。使用程序集执行此操作,或创建一个函数指针并调用它(如果您可以在不推送到堆栈的情况下使其工作)。现在您应该执行一个可用的唯一可写区域的内存区域。主堆栈受到保护,由于缺少库支持,堆不应该使用。

在父母:

  • 使用sigreturnptrace,抓住错误或成功完成。
  • 通过wait或等效于文件的位置读取已知位置的映射区域。