5

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

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

顺便说一句,这是 x86_64 上的 Linux

谢谢

4

1 回答 1

4

首先,观察:没有什么说您必须将mmap()机器指令放入内存或将它们保存回文件。read()并且write()也可以这样做,只需注意您应该为此目的创建一个可写且可执行的私有映射。

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

您的下一个选择是fork(). 您可以exec将孩子放入一个特殊的包装器可执行文件中,该可执行文件允许恶意可执行代码的破坏和自省最小化(提供简单的加载/转储),或者您可以通过让孩子修改自身以达到相同的效果来做同样的事情。这仍然不是 100% 安全的。

提案0

  • 创建与最小库 ( ) 链接的独立二进制文件-nodefaultlibs
  • 之后forkptrace(PTRACE_TRACEME)在孩子中(以便您可以可靠地读取内存内容并进行其他干预),并关闭除管道之外的所有句柄(只是stdin为了简单起见)。exec()进入上述包装二进制文件。

在包装二进制文件中:

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

在父级中:

  • 使用ptraceor wait,捕获错误或成功的完成。
  • /proc/<pid>/mem通过或等效于文件读取已知位置的映射区域。
于 2011-11-13T14:03:35.853 回答