1. เลือกวิธีการดำเนินการแซนด์บ็อกซ์

การแซนด์บ็อกซ์เริ่มต้นด้วยตัวดำเนินการ (ดูตัวดำเนินการ แซนด์บ็อกซ์) ซึ่งมีหน้าที่ รับผิดชอบในการเรียกใช้Sandboxee ไฟล์ส่วนหัว executor.h มี API ที่จำเป็นสำหรับวัตถุประสงค์นี้ API มีความยืดหยุ่นสูง และช่วยให้คุณเลือกสิ่งที่เหมาะกับกรณีการใช้งานของคุณมากที่สุดได้ ส่วนต่อไปนี้ จะอธิบาย 3 วิธีที่แตกต่างกันซึ่งคุณเลือกใช้ได้

วิธีที่ 1: แบบสแตนด์อโลน - เรียกใช้ไบนารีที่เปิดใช้แซนด์บ็อกซ์อยู่แล้ว

วิธีนี้เป็นวิธีที่ง่ายที่สุดในการใช้แซนด์บ็อกซ์ และเป็นวิธีที่แนะนําเมื่อ คุณต้องการแซนด์บ็อกซ์ไบนารีทั้งหมดที่คุณไม่มีซอร์สโค้ด นอกจากนี้ยังเป็นวิธีที่ปลอดภัยที่สุดในการใช้แซนด์บ็อกซ์ เนื่องจากไม่มีการเริ่มต้นที่ไม่ใช่แซนด์บ็อกซ์ ซึ่งอาจส่งผลเสียได้

ในข้อมูลโค้ดต่อไปนี้ เราจะกำหนดเส้นทางของไบนารีที่จะแซนด์บ็อกซ์ และอาร์กิวเมนต์ที่เราต้องส่งไปยัง Syscall execve ดังที่คุณเห็นในไฟล์ส่วนหัว executor.h เราไม่ได้ระบุค่าสำหรับ envp จึงคัดลอก สภาพแวดล้อมจากกระบวนการหลัก โปรดทราบว่าอาร์กิวเมนต์แรกจะเป็น ชื่อของโปรแกรมที่จะเรียกใช้เสมอ และข้อมูลโค้ดของเราไม่ได้กำหนดอาร์กิวเมนต์อื่นๆ

ตัวอย่างของเมธอดนี้ ได้แก่ static และ tool

#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: Forkserver ของ Sandbox2 - บอกผู้ปฏิบัติงานเมื่อใดที่ควรอยู่ในแซนด์บ็อกซ์

วิธีนี้ช่วยให้คุณมีความยืดหยุ่นในการยกเลิกการแซนด์บ็อกซ์ในระหว่างการเริ่มต้น แล้วเลือกเวลาที่จะเข้าสู่แซนด์บ็อกซ์โดยการเรียกใช้ ::sandbox2::Client::SandboxMeHere() คุณต้องกำหนดในโค้ดได้ว่าต้องการเริ่มแซนด์บ็อกซ์เมื่อใด และต้องเป็นแบบ Single-Thread (อ่านเหตุผลในคำถามที่พบบ่อย)

ในข้อมูลโค้ดต่อไปนี้ เราใช้โค้ดเดียวกันกับที่ระบุไว้ในวิธีที่ 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);

เนื่องจากตอนนี้ตัวดำเนินการมี Sandbox ที่ปิดใช้จนกว่าจะได้รับการแจ้งเตือนจาก Sandboxee เราจึงต้องสร้างอินสแตนซ์ ::sandbox2::Client ตั้งค่า การสื่อสารระหว่างตัวดำเนินการกับ Sandboxee แล้วแจ้งให้ตัวดำเนินการทราบ ว่าการเริ่มต้นของเราเสร็จสิ้นแล้ว และเราต้องการเริ่มใช้ Sandbox ตอนนี้โดย เรียกใช้ 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 ไปยังไบนารีของคุณ ซึ่งจะ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 เป็นเวลานาน ซึ่งสามารถเรียกใช้ได้ก่อนที่จะประมวลผลข้อมูลที่ไม่น่าเชื่อถือ

ดูตัวอย่างของเมธอด Executor นี้ได้ที่ custom_fork