From 96619b66ebff84ff0dc6825343d522eecb6d7932 Mon Sep 17 00:00:00 2001 From: xwashere Date: Tue, 5 Mar 2024 17:46:15 -0500 Subject: [PATCH] framework for LRSS --- CMakeLists.txt | 61 ++++++++++++-- README | 4 + common/include/lindows/ntstatus.h | 11 +++ lrss/CMakeLists.txt | 16 ++++ lrss/README | 2 + lrss/include/lindows/lrss.h | 27 +++++++ lrss/lib/library.cxx | 63 +++++++++++++++ lrss/main.cxx | 103 ++++++++++++++++++++++++ lsmss/CMakeLists.txt | 2 +- lsmss/main.cxx | 128 ++++++++++++++++++++++++++---- scripts/copylib | 52 +++++++----- 11 files changed, 424 insertions(+), 45 deletions(-) create mode 100644 common/include/lindows/ntstatus.h create mode 100644 lrss/CMakeLists.txt create mode 100644 lrss/README create mode 100644 lrss/include/lindows/lrss.h create mode 100644 lrss/lib/library.cxx create mode 100644 lrss/main.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index ade1047..586381b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,46 @@ function(lw_add_executable NAME) set_property(SOURCE TARGET_DIRECTORY ${NAME} APPEND PROPERTY OBJECT_DEPENDS GCC-install ) + + target_include_directories(${NAME} PUBLIC "${CMAKE_SOURCE_DIR}/common/include") + else() + message(FATAL_ERROR "LW_TARGET not defined, are you in a lw_project?") + endif() +endfunction() + +# defines a library target. this must be used if lw_project is used +function(lw_add_library NAME) + set(cpa_flag SHARED) + set(cpa_single) + set(cpa_multi SOURCES) + cmake_parse_arguments(PARSE_ARGV 1 ARG "${cpa_flag}" "${cpa_single}" "${cpa_multi}") + + if (ARG_SHARED) + add_library(${NAME} SHARED) + else() + add_library(${NAME}) + endif() + + if(ARG_SOURCES) + target_sources(${NAME} PRIVATE ${ARG_SOURCES}) + target_compile_options(${NAME} PRIVATE + $<$:${LW_CROSS_CFLAGS}> + $<$:${LW_CROSS_CXXFLAGS}> + ) + target_link_options(${NAME} PRIVATE ${LW_CROSS_LDFLAGS}) + endif() + + if(LW_TARGET) + set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS + $ + $ + ) + + set_property(SOURCE TARGET_DIRECTORY ${NAME} APPEND PROPERTY OBJECT_DEPENDS + GCC-install + ) + + target_include_directories(${NAME} PUBLIC "${CMAKE_SOURCE_DIR}/common/include") else() message(FATAL_ERROR "LW_TARGET not defined, are you in a lw_project?") endif() @@ -133,6 +173,7 @@ ExternalProject_Add(GCC --disable-bootstrap BUILD_COMMAND make INSTALL_COMMAND make install + COMMAND cp -r ${CMAKE_BINARY_DIR}/gcc/prefix/lib64 ${CMAKE_BINARY_DIR}/gcc/lib64 STEP_TARGETS install ) @@ -267,7 +308,7 @@ add_custom_command(OUTPUT lindows_c.img if=/dev/zero of=lindows_c.img bs=1M - count=256 + count=512 # format disk with new NTFS filesystem COMMAND "${NTFS3g_Mkfs_EXECUTABLE}" ARGS -F # scary! -L Windows @@ -303,9 +344,14 @@ add_custom_command(OUTPUT lindows_c.img # copy curses to system32 COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/copylib ncurses - # copy lsmss - COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $ lindows_c/Windows/System32/lsmss.exe + # continuedd + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/copylib gcc lib64 + # copy lindows binaries + COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $ lindows_c/Windows/System32/lsmss.exe + COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $ lindows_c/Windows/System32/lrss.exe + COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $ lindows_c/Windows/System32/liblrssclient.so + # create users folder COMMAND ${CoreUtils_Mkdir_EXECUTABLE} ARGS lindows_c/Users @@ -313,7 +359,7 @@ add_custom_command(OUTPUT lindows_c.img COMMAND "umount" ARGS lindows_c DEPENDS - lsmss + lsmss lrss lrssclient Linux-install GLibC-install Bash-install @@ -351,12 +397,12 @@ add_custom_command(OUTPUT lindows.img COMMAND echo ARGS label: gpt > disk.sfdisk COMMAND echo ARGS unit: sectors >> disk.sfdisk COMMAND echo ARGS start=1MiB, size=33MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B >> disk.sfdisk - COMMAND echo ARGS start=34MiB, size=256MiB, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=48D29DA8-2FF8-4F23-BA1A-0E8CCFC329E2 >> disk.sfdisk + COMMAND echo ARGS start=34MiB, size=512MiB, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=48D29DA8-2FF8-4F23-BA1A-0E8CCFC329E2 >> disk.sfdisk COMMAND "${CoreUtils_dd_EXECUTABLE}" ARGS if=/dev/zero of=lindows.img bs=1M - count=300 + count=600 # partition disk COMMAND sfdisk ARGS lindows.img < disk.sfdisk # write partitions @@ -371,7 +417,7 @@ add_custom_command(OUTPUT lindows.img if=lindows_c.img of=lindows.img bs=1M - count=256 + count=512 seek=34 conv=notrunc DEPENDS lindows_c.img lindows_efi.img @@ -381,3 +427,4 @@ add_custom_command(OUTPUT lindows.img add_custom_target(lindows ALL DEPENDS lindows.img) add_subdirectory(lsmss) +add_subdirectory(lrss) diff --git a/README b/README index 41e8fab..e35a797 100644 --- a/README +++ b/README @@ -1,3 +1,7 @@ == NAMING == Pii: Preinit internal core routines +Lrc: Lindows Registry client + +== SUBSYSTEM IPC == +When a subsystem starts, LSMSS will wait for it to send a SIGSTOP to itself, afterwards, it will send a continue signal then keep going. diff --git a/common/include/lindows/ntstatus.h b/common/include/lindows/ntstatus.h new file mode 100644 index 0000000..46bcb28 --- /dev/null +++ b/common/include/lindows/ntstatus.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +/** + * Windows NT Error code + */ +typedef uint32_t NTSTATUS; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_INTERNAL_ERROR 0xC00000E5 diff --git a/lrss/CMakeLists.txt b/lrss/CMakeLists.txt new file mode 100644 index 0000000..ef46f95 --- /dev/null +++ b/lrss/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.28) + +lw_project(lrss + DESCRIPTION "Lindows Registry Subsystem" + TARGET LINDOWS +) + +lw_add_executable(lrss + SOURCES main.cxx +) +target_link_libraries(lrss PRIVATE lrssclient) + +lw_add_library(lrssclient SHARED + SOURCES lib/library.cxx +) +target_include_directories(lrssclient PUBLIC include) diff --git a/lrss/README b/lrss/README new file mode 100644 index 0000000..c9ecc69 --- /dev/null +++ b/lrss/README @@ -0,0 +1,2 @@ +== Lindows Registry Subsystem == +The lindows registry subsystem preforms all operations related to the windows registry, when a linux program runs, it connects to LRSS, authenticates and then communicates to it via a unix domain socket, when a windows program runs, it connects to the wineserver, which is patched to use LRSS instead of it's builtin registry code. diff --git a/lrss/include/lindows/lrss.h b/lrss/include/lindows/lrss.h new file mode 100644 index 0000000..baefb85 --- /dev/null +++ b/lrss/include/lindows/lrss.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the address of the LRSS socket + * \param buffer (out) buffer to write the address to + * \return status code: + * STATUS_SUCCESS: I really hope this function always succeeds + */ +extern NTSTATUS LrcSocketAddress(char Buffer[108]); + +/** + * Open a connection to LRSS + * \return status code + * STATUS_SUCCESS: Registry connection has been established + * STATUS_INTERNAL_ERROR: Something went wrong + */ +extern NTSTATUS LrcOpenRegistry(); + +#ifdef __cplusplus +} +#endif diff --git a/lrss/lib/library.cxx b/lrss/lib/library.cxx new file mode 100644 index 0000000..3c63e42 --- /dev/null +++ b/lrss/lib/library.cxx @@ -0,0 +1,63 @@ +#include +#include + +#include +#include +#include + +#include + +static const char SOCKET_ADDRESS[108] = { + 0, 'L', 'R', 'S', 'S', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static int REGISTRY_SOCKET = -1; + +NTSTATUS LrcSocketAddress(char Buffer[108]) { + std::memcpy(Buffer, SOCKET_ADDRESS, 108); + return STATUS_SUCCESS; +} + +NTSTATUS LrcOpenRegistry() { + // This may not work if the thing got closed somehow :> + if (REGISTRY_SOCKET == -1) { + REGISTRY_SOCKET = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (REGISTRY_SOCKET < 0) { + REGISTRY_SOCKET = -1; + return STATUS_INTERNAL_ERROR; + } + + int tmp = 1; + if (setsockopt(REGISTRY_SOCKET, SOL_SOCKET, SO_PASSCRED, &tmp, sizeof(tmp)) < 0) { + close(REGISTRY_SOCKET); + REGISTRY_SOCKET = -1; + return STATUS_INTERNAL_ERROR; + } + + struct sockaddr_un sockaddr { + .sun_family = AF_UNIX, + .sun_path = {} + }; + LrcSocketAddress(sockaddr.sun_path); + if (connect(REGISTRY_SOCKET, reinterpret_cast(&sockaddr), sizeof(sockaddr)) < 0) { + close(REGISTRY_SOCKET); + REGISTRY_SOCKET = -1; + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; + } else return STATUS_SUCCESS; +} diff --git a/lrss/main.cxx b/lrss/main.cxx new file mode 100644 index 0000000..a29a65c --- /dev/null +++ b/lrss/main.cxx @@ -0,0 +1,103 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include + +class ConnectionState { + // -- general -- + int socket; + + public: + + // -- constructors -- + ConnectionState(int sock) : socket(sock) {}; +}; + +int main() { + int tmp; + + // Hi + std::cout << "Starting Lindows Registry Subsystem\n"; + + // Set up handshake socket (used to be used for a handshake, kept the name, sry) + struct sockaddr_un theSockAddr { + .sun_family = AF_UNIX, + .sun_path = {} + }; + + // Get the listening address + if (auto err = LrcSocketAddress(theSockAddr.sun_path)) { + std::cerr << "Failed to get LRSS socket address: " << err << std::endl; + return 1; + } + + // Create the socket + int handshakeSocket = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (handshakeSocket < 0) { + perror("socket"); + return 1; + } + + // Configure the socket + tmp = 1; + if (setsockopt(handshakeSocket, SOL_SOCKET, SO_PASSCRED, &tmp, sizeof(tmp)) < 0) { + perror("setsockopt"); + return 1; + } + + // Bind to the socket + if (bind(handshakeSocket, reinterpret_cast(&theSockAddr), sizeof(theSockAddr)) < 0) { + perror("bind"); + return 1; + } + + // Listen on da socket + if (listen(handshakeSocket, 20) < 0) { + perror("listen"); + return 1; + } + + // Wait for LSMSS + raise(SIGSTOP); + + // Main loop + std::unordered_map connections {}; + std::vector pollfds {{ handshakeSocket, POLLIN, 0 }}; + while (true) { + // Poll for whateversies + if (poll(pollfds.data(), pollfds.size(), -1) < 0) { + perror("poll"); + return 1; + } + + // First fd is our handshake socket + if (pollfds[0].revents) { + if (pollfds[0].revents & POLLIN) { + // Accept the connection and add it to our client list + int sock = accept(handshakeSocket, nullptr, nullptr); + if (sock < 0) { + perror("accept"); + } + + connections.emplace(sock, sock); + pollfds.emplace(pollfds.end(), sock, POLLIN | POLLHUP, 0); + } + + pollfds[0].revents = 0; + } + + // Iterate over client sockets + for (auto it = ++pollfds.begin(); it < pollfds.end(); it++) { + std::cout << it->fd << " " << it->revents << "\n"; + it->revents = 0; + } + } + + return 1; +} diff --git a/lsmss/CMakeLists.txt b/lsmss/CMakeLists.txt index 45bfaac..cd74723 100644 --- a/lsmss/CMakeLists.txt +++ b/lsmss/CMakeLists.txt @@ -9,5 +9,5 @@ lw_project(lsmss lw_add_executable(lsmss SOURCES main.cxx ) - target_link_options(lsmss PRIVATE "-static") +target_include_directories(lsmss PRIVATE $) diff --git a/lsmss/main.cxx b/lsmss/main.cxx index 50902ee..2d36dd3 100644 --- a/lsmss/main.cxx +++ b/lsmss/main.cxx @@ -3,19 +3,100 @@ #include #include #include +#include #include #include #include #include +#include + +#include + +void PiiLog(const std::string& message); +[[noreturn]] void PiiAbortBoot(); + +/** + i* State for a subsystem managed by LSMSS + */ +class PiiSubsystemState { + // -- General info -- + std::string SubsystemName; // Subsystem display name in debug logs + + bool Running = false; // Subsystem is running + + // -- Linux information -- + pid_t LnxPid = 0; // PID + std::filesystem::path LnxPath; // Binary path + + public: + + // -- Constructors -- + PiiSubsystemState(const std::string& name, const std::filesystem::path& path) { + this->SubsystemName = name; + this->LnxPath = path; + + return; + } + + // -- General -- + + /** + * Start the subsystem process + */ + void Start() { + PiiLog("Starting subsystem " + this->SubsystemName); + if (pid_t child = fork()) { + // save the process id + this->LnxPid = child; + this->Running = true; + + while (true) { + int status; + if (waitpid(this->LnxPid, &status, WUNTRACED) < 0) { + perror("waitpid"); + PiiLog("Failed to start " + this->SubsystemName); + this->Running = false; + return; + } + + if (WIFEXITED(status)) { + PiiLog("Failed to start " + this->SubsystemName + " (Exit code " + std::to_string(WEXITSTATUS(status)) + ")"); + this->Running = false; + return; + } + + if (WIFSTOPPED(status)) { + if (kill(this->LnxPid, SIGCONT) < 0) { + perror("kill"); + PiiLog("Failed to start " + this->SubsystemName + ". Unknown state, boot process cannot continue."); + PiiAbortBoot(); + } + + PiiLog("Started subsystem " + this->SubsystemName); + return; + } + } + + return; + } else { + char* args[] = { strdup(this->LnxPath.c_str()), nullptr }; + execv(args[0], args); + + // this should never happen + std::exit(1); + } + } +}; + /** * Log something. * * \param message to log */ -void Pii_Log(const std::string& message) { +void PiiLog(const std::string& message) { std::cout << message << "\n"; return; } @@ -26,7 +107,7 @@ void Pii_Log(const std::string& message) { * \param path absolute path to program (duh) * \return program return value, 1 if it does not exist or -1 on error */ -int Pii_ExecuteProgram(const std::string& path) { +int PiiExecuteProgram(const std::string& path) { if (pid_t child = fork()) { int exitcode = -1; waitpid(child, &exitcode, 0); @@ -45,36 +126,53 @@ int Pii_ExecuteProgram(const std::string& path) { * Abort the boot process and drop the user into a recovery shell */ [[noreturn]] -void Pii_AbortBoot() { - Pii_Log("Unable to complete preinitialization. You have been dropped into a minimal recovery environment. Good luck."); - Pii_ExecuteProgram("/Windows/System32/bash.exe"); +void PiiAbortBoot() { + PiiLog("Unable to complete preinitialization. You have been dropped into a minimal recovery environment. Good luck."); + PiiExecuteProgram("/Windows/System32/bash.exe"); std::exit(1); } int main() { - // -- just print shit -- - Pii_Log("Starting Lindows Session Manager Subsystem"); + std::vector subsystems {}; + // -- just print shit -- + PiiLog("Starting Lindows Session Manager Subsystem"); + + // -- dynamic linking fun -- - Pii_Log("Regenerating dynamic linker cache"); - Pii_ExecuteProgram("/Windows/System32/ldconfig.exe"); + PiiLog("Regenerating dynamic linker cache"); + PiiExecuteProgram("/Windows/System32/ldconfig.exe"); // -- registry -- - Pii_Log("Starting Lindows Registry Subsystem"); - // TODO - Pii_Log("Loading Lindows registry"); + PiiLog("Starting Lindows Registry Subsystem"); + subsystems.emplace(subsystems.end(), "Lindows Registry Subsystem", "/Windows/System32/lrss.exe") + ->Start(); // We don't actually know if this started, we aren't in the mainloop. We just have to pray :> + + PiiLog("Loading Lindows registry"); + auto liblrssclient = dlopen("/Windows/System32/liblrssclient.so", RTLD_NOW); + auto pLrcOpenRegistry = reinterpret_cast(dlsym(liblrssclient, "LrcOpenRegistry")); + if (auto error = dlerror()) { + PiiLog(error); + PiiAbortBoot(); + } + + if (pLrcOpenRegistry()) { + PiiLog("Failed to connect to registry subsystem."); + PiiAbortBoot(); + } + // TODO // -- swap -- - Pii_Log("Creating pagefiles"); + PiiLog("Creating pagefiles"); // TODO // -- env -- - Pii_Log("Setting environment variables"); + PiiLog("Setting environment variables"); // TODO // we are never ok - Pii_AbortBoot(); + PiiAbortBoot(); return 0; } diff --git a/scripts/copylib b/scripts/copylib index 104820d..b8149a5 100755 --- a/scripts/copylib +++ b/scripts/copylib @@ -1,30 +1,38 @@ #!/bin/sh # ARG 1: LIB PATH +# ARG 2: LIB DIR -# copy libs -find $1/bin $1/lib -type d | sed -r 's/'$1'\/(bin|lib)\///;Tn;s/^/mkdir lindows_c\/Windows\/System32\//;by;:n;d;:y;e' > /dev/null; -find $1/bin -type f | sed -r 's/'$1'\/bin\/(.*)/cp '$1'\/bin\/\1 lindows_c\/Windows\/System32\/\1.exe/;e' > /dev/null -find $1/lib -type f | sed -r 's/'$1'\/lib\/(.*)/cp '$1'\/lib\/\1 lindows_c\/Windows\/System32\/\1/;e' > /dev/null +main() { + # copy libs + find $1/bin $1/$2 -type d | sed -r 's/'$1'\/(bin|'$2')\///;Tn;s/^/mkdir lindows_c\/Windows\/System32\//;by;:n;d;:y;e' > /dev/null; + find $1/bin -type f | sed -r 's/'$1'\/bin\/(.*)/cp '$1'\/bin\/\1 lindows_c\/Windows\/System32\/\1.exe/;e' > /dev/null + find $1/$2 -type f | sed -r 's/'$1'\/'$2'\/(.*)/cp '$1'\/'$2'\/\1 lindows_c\/Windows\/System32\/\1/;e' > /dev/null -# ntfs-3g cant seem to figure out ntfs symlinks, its 1500 lines of code for reparse points dont even work -# with symlinks, so we have to build the reparse point and do it ourselves -for lib in $(cd $1/lib && find -type l); do - WPATH="$(echo -n $(readlink "$1/lib/$lib"))" - LPATH="$(echo -n '/Windows/System32/'"$WPATH" | sed -E 's/(.)/\1\\x00/g;s/\//\\\\/g')" # sed -E 's/(.)/\\0\1/g'); - LPATHL=$(( 36 + ${#WPATH} * 2 )); + # ntfs-3g cant seem to figure out ntfs symlinks, its 1500 lines of code for reparse points dont even work + # with symlinks, so we have to build the reparse point and do it ourselves + for lib in $(cd $1/$2 && find -type l); do + WPATH="$(echo -n $(readlink "$1/$2/$lib"))" + LPATH="$(echo -n '/Windows/System32/'"$WPATH" | sed -E 's/(.)/\1\\x00/g;s/\//\\\\/g')" # sed -E 's/(.)/\\0\1/g'); + LPATHL=$(( 36 + ${#WPATH} * 2 )); - LPATHL1=$(printf "%o" $((($LPATHL >> 8 ) & 255 )) ); - LPATHL0=$(printf "%o" $((($LPATHL ) & 255 )) ); + LPATHL1=$(printf "%o" $((($LPATHL >> 8 ) & 255 )) ); + LPATHL0=$(printf "%o" $((($LPATHL ) & 255 )) ); - LBUF="\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\x00\\x00$LPATH" - LBUFL=$(( 12 + $LPATHL )) - LBUFL1=$(printf "%o" $((($LBUFL >> 8) & 255 )) ); - LBUFL0=$(printf "%o" $((($LBUFL ) & 255 )) ); + LBUF="\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\x00\\x00$LPATH" + LBUFL=$(( 12 + $LPATHL )) + LBUFL1=$(printf "%o" $((($LBUFL >> 8) & 255 )) ); + LBUFL0=$(printf "%o" $((($LBUFL ) & 255 )) ); - touch "lindows_c/Windows/System32/$lib"; - setfattr -h -n system.ntfs_reparse_data \ - -v "0S$(printf "\x0C\\x00\\x00\xA0\\$LBUFL0\\$LBUFL1\\x00\\x00$LBUF" | base64 -w0)"\ - "lindows_c/Windows/System32/$lib"; + touch "lindows_c/Windows/System32/$lib"; + setfattr -h -n system.ntfs_reparse_data \ + -v "0S$(printf "\x0C\\x00\\x00\xA0\\$LBUFL0\\$LBUFL1\\x00\\x00$LBUF" | base64 -w0)"\ + "lindows_c/Windows/System32/$lib"; - #ln -sr "lindows_c/Windows/System32/$(readlink $1/lib/$lib)" "lindows_c/Windows/System32/$lib" -done + #ln -sr "lindows_c/Windows/System32/$(readlink $1/lib/$lib)" "lindows_c/Windows/System32/$lib" + done +} + +[ -z "$2" ] && main "$1" lib; +[ -n "$2" ] && main "$1" "$2"; + +exit 0;