From 218b4d7d7650d5b1aad7adb04958c90e8ef388ba Mon Sep 17 00:00:00 2001 From: ConorB <11508137+ConorBobbleHat@users.noreply.github.com> Date: Mon, 18 Sep 2023 07:05:47 +0100 Subject: [PATCH] Implement enough process handling logic to make psyq4.0 happy (#46) * Implement enough process handling logic to make psyq4.0 happy * revert gitignore * data type update * PR review * DEBUG_LOG mistake --------- Co-authored-by: ConorBobbleHat --- .gitignore | 2 +- CMakeLists.txt | 1 + common.h | 2 + dll/kernel32.cpp | 133 +++++++++++++++++++++++++++++++++++++++-------- handles.h | 1 + main.cpp | 15 +++++- processes.cpp | 24 +++++++++ processes.h | 12 +++++ 8 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 processes.cpp create mode 100644 processes.h diff --git a/.gitignore b/.gitignore index f7d5541..1cb8d6d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ build/ .vscode/ # CLion .idea/ -cmake-build-*/ +cmake-build-*/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index df72242..f3a9fc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(wibo dll/vcruntime.cpp dll/version.cpp files.cpp + processes.cpp handles.cpp loader.cpp main.cpp diff --git a/common.h b/common.h index 844c42f..b467b09 100644 --- a/common.h +++ b/common.h @@ -72,8 +72,10 @@ namespace wibo { extern uint32_t lastError; extern char **argv; extern int argc; + extern char *executableName; extern char *commandLine; extern bool debugEnabled; + extern unsigned int debugIndent; void debug_log(const char *fmt, ...); diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index cd60fb1..80c3d9c 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -1,7 +1,9 @@ #include "common.h" #include "files.h" +#include "processes.h" #include "handles.h" #include +#include #include #include #include @@ -11,6 +13,8 @@ #include #include #include +#include +#include typedef union _RTL_RUN_ONCE { PVOID Ptr; @@ -198,32 +202,115 @@ namespace kernel32 { exit(uExitCode); } - int WIN_FUNC CreateProcessA( - const char *lpApplicationName, - char *lpCommandLine, - void *lpProcessAttributes, - void *lpThreadAttributes, - int bInheritHandles, - int dwCreationFlags, - void *lpEnvironment, - const char *lpCurrentDirectory, - void *lpStartupInfo, - void *lpProcessInformation - ) { - printf("CreateProcessA %s \"%s\" %p %p %d 0x%x %p %s %p %p\n", - lpApplicationName, - lpCommandLine, - lpProcessAttributes, + BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { + DEBUG_LOG("GetExitCodeProcess\n"); + + processes::Process* process = processes::processFromHandle(hProcess, false); + *lpExitCode = process->exitCode; + return 1; // success in retrieval + } + + struct PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; + }; + + + BOOL WIN_FUNC CreateProcessA( + LPCSTR lpApplicationName, + LPSTR lpCommandLine, + void *lpProcessAttributes, + void *lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, + void *lpStartupInfo, + PROCESS_INFORMATION *lpProcessInformation + ) { + DEBUG_LOG("CreateProcessA %s \"%s\" %p %p %d 0x%x %p %s %p %p\n", + lpApplicationName, + lpCommandLine, + lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory ? lpCurrentDirectory : "", - lpStartupInfo, - lpProcessInformation - ); - printf("Cannot handle process creation, aborting\n"); - exit(1); + lpStartupInfo, + lpProcessInformation + ); + + // Argument parsing + // First: how many arguments do we have? + size_t argc = 2; + + for (size_t i = 1; i < strlen(lpCommandLine); i++) { + if (isspace(lpCommandLine[i]) && !isspace(lpCommandLine[i - 1])) + argc++; + } + + char **argv = (char **) calloc(argc + 1, sizeof(char*)); + argv[0] = wibo::executableName; + argv[1] = (char *) files::pathFromWindows(lpApplicationName).string().c_str(); + + char* arg = strtok(lpCommandLine, " "); + size_t current_arg_index = 2; + + while (arg != NULL) { + // We're deliberately discarding the first token here + // to prevent from doubling up on the target executable name + // (it appears as lpApplicationName, and as the first token in lpCommandLine) + arg = strtok(NULL, " "); + argv[current_arg_index++] = arg; + } + + argv[argc] = NULL; // Last element in argv should be a null pointer + + // YET TODO: take into account process / thread attributes, environment variables + // working directory, etc. + setenv("WIBO_DEBUG_INDENT", std::to_string(wibo::debugIndent + 1).c_str(), true); + + pid_t pid; + if (posix_spawn(&pid, wibo::executableName, NULL, NULL, argv, environ)) { + return 0; + }; + + *lpProcessInformation = { + .hProcess = processes::allocProcessHandle(pid), + .hThread = nullptr, + .dwProcessId = (DWORD) pid, + .dwThreadId = 42 + }; + + return 1; + } + + unsigned int WIN_FUNC WaitForSingleObject(void *hHandle, unsigned int dwMilliseconds) { + DEBUG_LOG("WaitForSingleObject (%u)\n", dwMilliseconds); + + // TODO - wait on other objects? + + // TODO: wait for less than forever + assert(dwMilliseconds == 0xffffffff); + + processes::Process* process = processes::processFromHandle(hHandle, false); + + int status; + waitpid(process->pid, &status, 0); + + if (WIFEXITED(status)) { + process->exitCode = WEXITSTATUS(status); + } else { + // If we're here, *something* has caused our child process to exit abnormally + // Specific exit codes don't really map onto any of these situations - we just know it's bad. + // Specify a non-zero exit code to alert our parent process something's gone wrong. + DEBUG_LOG("WaitForSingleObject: Child process exited abnormally - returning exit code 1."); + process->exitCode = 1; + } + return 0; } @@ -498,6 +585,8 @@ namespace kernel32 { if (data.ptr != (void *) 0x1) { munmap(data.ptr, data.size); } + } else if (data.type == handles::TYPE_PROCESS) { + delete (processes::Process*) data.ptr; } return TRUE; } @@ -1895,6 +1984,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "GetCurrentProcessId") == 0) return (void *) kernel32::GetCurrentProcessId; if (strcmp(name, "GetCurrentThreadId") == 0) return (void *) kernel32::GetCurrentThreadId; if (strcmp(name, "ExitProcess") == 0) return (void *) kernel32::ExitProcess; + if (strcmp(name, "GetExitCodeProcess") == 0) return (void *) kernel32::GetExitCodeProcess; if (strcmp(name, "CreateProcessA") == 0) return (void *) kernel32::CreateProcessA; if (strcmp(name, "TlsAlloc") == 0) return (void *) kernel32::TlsAlloc; if (strcmp(name, "TlsFree") == 0) return (void *) kernel32::TlsFree; @@ -1934,6 +2024,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "AcquireSRWLockExclusive") == 0) return (void *) kernel32::AcquireSRWLockExclusive; if (strcmp(name, "ReleaseSRWLockExclusive") == 0) return (void *) kernel32::ReleaseSRWLockExclusive; if (strcmp(name, "TryAcquireSRWLockExclusive") == 0) return (void *) kernel32::TryAcquireSRWLockExclusive; + if (strcmp(name, "WaitForSingleObject") == 0) return (void *) kernel32::WaitForSingleObject; // winbase.h if (strcmp(name, "GlobalAlloc") == 0) return (void *) kernel32::GlobalAlloc; diff --git a/handles.h b/handles.h index 6d6c9f7..f751845 100644 --- a/handles.h +++ b/handles.h @@ -5,6 +5,7 @@ namespace handles { TYPE_UNUSED, TYPE_FILE, TYPE_MAPPED, + TYPE_PROCESS }; struct Data { diff --git a/main.cpp b/main.cpp index bb73a22..c461071 100644 --- a/main.cpp +++ b/main.cpp @@ -13,15 +13,22 @@ uint32_t wibo::lastError = 0; char** wibo::argv; int wibo::argc; +char *wibo::executableName; char *wibo::commandLine; wibo::Executable *wibo::mainModule = 0; bool wibo::debugEnabled = false; +unsigned int wibo::debugIndent = 0; void wibo::debug_log(const char *fmt, ...) { va_list args; va_start(args, fmt); - if (wibo::debugEnabled) + if (wibo::debugEnabled) { + for (size_t i = 0; i < wibo::debugIndent; i++) + fprintf(stderr, "\t"); + vfprintf(stderr, fmt, args); + } + va_end(args); } @@ -198,6 +205,11 @@ int main(int argc, char **argv) { wibo::debugEnabled = true; } + if (getenv("WIBO_DEBUG_INDENT")) { + wibo::debugIndent = std::stoul(getenv("WIBO_DEBUG_INDENT")); + } + + files::init(); // Create TIB @@ -264,6 +276,7 @@ int main(int argc, char **argv) { wibo::commandLine = cmdLine.data(); DEBUG_LOG("Command line: %s\n", wibo::commandLine); + wibo::executableName = argv[0]; wibo::argv = argv + 1; wibo::argc = argc - 1; diff --git a/processes.cpp b/processes.cpp new file mode 100644 index 0000000..902acaa --- /dev/null +++ b/processes.cpp @@ -0,0 +1,24 @@ +#include "processes.h" +#include "handles.h" +#include +#include + +namespace processes { + void *allocProcessHandle(pid_t pid) { + auto* process = new Process; + process->pid = pid; + process->exitCode = 0; + + return handles::allocDataHandle(handles::Data{handles::TYPE_PROCESS, (void*)process, 0}); + } + + Process* processFromHandle(void *handle, bool pop) { + handles::Data data = handles::dataFromHandle(handle, pop); + if (data.type == handles::TYPE_PROCESS) { + return (Process*)data.ptr; + } else { + printf("Invalid file handle %p\n", handle); + assert(0); + } + } +} \ No newline at end of file diff --git a/processes.h b/processes.h new file mode 100644 index 0000000..4bcad09 --- /dev/null +++ b/processes.h @@ -0,0 +1,12 @@ +#include +#include + +namespace processes { + struct Process { + pid_t pid; + uint32_t exitCode; + }; + + void *allocProcessHandle(pid_t pid); + Process* processFromHandle(void* hHandle, bool pop); +} \ No newline at end of file