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

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

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

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

谢谢

首先,一个观察:没有什么说你必须mmap()获取机器指令到内存或将其保存回文件。 read()write()也可以这样做,只要注意你应该为此写一个可写和可执行的私有映射。

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

你的下一个选择是fork() 。 你可以在孩子身上执行一个特殊的包装可执行文件,允许通过恶意的可执行代码(提供简单的加载/转储)来最小的破坏和反省,或者你也可以通过让孩子修改自己来达到同样的效果。 这仍然不是100%安全。

Proposal0

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

在包装二进制文件中:

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

在父母:

  • 使用ptracewait ,错误或成功完成。
  • 通过/proc/<pid>/mem或等同于文件的方式读取已知位置的映射区域。