diff --git a/CMakeLists.txt b/CMakeLists.txt index aecd4c8..5105c5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ if(NOT TRAMPOLINE_HEADERS_ONLY) # Include Correct Sub-Project if(USE_NATIVE_TRAMPOLINE) add_subdirectory(native) + target_compile_definitions(trampoline-headers INTERFACE MCPI_USE_NATIVE_TRAMPOLINE) elseif(USE_QEMU_TRAMPOLINE) add_subdirectory(qemu) target_compile_definitions(trampoline-headers INTERFACE MCPI_USE_QEMU) diff --git a/include/trampoline/types.h b/include/trampoline/types.h index df4443c..15b141f 100644 --- a/include/trampoline/types.h +++ b/include/trampoline/types.h @@ -2,10 +2,26 @@ #include -// Constants +// Switch Between Pipe?PTrace Mode +#ifdef MCPI_USE_NATIVE_TRAMPOLINE +#define TRAMPOLINE_USE_PTRACE_ENV "_MCPI_TRAMPOLINE_USE_PTRACE" +#endif + +// System Call Constants #define MAX_TRAMPOLINE_ARGS_SIZE 2097152 // 2 MiB #define TRAMPOLINE_SYSCALL 0x1337 +// Pipe Constants +#ifdef MCPI_USE_NATIVE_TRAMPOLINE +#define TRAMPOLINE_ARGUMENTS_PIPE_ENV "_MCPI_TRAMPOLINE_ARGUMENTS" +#define TRAMPOLINE_RETURN_VALUE_PIPE_ENV "_MCPI_TRAMPOLINE_RETURN_VALUE" +struct trampoline_pipe_arguments { + uint32_t id; + uint32_t length; + uint32_t args_addr; +}; +#endif + // Function Types typedef void (*trampoline_writer_t)(uint32_t guest_addr, void *data, uint32_t size); -typedef uint32_t (*trampoline_t)(trampoline_writer_t writer, uint32_t id, const unsigned char *args); +typedef uint32_t (*trampoline_t)(trampoline_writer_t writer, uint32_t id, const unsigned char *args); \ No newline at end of file diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 6050b1e..1b42d25 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -1,7 +1,15 @@ project(native-trampoline) # Build -add_executable(trampoline src/memory.cpp src/main.cpp src/trampoline.cpp src/ptrace.cpp) +add_executable(trampoline + src/memory.cpp + src/main.cpp + src/trampoline.cpp + src/ptrace/loop.cpp + src/ptrace/init.cpp + src/pipe/pipe.cpp + src/signals.cpp +) # Warnings target_compile_options(trampoline PRIVATE -Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference) diff --git a/native/src/main.cpp b/native/src/main.cpp index b035338..b3b35c8 100644 --- a/native/src/main.cpp +++ b/native/src/main.cpp @@ -1,49 +1,37 @@ #include #include #include -#include #include -#include -#include -#include -#include - -#include #include "log.h" #include "memory.h" #include "trampoline.h" -#include "ptrace.h" +#include "ptrace/ptrace.h" +#include "pipe/pipe.h" +#include "signals.h" // Main int main(__attribute__((unused)) int argc, char *argv[]) { + // Check Arguments + if (argc < 2) { + ERR("Invalid Arguments"); + } + bool use_ptrace = getenv(TRAMPOLINE_USE_PTRACE_ENV) != nullptr; + // Setup + if (!use_ptrace) { + init_pipe_common(); + } // Fork pid_t pid = fork(); if (pid == -1) { ERR("Unable To Fork Process: %s", strerror(errno)); } else if (pid == 0) { - // seccomp Filter - sock_filter filter[] = { - // Load Syscall Number To Accumulator - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(seccomp_data, nr)), - // If Syscall Does Not Match The Trampoline, Then Skip The Next Instruction - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, TRAMPOLINE_SYSCALL, 0, 1), - // Trigger PTrace And End Filter - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE), - // Allow Syscall And End Filter - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - }; - sock_fprog prog = { - .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), - .filter = filter - }; - // Setup PTrace - ptrace(PTRACE_TRACEME, 0, 0, 0); - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { - ERR("Unable To Prepare Process For seccomp: %s", strerror(errno)); - } - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) { - ERR("Unable To Set seccomp Filter: %s", strerror(errno)); + // Setup + setpgid(0, 0); + if (use_ptrace) { + init_ptrace_guest(); + } else { + init_pipe_guest(); } // Execute Program execvp(argv[1], (char *const *) &argv[1]); @@ -51,16 +39,16 @@ int main(__attribute__((unused)) int argc, char *argv[]) { } else { // Parent - // Wait For PTrace - waitpid(pid, nullptr, 0); - // Configure PTrace - ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACESECCOMP); - // Setup Trampoline + init_signals(pid); init_memory(pid); init_trampoline(); - // Start PTrace Loop - loop_ptrace(pid); + // Start PTrace + if (use_ptrace) { + init_ptrace_host(pid); + } else { + init_pipe_host(pid); + } } } \ No newline at end of file diff --git a/native/src/pipe/pipe.cpp b/native/src/pipe/pipe.cpp new file mode 100644 index 0000000..c4d9d6b --- /dev/null +++ b/native/src/pipe/pipe.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +#include + +#include "pipe.h" + +#include "../log.h" +#include "../trampoline.h" +#include "../memory.h" + +// Setup Pipes +#define PIPE_READ 0 +#define PIPE_WRITE 1 +static int arguments_pipe[2]; +static int return_value_pipe[2]; +static void safe_pipe(int *out) { + int ret = pipe(out); + if (ret != 0) { + ERR("Unable To Create Pipe: %s", strerror(errno)); + } +} +void init_pipe_common() { + safe_pipe(arguments_pipe); + safe_pipe(return_value_pipe); +} + +// Guest +void init_pipe_guest() { + // Close Unneeded Pipes + close(arguments_pipe[PIPE_READ]); + close(return_value_pipe[PIPE_WRITE]); + // Setup Environment + setenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV, std::to_string(arguments_pipe[PIPE_WRITE]).c_str(), true); + setenv(TRAMPOLINE_RETURN_VALUE_PIPE_ENV, std::to_string(return_value_pipe[PIPE_READ]).c_str(), true); +} + +// Host +void init_pipe_host(pid_t guest_pid) { + // Close Unneeded Pipes + close(arguments_pipe[PIPE_WRITE]); + close(return_value_pipe[PIPE_READ]); + // Wait For Commands + trampoline_pipe_arguments cmd = {}; + while (read(arguments_pipe[PIPE_READ], &cmd, sizeof(trampoline_pipe_arguments)) > 0) { + static unsigned char args[MAX_TRAMPOLINE_ARGS_SIZE]; + memory_reader(cmd.args_addr, args, cmd.length); + uint32_t ret = trampoline(cmd.id, args); + write(return_value_pipe[PIPE_WRITE], &ret, sizeof(uint32_t)); + } + // Reap Child + int status; + if (waitpid(guest_pid, &status, 0) == -1) { + ERR("Unable To Reap Child: %s", strerror(errno)); + } + if (WIFEXITED(status)) { + exit(WEXITSTATUS(status)); + } else { + ERR("Unable To Determine Exit Code"); + } +} \ No newline at end of file diff --git a/native/src/pipe/pipe.h b/native/src/pipe/pipe.h new file mode 100644 index 0000000..6bb962e --- /dev/null +++ b/native/src/pipe/pipe.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void init_pipe_common(); +void init_pipe_host(pid_t guest_pid); +void init_pipe_guest(); diff --git a/native/src/ptrace/init.cpp b/native/src/ptrace/init.cpp new file mode 100644 index 0000000..1628fb4 --- /dev/null +++ b/native/src/ptrace/init.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "ptrace.h" +#include "../log.h" + +// Init +void init_ptrace_host(pid_t guest_pid) { + // Wait For PTrace + safe_wait(guest_pid, nullptr); + // Configure PTrace + safe_ptrace(PTRACE_SETOPTIONS, guest_pid, nullptr, (void *) (PTRACE_O_EXITKILL | PTRACE_O_TRACESECCOMP)); + // Start Loop + loop_ptrace(guest_pid); +} +void init_ptrace_guest() { + // seccomp Filter + sock_filter filter[] = { + // Load Syscall Number To Accumulator + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(seccomp_data, nr)), + // If Syscall Does Not Match The Trampoline, Then Skip The Next Instruction + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, TRAMPOLINE_SYSCALL, 0, 1), + // Trigger PTrace And End Filter + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE), + // Allow Syscall And End Filter + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) + }; + sock_fprog prog = { + .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), + .filter = filter + }; + // Setup PTrace + safe_ptrace(PTRACE_TRACEME, 0, nullptr, nullptr); + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { + ERR("Unable To Prepare Process For Seccomp: %s", strerror(errno)); + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) { + ERR("Unable To Set Seccomp Filter: %s", strerror(errno)); + } +} \ No newline at end of file diff --git a/native/src/ptrace.cpp b/native/src/ptrace/loop.cpp similarity index 87% rename from native/src/ptrace.cpp rename to native/src/ptrace/loop.cpp index 25e4ca7..9d51e9b 100644 --- a/native/src/ptrace.cpp +++ b/native/src/ptrace/loop.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -11,23 +10,27 @@ #include #include "ptrace.h" -#include "log.h" -#include "trampoline.h" -#include "memory.h" +#include "../log.h" +#include "../trampoline.h" +#include "../memory.h" // Helper Functions -static void safe_ptrace(__ptrace_request request, pid_t pid, void *addr, void *data) { +void safe_ptrace(__ptrace_request request, pid_t pid, void *addr, void *data) { if (ptrace(request, pid, addr, data) == -1) { ERR("PTrace Error: %s", strerror(errno)); } } -static void safe_wait(pid_t guest_pid, int *status) { - if (waitpid(guest_pid, status, 0) == -1) { +void safe_wait(pid_t guest_pid, int *status) { + int real_status; + if (waitpid(guest_pid, &real_status, 0) == -1) { ERR("Unable To Wait For Guest"); } - if (WIFEXITED(*status)) { + if (WIFEXITED(real_status)) { // Process Exited - exit(WEXITSTATUS(*status)); + exit(WEXITSTATUS(real_status)); + } + if (status != nullptr) { + *status = real_status; } } static void ptrace_wait_syscall(pid_t guest_pid) { diff --git a/native/src/ptrace/ptrace.h b/native/src/ptrace/ptrace.h new file mode 100644 index 0000000..7163896 --- /dev/null +++ b/native/src/ptrace/ptrace.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +void safe_ptrace(__ptrace_request request, pid_t pid, void *addr, void *data); +void safe_wait(pid_t guest_pid, int *status); +void init_ptrace_host(pid_t guest_pid); +void init_ptrace_guest(); +void loop_ptrace(pid_t guest_pid); \ No newline at end of file diff --git a/native/src/signals.cpp b/native/src/signals.cpp new file mode 100644 index 0000000..1a7b508 --- /dev/null +++ b/native/src/signals.cpp @@ -0,0 +1,28 @@ +#include + +#include "signals.h" + +#include +#include +#include + +// Signal Handlers +static pid_t guest_pid = -1; +static void exit_handler(__attribute__((unused)) int signal_id) { + // Stop Child + kill(guest_pid, SIGTERM); +} + +// Init +void init_signals(pid_t pid) { + guest_pid = pid; + // Install Signal Handlers + struct sigaction act_sigint = {}; + act_sigint.sa_flags = SA_RESTART; + act_sigint.sa_handler = &exit_handler; + sigaction(SIGINT, &act_sigint, nullptr); + struct sigaction act_sigterm = {}; + act_sigterm.sa_flags = SA_RESTART; + act_sigterm.sa_handler = &exit_handler; + sigaction(SIGTERM, &act_sigterm, nullptr); +} \ No newline at end of file diff --git a/native/src/ptrace.h b/native/src/signals.h similarity index 52% rename from native/src/ptrace.h rename to native/src/signals.h index d11bfb1..1d18035 100644 --- a/native/src/ptrace.h +++ b/native/src/signals.h @@ -2,4 +2,4 @@ #include -void loop_ptrace(pid_t guest_pid); \ No newline at end of file +void init_signals(pid_t guest_pid); \ No newline at end of file diff --git a/native/src/trampoline.h b/native/src/trampoline.h index 44d2345..04503c8 100644 --- a/native/src/trampoline.h +++ b/native/src/trampoline.h @@ -2,6 +2,5 @@ #include -#define MAX_TRAMPOLINE_ARGS_SIZE 2097152 // See media-layer/trampoline/src/guest/guest.h uint32_t trampoline(uint32_t id, const unsigned char *args); void init_trampoline(); \ No newline at end of file