Rewire as library
This commit is contained in:
parent
91410dd6a7
commit
c19a487436
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1 @@
|
||||
rewire
|
||||
*.so
|
||||
build/
|
||||
|
||||
22
CMakeLists.txt
Normal file
22
CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
project(Rewire LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Make rewire core library
|
||||
add_library(rewire SHARED "")
|
||||
target_include_directories(rewire PUBLIC include)
|
||||
add_subdirectory(src)
|
||||
|
||||
# Building the cli tool and example wirekit is optional
|
||||
option(REWIRE_BUILD_CLI "Build rewire cli" OFF)
|
||||
option(REWIRE_BUILD_EXAMPLE "Build rewire example wirekit" OFF)
|
||||
|
||||
if (REWIRE_BUILD_CLI OR (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME))
|
||||
add_subdirectory(cli)
|
||||
endif()
|
||||
|
||||
if (REWIRE_BUILD_EXAMPLE OR (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME))
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
21
Makefile
21
Makefile
@ -1,21 +0,0 @@
|
||||
REWIRE_SRC_DIR := src
|
||||
REWIRE_INCLUDE_DIR := include
|
||||
REWIRE_SRC := $(wildcard $(REWIRE_SRC_DIR)/*.cpp)
|
||||
REWIRE_CC := g++
|
||||
REWIRE_CFLAGS := -std=c++20 -I $(REWIRE_INCLUDE_DIR) -Wall -fPIC -fvisibility=hidden -rdynamic
|
||||
|
||||
WIREKIT_CC := g++
|
||||
WIREKIT_CFLAGS := -std=c++20 -I $(REWIRE_INCLUDE_DIR) -Wall -fno-rtti -Wno-literal-suffix -fPIC -shared
|
||||
|
||||
all: rewire wirekits
|
||||
|
||||
rewire: $(REWIRE_SRC)
|
||||
$(REWIRE_CC) $(REWIRE_CFLAGS) -o $@ $(REWIRE_SRC)
|
||||
|
||||
wirekits: example.so
|
||||
|
||||
example.so: $(wildcard wirekits/example/*.cpp)
|
||||
$(WIREKIT_CC) $(WIREKIT_CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f rewire example.so darkrose.so
|
||||
4
cli/CMakeLists.txt
Normal file
4
cli/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
add_executable(rewire_cli main.cpp dl.cpp install.cpp cli.cpp)
|
||||
set_target_properties(rewire_cli PROPERTIES OUTPUT_NAME "rewire")
|
||||
target_compile_options(rewire_cli PRIVATE -Wall -fPIC -rdynamic)
|
||||
target_link_libraries(rewire_cli PRIVATE rewire)
|
||||
224
cli/main.cpp
Normal file
224
cli/main.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include "cli.hpp"
|
||||
#include "dl.hpp"
|
||||
#include "install.hpp"
|
||||
#include "interface.hpp"
|
||||
|
||||
int launch_help(int argc, char** argv) {
|
||||
std::cout << "Use rewire via one of the following commands:" << std::endl;
|
||||
|
||||
std::cout << " \"rewire help\" -> You appear to already know what it does ;)" << std::endl;
|
||||
|
||||
std::cout << " \"rewire install <wirekit.so> <name>\" -> Installs a wirekit to execute programs with" << std::endl;
|
||||
std::cout << " <wirekit.so> -> The shared object to be installed as wirekit" << std::endl;
|
||||
std::cout << " <name> -> (Optional) The name via which the wirekit is supposed to be used" << std::endl;
|
||||
std::cout << " -> Defaults to \"wirekit\" when installing \"wirekit.so\"" << std::endl;
|
||||
|
||||
std::cout << " \"rewire uninstall <name>\" -> Removes a wirekit from the system" << std::endl;
|
||||
std::cout << " <name> -> The name of the wirekit to be uninstalled" << std::endl;
|
||||
|
||||
std::cout << " \"rewire list\" -> Lists all installed wirekits" << std::endl;
|
||||
|
||||
std::cout << " \"rewire <wirekit> <command+args>\"" << std::endl;
|
||||
std::cout << " <wirekit> -> The wirekit to use for execution" << std::endl;
|
||||
std::cout << " -> Can either be installed or in current working directory" << std::endl;
|
||||
std::cout << " <command+args> -> (Optional) A command to be executed" << std::endl;
|
||||
std::cout << " -> Acts as rewired shell if no command is given" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_install(int argc, char** argv) {
|
||||
if (argc < 3 || argc > 4) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to manage installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto kitcheck = [](const std::filesystem::path& kit) -> bool {
|
||||
DL tmp_dl { kit };
|
||||
if (!tmp_dl) {
|
||||
std::cout << "Failed to check wirekit requirements for installation!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tmp_dl.resolve("wirekit_prepare")) {
|
||||
std::cout << "Wirekit must implement \"wirekit_prepare\" to be installed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tmp_dl.resolve("wirekit_command_start")) {
|
||||
std::cout << "Wirekit must implement \"wirekit_command_start\" to be installed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!im.install(argv[2], (argc == 4 ? argv[3] : nullptr), kitcheck)) {
|
||||
std::cout << "Failed to install wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Successfully installed wirekit!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_uninstall(int argc, char** argv) {
|
||||
if (argc != 3) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to manage installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!im.uninstall(argv[2])) {
|
||||
std::cout << "Failed to uninstall wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Successfully uninstalled wirekit!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_list(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to access installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto wirekits = im.installs();
|
||||
if (wirekits.empty()) {
|
||||
std::cout << "Currently, no wirekits are installed!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Currently, the following wirekits are installed:" << std::endl;
|
||||
for (const std::string& kit : wirekits)
|
||||
std::cout << " \"" << kit << "\"" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_run(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to access installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path wirekit_path;
|
||||
if (!im.get(argv[1], wirekit_path)) {
|
||||
std::cout << "No wirekit named \"" << argv[1] << "\" is installed!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DL wirekit { wirekit_path };
|
||||
if (!wirekit) {
|
||||
std::cout << "Unable to load wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Collect wirekit control function handles
|
||||
KitInterface ki;
|
||||
ki.prepare = (err_t(*)())wirekit.resolve("wirekit_prepare");
|
||||
ki.command_start = (err_t(*)(int, const char* const*))wirekit.resolve("wirekit_command_start");
|
||||
ki.command_exit = (void(*)())wirekit.resolve("wirekit_command_exit");
|
||||
ki.subject_start = (void(*)())wirekit.resolve("wirekit_subject_start");
|
||||
ki.subject_exit = (void(*)())wirekit.resolve("wirekit_subject_exit");
|
||||
|
||||
// Initialize rewire (internally prepares wirekit)
|
||||
if (!rewire_init(ki)) {
|
||||
std::cout << "Rewire and wirekit preparation failed!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Execute commands in rewired syscall environment
|
||||
if (argc > 2) {
|
||||
rewire_run(argc - 2, (const char**)(argv + 2));
|
||||
} else {
|
||||
// Shell mode
|
||||
while (true) {
|
||||
std::cout << "> ";
|
||||
|
||||
std::string command;
|
||||
std::getline(std::cin, command);
|
||||
|
||||
// Split command at whitespaces
|
||||
std::stringstream command_stream { command };
|
||||
std::vector<std::string> command_split; // No need to reserve, presumably not many elements
|
||||
|
||||
std::string part;
|
||||
while (command_stream >> std::quoted(part))
|
||||
command_split.emplace_back(std::move(part));
|
||||
|
||||
if (command_split.empty())
|
||||
continue;
|
||||
|
||||
if (command_split[0] == "exit")
|
||||
break;
|
||||
|
||||
// Compile argv (cstr pointer array)
|
||||
std::vector<const char*> command_split_cstr;
|
||||
command_split_cstr.resize(command_split.size() + 1, nullptr); // +1 for nullptr termination
|
||||
|
||||
for (std::size_t i = 0; i < command_split.size(); i++)
|
||||
command_split_cstr[i] = command_split[i].c_str();
|
||||
|
||||
// Execute command
|
||||
rewire_run((int)command_split_cstr.size() - 1, command_split_cstr.data());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\"!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
LAUNCH_MODE lm = launch_mode(argv[1]);
|
||||
|
||||
switch (lm) {
|
||||
case LAUNCH_MODE::HELP:
|
||||
return launch_help(argc, argv);
|
||||
case LAUNCH_MODE::INSTALL:
|
||||
return launch_install(argc, argv);
|
||||
case LAUNCH_MODE::UNINSTALL:
|
||||
return launch_uninstall(argc, argv);
|
||||
case LAUNCH_MODE::LIST:
|
||||
return launch_list(argc, argv);
|
||||
case LAUNCH_MODE::RUN:
|
||||
return launch_run(argc, argv);
|
||||
default:
|
||||
std::cout << "Invalid launch mode!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
3
example/CMakeLists.txt
Normal file
3
example/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_library(example SHARED main.cpp)
|
||||
target_include_directories(example PRIVATE $<TARGET_PROPERTY:rewire,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
target_compile_options(example PRIVATE -Wall -fno-rtti -Wno-literal-suffix -fPIC)
|
||||
@ -1,12 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "wirekit.hpp"
|
||||
#include "interface_types.hpp"
|
||||
|
||||
extern err_t(*wirekit_prepare_handle)();
|
||||
extern err_t(*wirekit_command_start_handle)(int, const char* const*);
|
||||
extern void(*wirekit_command_exit_handle)();
|
||||
extern void(*wirekit_subject_start_handle)();
|
||||
extern void(*wirekit_subject_exit_handle)();
|
||||
struct KitInterface {
|
||||
err_t(*prepare)() = nullptr;
|
||||
err_t(*command_start)(int, const char* const*) = nullptr;
|
||||
void(*command_exit)() = nullptr;
|
||||
void(*subject_start)() = nullptr;
|
||||
void(*subject_exit)() = nullptr;
|
||||
};
|
||||
|
||||
bool rewire_init(const KitInterface& iface);
|
||||
bool rewire_run(int argc, const char* const* argv);
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Exposure to wirekit is achieved by adding these functions to rewire's symbol tables
|
||||
#define EXPOSED __attribute__((visibility("default")))
|
||||
@ -19,3 +26,5 @@ EXPOSED err_t rewire_subject_id(pid_t* pid);
|
||||
EXPOSED err_t rewire_subject_get_regs(user_regs_struct* regs);
|
||||
EXPOSED err_t rewire_subject_set_regs(const user_regs_struct* regs);
|
||||
EXPOSED err_t rewire_subject_load_cstr(const char* subject_addr, char* buf, uint32_t* read, uint32_t n);
|
||||
|
||||
}
|
||||
|
||||
12
include/interface_types.hpp
Normal file
12
include/interface_types.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <cstdint>
|
||||
|
||||
using reg_t = decltype(user_regs_struct::orig_rax);
|
||||
using hook_t = void(*)();
|
||||
using err_t = uint8_t;
|
||||
|
||||
constexpr err_t REWIRE_SUCCESS = 0;
|
||||
constexpr err_t REWIRE_FAILURE = 1;
|
||||
constexpr err_t REWIRE_FAILURE_NOCTX = 2;
|
||||
@ -1,15 +1,6 @@
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <cstdint>
|
||||
|
||||
using reg_t = decltype(user_regs_struct::orig_rax);
|
||||
using hook_t = void(*)();
|
||||
using err_t = uint8_t;
|
||||
|
||||
constexpr err_t REWIRE_SUCCESS = 0;
|
||||
constexpr err_t REWIRE_FAILURE = 1;
|
||||
constexpr err_t REWIRE_FAILURE_NOCTX = 2;
|
||||
#include "interface_types.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
2
src/CMakeLists.txt
Normal file
2
src/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
target_sources(rewire PRIVATE environment.cpp execution.cpp)
|
||||
target_include_directories(rewire PRIVATE .) # Internal headers
|
||||
@ -4,7 +4,7 @@
|
||||
#include <memory>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "wirekit.hpp"
|
||||
#include "interface_types.hpp"
|
||||
|
||||
struct TraceContext {
|
||||
enum class EXECUTION_MODE { STARTING, USER, KERNEL /* Currently executing a system call */ };
|
||||
286
src/execution.cpp
Normal file
286
src/execution.cpp
Normal file
@ -0,0 +1,286 @@
|
||||
#include "interface.hpp"
|
||||
#include "environment.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <sys/ptrace.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
bool initialized = false;
|
||||
KitInterface kit;
|
||||
|
||||
bool rewire_init(const KitInterface& iface) {
|
||||
initialized = false;
|
||||
|
||||
if (!iface.prepare || !iface.command_start)
|
||||
return false;
|
||||
|
||||
kit = iface;
|
||||
|
||||
if (kit.prepare() != REWIRE_SUCCESS)
|
||||
return false;
|
||||
|
||||
return initialized = true;
|
||||
}
|
||||
|
||||
pid_t dispatch_tracee(int argc, const char* const* argv) {
|
||||
pid_t child = fork();
|
||||
|
||||
if (child != 0)
|
||||
return child; // Either -1 on error or child pid
|
||||
|
||||
ptrace(PTRACE_TRACEME, 0, 0, 0);
|
||||
execvp(argv[0], (char* const*)argv); // Removing const here is in child process
|
||||
// Note -> Tracee stops with SIGTRAP immediately after execvp!
|
||||
|
||||
// Child should never return from execvp
|
||||
while (true) exit(1);
|
||||
}
|
||||
|
||||
class KillWrapper {
|
||||
pid_t m_pid;
|
||||
public:
|
||||
KillWrapper(const KillWrapper& other) = delete;
|
||||
KillWrapper(KillWrapper&& other) = delete;
|
||||
KillWrapper& operator=(const KillWrapper& other) = delete;
|
||||
KillWrapper& operator=(KillWrapper&& other) = delete;
|
||||
|
||||
KillWrapper(pid_t pid) : m_pid{ pid } {}
|
||||
|
||||
~KillWrapper() {
|
||||
if (m_pid != -1)
|
||||
kill(m_pid, SIGKILL);
|
||||
}
|
||||
|
||||
void release() {
|
||||
m_pid = -1;
|
||||
}
|
||||
};
|
||||
|
||||
#define FORCEQUIT { std::cerr << "Forcefully quitting!" << std::endl; while (true) exit(1); }
|
||||
|
||||
bool rewire_run(int argc, const char* const* argv) {
|
||||
if (!initialized)
|
||||
return false;
|
||||
|
||||
EnvironmentScope es { ENV }; // Make sure environment is cleared after each command
|
||||
|
||||
// Notify wirekit about command start
|
||||
if (kit.command_start(argc, argv) != REWIRE_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Dispatch main command process
|
||||
pid_t main_pid = dispatch_tracee(argc, argv);
|
||||
if (main_pid == -1)
|
||||
return false;
|
||||
|
||||
KillWrapper kw { main_pid }; // Make sure it gets whiped until in main trace loop
|
||||
|
||||
int exec_status;
|
||||
if (waitpid(main_pid, &exec_status, 0) != main_pid || !WIFSTOPPED(exec_status) || WSTOPSIG(exec_status) != SIGTRAP)
|
||||
return false;
|
||||
|
||||
/*
|
||||
PTRACE_O_EXITKILL -> Kill tracees when tracer exits
|
||||
PTRACE_O_TRACESYSGOOD -> SIGTRAP signals caused by system call entry/exit stops have 0x80 bit set
|
||||
PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK, PTRACE_O_TRACECLONE -> Automatically trace new children (inherit options and mode)
|
||||
*/
|
||||
if (ptrace(PTRACE_SETOPTIONS, main_pid, PTRACE_O_EXITKILL, PTRACE_O_TRACESYSGOOD,
|
||||
PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK, PTRACE_O_TRACECLONE) != 0)
|
||||
return false;
|
||||
|
||||
// Let first thread actually start running (until syscall entry)
|
||||
if (ptrace(PTRACE_SYSCALL, main_pid, 0, 0) != 0)
|
||||
return false;
|
||||
|
||||
// Inform wirekit about first thread and initialize its trace context
|
||||
ENV.contexts().add_context(main_pid);
|
||||
ENV.contexts().context(main_pid)->mode = TraceContext::EXECUTION_MODE::USER;
|
||||
|
||||
ENV.contexts().context_set_active(main_pid);
|
||||
|
||||
if (kit.subject_start)
|
||||
kit.subject_start();
|
||||
|
||||
ENV.contexts().context_clear_active();
|
||||
|
||||
// Start tracer loop
|
||||
kw.release();
|
||||
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
while ((pid = waitpid(-1, &status, __WALL)) != -1 || errno != ECHILD /* No (grand)children left */) {
|
||||
if (pid == -1)
|
||||
FORCEQUIT;
|
||||
|
||||
/*
|
||||
New processes and threads inherit PTRACE_SYSCALL execution mode from parent and thus end up
|
||||
here when they perform their first syscall. Start logging context at this point.
|
||||
*/
|
||||
if (!ENV.contexts().context(pid)) {
|
||||
ENV.contexts().add_context(pid);
|
||||
ENV.contexts().context(pid)->mode = TraceContext::EXECUTION_MODE::USER;
|
||||
|
||||
ENV.contexts().context_set_active(pid);
|
||||
|
||||
if (kit.subject_start)
|
||||
kit.subject_start();
|
||||
|
||||
ENV.contexts().context_clear_active();
|
||||
}
|
||||
|
||||
// Activate context (definitely exists now)
|
||||
ENV.contexts().context_set_active(pid);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
// A child exited -> notify wirekit and stop tracing it
|
||||
if (kit.subject_exit)
|
||||
kit.subject_exit();
|
||||
|
||||
ENV.contexts().delete_context(pid);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
int sig = WSTOPSIG(status);
|
||||
|
||||
if (sig == SIGTRAP) {
|
||||
int event = (unsigned)status >> 16;
|
||||
|
||||
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK || event == PTRACE_EVENT_CLONE) {
|
||||
; // New thread but nothing to do -> tracer attaches on first syscall
|
||||
} else {
|
||||
FORCEQUIT;
|
||||
}
|
||||
} else if (sig == (SIGTRAP | 0x80) /* 0x80 thanks to PTRACE_O_TRACESYSGOOD */) {
|
||||
// Tracee was trapped on either syscall entry or exit
|
||||
TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
|
||||
if (ptrace(PTRACE_GETREGS, pid, 0, &ctx->regs) != 0)
|
||||
FORCEQUIT;
|
||||
|
||||
ctx->regs_dirty = false;
|
||||
|
||||
reg_t syscall = ctx->regs.orig_rax;
|
||||
|
||||
if (ctx->mode == TraceContext::EXECUTION_MODE::USER)
|
||||
ENV.entry_hook(syscall) || ENV.default_entry_hook(); // Short-circuit guaranteed by C++ standard ;)
|
||||
else if (ctx->mode == TraceContext::EXECUTION_MODE::KERNEL)
|
||||
ENV.exit_hook(syscall) || ENV.default_exit_hook();
|
||||
|
||||
if (ctx->regs_dirty) {
|
||||
if (ptrace(PTRACE_SETREGS, pid, NULL, &ctx->regs) != 0)
|
||||
FORCEQUIT;
|
||||
}
|
||||
|
||||
ctx->regs_dirty = false;
|
||||
|
||||
auto progress_mode = [](TraceContext::EXECUTION_MODE m) {
|
||||
switch (m) {
|
||||
case TraceContext::EXECUTION_MODE::STARTING:
|
||||
return TraceContext::EXECUTION_MODE::USER;
|
||||
case TraceContext::EXECUTION_MODE::USER:
|
||||
return TraceContext::EXECUTION_MODE::KERNEL;
|
||||
case TraceContext::EXECUTION_MODE::KERNEL:
|
||||
return TraceContext::EXECUTION_MODE::USER;
|
||||
}
|
||||
|
||||
return TraceContext::EXECUTION_MODE::STARTING;
|
||||
};
|
||||
|
||||
ctx->mode = progress_mode(ctx->mode);
|
||||
}
|
||||
|
||||
// Let tracee continue until next system call event (entry or exit)
|
||||
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) != 0)
|
||||
FORCEQUIT;
|
||||
} // else { Weird signal is happening, should be irrelevant to wirekit though! }
|
||||
|
||||
// Deactivate current context
|
||||
ENV.contexts().context_clear_active();
|
||||
}
|
||||
|
||||
// Notify wirekit that command has finished executing (no threads are left)
|
||||
if (kit.command_exit)
|
||||
kit.command_exit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_hook(reg_t syscall, hook_t entry, hook_t exit) {
|
||||
ENV.register_hooks(syscall, entry, exit);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_unhook(reg_t syscall) {
|
||||
rewire_syscall_hook(syscall, nullptr, nullptr);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_hook_default(hook_t entry, hook_t exit) {
|
||||
ENV.register_default_hooks(entry, exit);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_unhook_default() {
|
||||
rewire_syscall_hook_default(nullptr, nullptr);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_id(pid_t* pid) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
*pid = ctx->pid;
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_get_regs(user_regs_struct* regs) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
std::memcpy(regs, &ctx->regs, sizeof(ctx->regs));
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_set_regs(const user_regs_struct* regs) {
|
||||
TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
std::memcpy(&ctx->regs, regs, sizeof(ctx->regs));
|
||||
ctx->regs_dirty = true;
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_load_cstr(const char* subject_addr, char* buf, uint32_t* read, uint32_t n) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
*read = 0;
|
||||
while (*read < n) {
|
||||
errno = 0;
|
||||
long word = ptrace(PTRACE_PEEKDATA, ctx->pid, (char*)subject_addr, NULL);
|
||||
if (errno == -1l && errno != 0) // errno differentiates successful -1 word from error
|
||||
return REWIRE_FAILURE;
|
||||
|
||||
for (unsigned i = 0; i < sizeof(long) && *read < n; i++) {
|
||||
char c = *(reinterpret_cast<char*>(&word) + i); // Unlike bit operations, this is independent of endianness
|
||||
*(buf++) = c;
|
||||
(*read)++;
|
||||
|
||||
if (c == 0)
|
||||
return REWIRE_SUCCESS; // Encountered terminating null byte
|
||||
}
|
||||
|
||||
subject_addr += sizeof(long);
|
||||
}
|
||||
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
#include "interface.hpp"
|
||||
#include "environment.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/ptrace.h>
|
||||
#include <errno.h>
|
||||
|
||||
err_t(*wirekit_prepare_handle)() = nullptr;
|
||||
err_t(*wirekit_command_start_handle)(int, const char* const*) = nullptr;
|
||||
void(*wirekit_command_exit_handle)() = nullptr;
|
||||
void(*wirekit_subject_start_handle)() = nullptr;
|
||||
void(*wirekit_subject_exit_handle)() = nullptr;
|
||||
|
||||
err_t rewire_syscall_hook(reg_t syscall, hook_t entry, hook_t exit) {
|
||||
ENV.register_hooks(syscall, entry, exit);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_unhook(reg_t syscall) {
|
||||
rewire_syscall_hook(syscall, nullptr, nullptr);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_hook_default(hook_t entry, hook_t exit) {
|
||||
ENV.register_default_hooks(entry, exit);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_syscall_unhook_default() {
|
||||
rewire_syscall_hook_default(nullptr, nullptr);
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_id(pid_t* pid) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
*pid = ctx->pid;
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_get_regs(user_regs_struct* regs) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
std::memcpy(regs, &ctx->regs, sizeof(ctx->regs));
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_set_regs(const user_regs_struct* regs) {
|
||||
TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
std::memcpy(&ctx->regs, regs, sizeof(ctx->regs));
|
||||
ctx->regs_dirty = true;
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
err_t rewire_subject_load_cstr(const char* subject_addr, char* buf, uint32_t* read, uint32_t n) {
|
||||
const TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
if (!ctx)
|
||||
return REWIRE_FAILURE_NOCTX;
|
||||
|
||||
*read = 0;
|
||||
while (*read < n) {
|
||||
errno = 0;
|
||||
long word = ptrace(PTRACE_PEEKDATA, ctx->pid, (char*)subject_addr, NULL); // TODO
|
||||
if (errno == -1l && errno != 0) // errno differentiates successful -1 word from error
|
||||
return REWIRE_FAILURE;
|
||||
|
||||
for (unsigned i = 0; i < sizeof(long) && *read < n; i++) {
|
||||
char c = *(reinterpret_cast<char*>(&word) + i); // Unlike bit operations, this is independent of endianness
|
||||
*(buf++) = c;
|
||||
(*read)++;
|
||||
|
||||
if (c == 0)
|
||||
return REWIRE_SUCCESS; // Encountered terminating null byte
|
||||
}
|
||||
|
||||
subject_addr += sizeof(long);
|
||||
}
|
||||
|
||||
return REWIRE_SUCCESS;
|
||||
}
|
||||
|
||||
434
src/main.cpp
434
src/main.cpp
@ -1,434 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "cli.hpp"
|
||||
#include "dl.hpp"
|
||||
#include "install.hpp"
|
||||
#include "interface.hpp"
|
||||
#include "environment.hpp"
|
||||
|
||||
#define FORCEQUIT { std::cout << "Forcefully quitting!" << std::endl; while (true) exit(1); }
|
||||
|
||||
int launch_help(int argc, char** argv) {
|
||||
std::cout << "Use rewire via one of the following commands:" << std::endl;
|
||||
|
||||
std::cout << " \"rewire help\" -> You appear to already know what it does ;)" << std::endl;
|
||||
|
||||
std::cout << " \"rewire install <wirekit.so> <name>\" -> Installs a wirekit to execute programs with" << std::endl;
|
||||
std::cout << " <wirekit.so> -> The shared object to be installed as wirekit" << std::endl;
|
||||
std::cout << " <name> -> (Optional) The name via which the wirekit is supposed to be used" << std::endl;
|
||||
std::cout << " -> Defaults to \"wirekit\" when installing \"wirekit.so\"" << std::endl;
|
||||
|
||||
std::cout << " \"rewire uninstall <name>\" -> Removes a wirekit from the system" << std::endl;
|
||||
std::cout << " <name> -> The name of the wirekit to be uninstalled" << std::endl;
|
||||
|
||||
std::cout << " \"rewire list\" -> Lists all installed wirekits" << std::endl;
|
||||
|
||||
std::cout << " \"rewire <wirekit> <command+args>\"" << std::endl;
|
||||
std::cout << " <wirekit> -> The wirekit to use for execution" << std::endl;
|
||||
std::cout << " -> Can either be installed or in current working directory" << std::endl;
|
||||
std::cout << " <command+args> -> (Optional) A command to be executed" << std::endl;
|
||||
std::cout << " -> Acts as rewired shell if no command is given" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_install(int argc, char** argv) {
|
||||
if (argc < 3 || argc > 4) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to manage installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto kitcheck = [](const std::filesystem::path& kit) -> bool {
|
||||
DL tmp_dl { kit };
|
||||
if (!tmp_dl) {
|
||||
std::cout << "Failed to check wirekit requirements for installation!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tmp_dl.resolve("wirekit_prepare")) {
|
||||
std::cout << "Wirekit must implement \"wirekit_prepare\" to be installed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tmp_dl.resolve("wirekit_command_start")) {
|
||||
std::cout << "Wirekit must implement \"wirekit_command_start\" to be installed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!im.install(argv[2], (argc == 4 ? argv[3] : nullptr), kitcheck)) {
|
||||
std::cout << "Failed to install wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Successfully installed wirekit!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_uninstall(int argc, char** argv) {
|
||||
if (argc != 3) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to manage installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!im.uninstall(argv[2])) {
|
||||
std::cout << "Failed to uninstall wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Successfully uninstalled wirekit!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int launch_list(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to access installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto wirekits = im.installs();
|
||||
if (wirekits.empty()) {
|
||||
std::cout << "Currently, no wirekits are installed!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Currently, the following wirekits are installed:" << std::endl;
|
||||
for (const std::string& kit : wirekits)
|
||||
std::cout << " \"" << kit << "\"" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t dispatch_tracee(int argc, const char* const* argv) {
|
||||
pid_t child = fork();
|
||||
|
||||
if (child != 0)
|
||||
return child; // Either -1 on error or child pid
|
||||
|
||||
ptrace(PTRACE_TRACEME, 0, 0, 0);
|
||||
execvp(argv[0], (char* const*)argv); // Removing const here is in child process
|
||||
// Note -> Tracee stops with SIGTRAP immediately after execvp!
|
||||
|
||||
// Child should never return from execvp
|
||||
while (true) exit(1);
|
||||
}
|
||||
|
||||
class KillWrapper {
|
||||
pid_t m_pid;
|
||||
public:
|
||||
KillWrapper(const KillWrapper& other) = delete;
|
||||
KillWrapper(KillWrapper&& other) = delete;
|
||||
KillWrapper& operator=(const KillWrapper& other) = delete;
|
||||
KillWrapper& operator=(KillWrapper&& other) = delete;
|
||||
|
||||
KillWrapper(pid_t pid) : m_pid{ pid } {}
|
||||
|
||||
~KillWrapper() {
|
||||
if (m_pid != -1)
|
||||
kill(m_pid, SIGKILL);
|
||||
}
|
||||
|
||||
void release() {
|
||||
m_pid = -1;
|
||||
}
|
||||
};
|
||||
|
||||
void run_command(int argc, const char* const* argv) {
|
||||
EnvironmentScope es { ENV }; // Make sure environment is cleared after each command
|
||||
|
||||
// Notify wirekit about command start
|
||||
if (wirekit_command_start_handle(argc, argv) != REWIRE_SUCCESS) {
|
||||
std::cout << "Wirekit prohibited command execution!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch main command process
|
||||
pid_t main_pid = dispatch_tracee(argc, argv);
|
||||
if (main_pid == -1) {
|
||||
std::cout << "Failed to start command main process!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
KillWrapper kw { main_pid }; // Make sure it gets whiped until in main trace loop
|
||||
|
||||
int exec_status;
|
||||
if (waitpid(main_pid, &exec_status, 0) != main_pid || !WIFSTOPPED(exec_status) || WSTOPSIG(exec_status) != SIGTRAP) {
|
||||
std::cout << "Execvp failed to run the command!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
PTRACE_O_EXITKILL -> Kill tracees when tracer exits
|
||||
PTRACE_O_TRACESYSGOOD -> SIGTRAP signals caused by system call entry/exit stops have 0x80 bit set
|
||||
PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK, PTRACE_O_TRACECLONE -> Automatically trace new children (inherit options and mode)
|
||||
*/
|
||||
if (ptrace(PTRACE_SETOPTIONS, main_pid, PTRACE_O_EXITKILL, PTRACE_O_TRACESYSGOOD,
|
||||
PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK, PTRACE_O_TRACECLONE) != 0) {
|
||||
std::cout << "Tracer failed to set trace options of command main process!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Let first thread actually start running (until syscall entry)
|
||||
if (ptrace(PTRACE_SYSCALL, main_pid, 0, 0) != 0) {
|
||||
std::cout << "Failed to start command main process execution!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform wirekit about first thread and initialize its trace context
|
||||
ENV.contexts().add_context(main_pid);
|
||||
ENV.contexts().context(main_pid)->mode = TraceContext::EXECUTION_MODE::USER;
|
||||
|
||||
ENV.contexts().context_set_active(main_pid);
|
||||
|
||||
if (wirekit_subject_start_handle)
|
||||
wirekit_subject_start_handle();
|
||||
|
||||
ENV.contexts().context_clear_active();
|
||||
|
||||
// Start tracer loop
|
||||
kw.release();
|
||||
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
while ((pid = waitpid(-1, &status, __WALL)) != -1 || errno != ECHILD /* No (grand)children left */) {
|
||||
if (pid == -1) {
|
||||
std::cout << "Tracer interrupted by signal (errno=" << errno << ")!" << std::endl;
|
||||
FORCEQUIT;
|
||||
}
|
||||
|
||||
/*
|
||||
New processes and threads inherit PTRACE_SYSCALL execution mode from parent and thus end up
|
||||
here when they perform their first syscall. Start logging context at this point.
|
||||
*/
|
||||
if (!ENV.contexts().context(pid)) {
|
||||
ENV.contexts().add_context(pid);
|
||||
ENV.contexts().context(pid)->mode = TraceContext::EXECUTION_MODE::USER;
|
||||
|
||||
ENV.contexts().context_set_active(pid);
|
||||
|
||||
if (wirekit_subject_start_handle)
|
||||
wirekit_subject_start_handle();
|
||||
|
||||
ENV.contexts().context_clear_active();
|
||||
}
|
||||
|
||||
// Activate context (definitely exists now)
|
||||
ENV.contexts().context_set_active(pid);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
// A child exited -> notify wirekit and stop tracing it
|
||||
if (wirekit_subject_exit_handle)
|
||||
wirekit_subject_exit_handle();
|
||||
|
||||
ENV.contexts().delete_context(pid);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
int sig = WSTOPSIG(status);
|
||||
|
||||
if (sig == SIGTRAP) {
|
||||
int event = (unsigned)status >> 16;
|
||||
|
||||
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK || event == PTRACE_EVENT_CLONE) {
|
||||
; // New thread but nothing to do -> tracer attaches on first syscall
|
||||
} else {
|
||||
std::cout << "Received non-syscall SIGTRAP without thread creation event!" << std::endl;
|
||||
FORCEQUIT;
|
||||
}
|
||||
} else if (sig == (SIGTRAP | 0x80) /* 0x80 thanks to PTRACE_O_TRACESYSGOOD */) {
|
||||
// Tracee was trapped on either syscall entry or exit
|
||||
TraceContext* ctx = ENV.contexts().context_get_active();
|
||||
|
||||
if (ptrace(PTRACE_GETREGS, pid, 0, &ctx->regs) != 0) {
|
||||
std::cout << "Failed to read tracee registers!" << std::endl;
|
||||
FORCEQUIT;
|
||||
}
|
||||
|
||||
ctx->regs_dirty = false;
|
||||
|
||||
reg_t syscall = ctx->regs.orig_rax;
|
||||
|
||||
if (ctx->mode == TraceContext::EXECUTION_MODE::USER)
|
||||
ENV.entry_hook(syscall) || ENV.default_entry_hook(); // Short-circuit guaranteed by C++ standard ;)
|
||||
else if (ctx->mode == TraceContext::EXECUTION_MODE::KERNEL)
|
||||
ENV.exit_hook(syscall) || ENV.default_exit_hook();
|
||||
|
||||
if (ctx->regs_dirty) {
|
||||
if (ptrace(PTRACE_SETREGS, pid, NULL, &ctx->regs) != 0) {
|
||||
std::cout << "Failed to write tracee registers!" << std::endl;
|
||||
FORCEQUIT;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->regs_dirty = false;
|
||||
|
||||
auto progress_mode = [](TraceContext::EXECUTION_MODE m) {
|
||||
switch (m) {
|
||||
case TraceContext::EXECUTION_MODE::STARTING:
|
||||
return TraceContext::EXECUTION_MODE::USER;
|
||||
case TraceContext::EXECUTION_MODE::USER:
|
||||
return TraceContext::EXECUTION_MODE::KERNEL;
|
||||
case TraceContext::EXECUTION_MODE::KERNEL:
|
||||
return TraceContext::EXECUTION_MODE::USER;
|
||||
}
|
||||
|
||||
return TraceContext::EXECUTION_MODE::STARTING;
|
||||
};
|
||||
|
||||
ctx->mode = progress_mode(ctx->mode);
|
||||
}
|
||||
|
||||
// Let tracee continue until next system call event (entry or exit)
|
||||
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) != 0) {
|
||||
std::cout << "Failed to continue command execution after SIGTRAP!" << std::endl;
|
||||
FORCEQUIT;
|
||||
}
|
||||
} // else { Weird signal is happening, should be irrelevant to wirekit though! }
|
||||
|
||||
// Deactivate current context
|
||||
ENV.contexts().context_clear_active();
|
||||
}
|
||||
|
||||
// Notify wirekit that command has finished executing (no threads are left)
|
||||
if (wirekit_command_exit_handle)
|
||||
wirekit_command_exit_handle();
|
||||
}
|
||||
|
||||
int launch_run(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
InstallManager im;
|
||||
if (!im) {
|
||||
std::cout << "Unable to access installations!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::filesystem::path wirekit_path;
|
||||
if (!im.get(argv[1], wirekit_path)) {
|
||||
std::cout << "No wirekit named \"" << argv[1] << "\" is installed!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DL wirekit { wirekit_path };
|
||||
if (!wirekit) {
|
||||
std::cout << "Unable to load wirekit!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Collect wirekit control function handles
|
||||
if (!(wirekit_prepare_handle = (err_t(*)())wirekit.resolve("wirekit_prepare"))) {
|
||||
std::cout << "Wirekit is missing \"wirekit_prepare\" implementation!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(wirekit_command_start_handle = (err_t(*)(int, const char* const*))wirekit.resolve("wirekit_command_start"))) {
|
||||
std::cout << "Wirekit is missing \"wirekit_command_start\" implementation!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
wirekit_command_exit_handle = (void(*)())wirekit.resolve("wirekit_command_exit");
|
||||
wirekit_subject_start_handle = (void(*)())wirekit.resolve("wirekit_subject_start");
|
||||
wirekit_subject_exit_handle = (void(*)())wirekit.resolve("wirekit_subject_exit");
|
||||
|
||||
// Prepare wirekit
|
||||
err_t prepare_err;
|
||||
if ((prepare_err = wirekit_prepare_handle()) != REWIRE_SUCCESS) {
|
||||
std::cout << "Wirekit preparation failed (wirekit_prepare returned " << prepare_err << ")!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Execute commands in rewired syscall environment
|
||||
if (argc > 2) {
|
||||
run_command(argc - 2, (const char**)(argv + 2)); // Execute a single command
|
||||
} else {
|
||||
// Shell mode
|
||||
while (true) {
|
||||
std::cout << "> ";
|
||||
|
||||
std::string command;
|
||||
std::getline(std::cin, command);
|
||||
|
||||
// Split command at whitespaces
|
||||
std::stringstream command_stream { command };
|
||||
std::vector<std::string> command_split; // No need to reserve, presumably not many elements
|
||||
|
||||
std::string part;
|
||||
while (command_stream >> std::quoted(part))
|
||||
command_split.emplace_back(std::move(part));
|
||||
|
||||
if (command_split.empty())
|
||||
continue;
|
||||
|
||||
if (command_split[0] == "exit")
|
||||
break;
|
||||
|
||||
// Compile argv (cstr pointer array)
|
||||
std::vector<const char*> command_split_cstr;
|
||||
command_split_cstr.resize(command_split.size() + 1, nullptr); // +1 for nullptr termination
|
||||
|
||||
for (std::size_t i = 0; i < command_split.size(); i++)
|
||||
command_split_cstr[i] = command_split[i].c_str();
|
||||
|
||||
// Execute command
|
||||
run_command((int)command_split_cstr.size() - 1, command_split_cstr.data());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Invalid usage, try \"rewire help\"!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
LAUNCH_MODE lm = launch_mode(argv[1]);
|
||||
|
||||
switch (lm) {
|
||||
case LAUNCH_MODE::HELP:
|
||||
return launch_help(argc, argv);
|
||||
case LAUNCH_MODE::INSTALL:
|
||||
return launch_install(argc, argv);
|
||||
case LAUNCH_MODE::UNINSTALL:
|
||||
return launch_uninstall(argc, argv);
|
||||
case LAUNCH_MODE::LIST:
|
||||
return launch_list(argc, argv);
|
||||
case LAUNCH_MODE::RUN:
|
||||
return launch_run(argc, argv);
|
||||
default:
|
||||
std::cout << "Invalid launch mode!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user