1. 选择沙盒执行器方法

沙盒化从执行器(请参阅沙盒执行器)开始,该执行器负责运行 Sandboxeeexecutor.h 头文件包含实现此目的所需的 API。该 API 非常灵活,可让您选择最适合自己使用场景的方案。以下各部分介绍了 3 种不同的方法,您可以从中选择一种。

方法 1:独立 - 执行已启用沙盒功能的二进制文件

这是使用沙盒的最简单方法,也是在您想要对没有源代码的整个二进制文件进行沙盒处理时建议使用的方法。这也是使用沙盒的最安全方式,因为没有可能产生不利影响的非沙盒初始化。

在以下代码段中,我们定义了要沙盒化的二进制文件的路径以及必须传递给 execve 系统调用的实参。如 executor.h 头文件中所示,我们未指定 envp 的值,因此会从父进程复制环境。请注意,第一个实参始终是要执行的程序的名称,而我们的代码段未定义任何其他实参。

此执行器方法的示例包括:statictool

#include "sandboxed_api/sandbox2/executor.h"

std::string path = "path/to/binary";
std::vector<std::string> args = {path};  // args[0] will become the sandboxed
                                         // process' argv[0], typically the
                                         // path to the binary.
auto executor = absl::make_unique<sandbox2::Executor>(path, args);

方法 2:Sandbox2 Forkserver - 告知执行器何时进行沙盒化

此方法可灵活地在初始化期间不受沙盒限制,然后通过调用 ::sandbox2::Client::SandboxMeHere() 选择何时进入沙盒。它要求您能够在代码中定义何时开始沙盒化,并且必须是单线程的(请参阅 FAQ 了解原因)。

在以下代码段中,我们使用了与上述方法 1 中相同的代码。不过,为了允许程序在初始化期间以非沙盒方式执行,我们调用了 set_enable_sandbox_before_exec(false)

#include "sandboxed_api/sandbox2/executor.h"

std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
executor->set_enable_sandbox_before_exec(false);

由于执行器现在有一个已停用的沙盒,直到 Sandboxee 通知它为止,因此我们必须创建一个 ::sandbox2::Client 实例,设置执行器和 Sandboxee 之间的通信,然后通过调用 sandbox2_client.SandboxMeHere() 通知执行器我们的初始化已完成,并且我们现在要开始沙盒化。

// main() of sandboxee
int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, false);

  // Set-up the sandbox2::Client object, using a file descriptor (1023).
  sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
  sandbox2::Client sandbox2_client(&comms);
  // Enable sandboxing from here.
  sandbox2_client.SandboxMeHere();
  

此执行器方法的一个示例是 crc4,其中 crc4bin.cc 是 Sandboxee,并在其应进入沙盒时通知执行器 (crc4sandbox.cc)。

方法 3:自定义 Forkserver - 准备二进制文件,等待 fork 请求,并自行进行沙盒处理

此模式可让您启动二进制文件、准备沙盒环境,并在二进制文件生命周期的特定时刻将其提供给执行器。

执行器将向您的二进制文件发送 fork 请求,该二进制文件将通过 ::sandbox2::ForkingClient::WaitAndFork() 进行 fork()。新创建的进程将准备好通过 ::sandbox2::Client::SandboxMeHere() 进行沙盒处理。

#include "sandboxed_api/sandbox2/executor.h"

// Start the custom ForkServer
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto fork_executor = absl::make_unique<sandbox2::Executor>(path, args);
fork_executor->StartForkServer();

// Initialize Executor with Comms channel to the ForkServer
auto executor = absl::make_unique<sandbox2::Executor>(
    fork_executor->ipc()->GetComms());

请注意,此模式相当复杂,仅适用于少数特定情况,例如当您有严格的内存要求时。您将受益于 COW,但缺点是没有真正的 ASLR。另一个典型的使用示例是,当 Sandboxee 具有长时间、占用大量 CPU 资源的初始化过程时,可以在处理不受信任的数据之前运行该初始化过程。

如需查看此执行器方法的示例,请参阅 custom_fork