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() 来选择何时进入沙盒模式。您需要能够在代码中定义何时想要启动沙盒,并且必须是单线程的(如需了解原因,请参阅常见问题解答)。

在以下代码段中,我们使用了与上述方法 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()(通过 ::sandbox2::ForkingClient::WaitAndFork())。新创建的进程将可以使用 ::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