From b015c0cce4801ceae4ae153af0e50a616824e237 Mon Sep 17 00:00:00 2001 From: Bottersnike Date: Thu, 30 Mar 2023 15:48:11 +0100 Subject: [PATCH] Rework launcher logic --- src/micetools/dll/dllmain.c | 2 +- src/micetools/dll/drivers/mxjvs.c | 2 +- src/micetools/dll/hooks/processes.c | 116 ++++++++++++++----------- src/micetools/dll/util/_util.h | 1 + src/micetools/dll/util/misc.c | 30 +++++-- src/micetools/launcher/main.c | 128 +++++++++++++++++++++++++--- src/micetools/lib/mice/config.def | 3 +- src/micetools/lib/mice/exe.c | 60 +++++++------ src/micetools/lib/mice/exe.h | 4 +- src/micetools/lib/mice/log.c | 55 +++++------- src/micetools/lib/mice/log.h | 2 +- 11 files changed, 268 insertions(+), 135 deletions(-) diff --git a/src/micetools/dll/dllmain.c b/src/micetools/dll/dllmain.c index ef0350e..89505c2 100644 --- a/src/micetools/dll/dllmain.c +++ b/src/micetools/dll/dllmain.c @@ -112,7 +112,7 @@ void init_injection(HMODULE hModule) { load_mice_config(); // We're in a new context now, so need to reconfigure - setup_logging(); + setup_logging(GetParentProcessId()); log_info(plfBoot, "Handover complete. Now executing within %ls", exeName); init_com_devices(); diff --git a/src/micetools/dll/drivers/mxjvs.c b/src/micetools/dll/drivers/mxjvs.c index 2a69056..520c5ad 100644 --- a/src/micetools/dll/drivers/mxjvs.c +++ b/src/micetools/dll/drivers/mxjvs.c @@ -261,7 +261,7 @@ unsigned char jvs_exchange(jvs_board_t* board, unsigned char* inData, short inCo jvs_read(gpioByteIndex); jvs_read(gpioByteData); log_warning(plfMxJvs, "GPIO%d Unhandled: [%02x]=%02x", - (cmd - JVS_CMD_WRITE_GPIO2) + 1, gpioByteIndex, gpioByteData); + (cmd - JVS_CMD_WRITE_GPIO2) + 2, gpioByteIndex, gpioByteData); break; case JVS_CMD_COIN_DECREASE: diff --git a/src/micetools/dll/hooks/processes.c b/src/micetools/dll/hooks/processes.c index c2b66ae..d0e7aa7 100644 --- a/src/micetools/dll/hooks/processes.c +++ b/src/micetools/dll/hooks/processes.c @@ -6,26 +6,35 @@ const wchar_t* HOOK_BINARIES[] = { L"app\\GmSync.exe", }; -#define DISABLE_PROC_SPAWNING - BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { + if (dwCreationFlags & CREATE_SUSPENDED) { + return TrueCreateProcessA(lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, + lpEnvironment, lpCurrentDirectory, lpStartupInfo, + lpProcessInformation); + } + log_info(plfProcesses, "CreateProcessA %s %s", lpApplicationName, lpCommandLine); - return TrueCreateProcessA("mxAuthDisc.bat", "", lpProcessAttributes, - lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, - lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + // return TrueCreateProcessA("mxAuthDisc.bat", "", lpProcessAttributes, lpThreadAttributes, + // bInheritHandles, dwCreationFlags, lpEnvironment, + // lpCurrentDirectory, lpStartupInfo, lpProcessInformation); - HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); - SetEvent(fake_evt); + // HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); + // SetEvent(fake_evt); + + HANDLE hProcess = start_and_inject(INVALID_HANDLE_VALUE, lpApplicationName, lpCommandLine, + MICELIB, FALSE, 0, "", 0); if (lpProcessInformation) { - lpProcessInformation->hProcess = fake_evt; + lpProcessInformation->hProcess = hProcess; } + return TRUE; } BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, @@ -34,61 +43,72 @@ BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { - // #ifdef DISABLE_PROC_SPAWNING - // log_error(plfProcesses, "CreateProcessW %ls %ls", lpApplicationName, lpCommandLine); - // return FALSE; - // #else log_info(plfProcesses, "CreateProcessW %ls %ls", lpApplicationName, lpCommandLine); - // log_info(plfProcesses, "CreateProcessW %ls", lpApplicationName); - - // lpProcessInformation->hThread = GetDummyHandle(); - // return TRUE; - CHAR applicationName[MAX_PATH + 1]; WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof applicationName, NULL, NULL); - HANDLE child; - CHAR commandLine[MAX_PATH + 1]; - WCHAR commandLineW[MAX_PATH + 1]; - WCHAR micePathW[MAX_PATH + 1]; - GetModuleFileNameW(NULL, micePathW, MAX_PATH); + int nMultiChars = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, NULL, 0, NULL, NULL); + LPSTR commandLine = malloc(nMultiChars); + WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, nMultiChars, NULL, NULL); - HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); - SetEvent(fake_evt); + // WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof + // applicationName, + // NULL, NULL); + // HANDLE child; + // CHAR commandLine[MAX_PATH + 1]; + // WCHAR commandLineW[MAX_PATH + 1]; + // WCHAR micePathW[MAX_PATH + 1]; + // GetModuleFileNameW(NULL, micePathW, MAX_PATH); + + // HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); + // SetEvent(fake_evt); + + // return TrueCreateProcessA(applicationName, commandLine, lpProcessAttributes, lpThreadAttributes, + // bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, + // lpStartupInfo, lpProcessInformation); + + HANDLE hProcess = start_and_inject(INVALID_HANDLE_VALUE, applicationName, commandLine, MICELIB, + TRUE, 0, NULL, 0); if (lpProcessInformation) { - lpProcessInformation->hProcess = fake_evt; + lpProcessInformation->hProcess = hProcess; lpProcessInformation->hThread = GetDummyHandle(); } + log_game(plfProcesses, "hP: %08x", hProcess); + return TRUE; - if (lpCommandLine != NULL) { - log_error(plfProcesses, "!!"); - return FALSE; - // WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, sizeof commandLine, NULL, - // NULL); - // child = start_and_inject(applicationName, commandLine, MICELIB, false, 0, NULL, - // CREATE_NEW_CONSOLE); - } else { - dwCreationFlags |= CREATE_NEW_CONSOLE; - wsprintfW(commandLineW, L"mice -b %ls", lpApplicationName); - printf("%ls %ls\n", micePathW, commandLineW); - BOOL ret = - TrueCreateProcessW(L"mice.cmd", commandLineW, lpProcessAttributes, lpThreadAttributes, - bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, - lpStartupInfo, lpProcessInformation); - printf("%d\n", ret); - return ret; - // CHAR commandLine[] - // child = - // start_and_inject(applicationName, NULL, MICELIB, false, 0, NULL, CREATE_NEW_CONSOLE); - } + // if (lpCommandLine != NULL) { + // log_error(plfProcesses, "!!"); + // return FALSE; + // // WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, sizeof commandLine, + // NULL, + // // NULL); + // // child = start_and_inject(INVALID_HANDLE_VALUE, applicationName, commandLine, MICELIB, + // // false, 0, NULL, + // // CREATE_NEW_CONSOLE); + // } else { + // dwCreationFlags |= CREATE_NEW_CONSOLE; + // wsprintfW(commandLineW, L"mice -b %ls", lpApplicationName); + // printf("%ls %ls\n", micePathW, commandLineW); + // BOOL ret = + // TrueCreateProcessW(L"mice.cmd", commandLineW, lpProcessAttributes, + // lpThreadAttributes, + // bInheritHandles, dwCreationFlags, lpEnvironment, + // lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + // printf("%d\n", ret); + // return ret; + // // CHAR commandLine[] + // // child = + // // start_and_inject(INVALID_HANDLE_VALUE, applicationName, NULL, MICELIB, false, 0, + // // NULL, CREATE_NEW_CONSOLE); + // } - return !FAILED(child); - // #endif + // return !FAILED(child); + // // #endif } BOOL WINAPI FakeGetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { diff --git a/src/micetools/dll/util/_util.h b/src/micetools/dll/util/_util.h index cba260b..b637545 100644 --- a/src/micetools/dll/util/_util.h +++ b/src/micetools/dll/util/_util.h @@ -13,6 +13,7 @@ BOOL RemoveDataForHandle(HANDLE hObject, DWORD type); HANDLE GetDummyHandle(); void BytesToHex(char* hex_buffer, BYTE* bytes, DWORD nbytes); void PrintStack(void); +DWORD GetParentProcessId(void); BOOL PathEqual(LPCSTR path1, LPCSTR path2); BOOL PathPrefix(LPCSTR path, LPCSTR prefix); diff --git a/src/micetools/dll/util/misc.c b/src/micetools/dll/util/misc.c index f97e198..f5ab26a 100644 --- a/src/micetools/dll/util/misc.c +++ b/src/micetools/dll/util/misc.c @@ -1,6 +1,7 @@ #define _WIN32_WINNT 0x0600 #include #include +#include #include "../hooks/files.h" @@ -156,15 +157,14 @@ void make_dirs(const char* path) { void* open_mapped_file(LPCWSTR path, DWORD size, HANDLE* file, HANDLE* file_mapping) { make_dirs(path); *file = _CreateFileW(path, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); if (*file == INVALID_HANDLE_VALUE) { log_error(plfMisc, "Failed to CreateFileW(%ls): %d", path, GetLastError()); return NULL; } - *file_mapping = - CreateFileMappingW(*file, NULL, PAGE_READWRITE, 0, size, NULL); + *file_mapping = CreateFileMappingW(*file, NULL, PAGE_READWRITE, 0, size, NULL); if (*file_mapping == INVALID_HANDLE_VALUE) { CloseHandle(*file); @@ -179,10 +179,30 @@ void* open_mapped_file(LPCWSTR path, DWORD size, HANDLE* file, HANDLE* file_mapp if (mapping == NULL || GetLastError()) { log_error(plfMisc, "Failed to MapViewOfFileEx: %d", GetLastError()); CloseHandle(*file); - CloseHandle(*file_mapping); + CloseHandle(*file_mapping); *file = INVALID_HANDLE_VALUE; *file_mapping = INVALID_HANDLE_VALUE; return NULL; } return mapping; } + +DWORD GetParentProcessIdFor(DWORD pid) { + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if (Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + CloseHandle(h); + return pe.th32ParentProcessID; + } + } while (Process32Next(h, &pe)); + } + CloseHandle(h); + return (DWORD)-1; +} +DWORD GetParentProcessId(void) { + return GetParentProcessIdFor(GetCurrentProcessId()); +} diff --git a/src/micetools/launcher/main.c b/src/micetools/launcher/main.c index ed19b15..f7008c8 100644 --- a/src/micetools/launcher/main.c +++ b/src/micetools/launcher/main.c @@ -10,7 +10,7 @@ int boot_delay = 0; char exe_name[MAX_PATH + 1] = ""; char commandline[MAX_PATH + 1] = ""; -void print_help(char* exe) { +static void print_help(char* exe) { log_info(plfBoot, "Usage: %s [-h] [-t] [-b executable.exe] [-d]", exe); log_info(plfBoot, " -h: Print this help message and exit"); log_info(plfBoot, " -b: Specify the game binary to use"); @@ -19,7 +19,7 @@ void print_help(char* exe) { exit(0); } -bool parse_cmdline(int argc, char* argv[]) { +static bool parse_cmdline(int argc, char* argv[]) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-h") == 0) { print_help(argv[0]); @@ -77,9 +77,102 @@ bool parse_cmdline(int argc, char* argv[]) { return true; } +static DWORD WINAPI MiceMailslotWatcher(HANDLE* pSlot) { + // Enable colour support in conhost + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + if (GetConsoleMode(hConsole, &dwMode)) + SetConsoleMode(hConsole, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + HANDLE hLogFile = INVALID_HANDLE_VALUE; + if (MiceConfig.mice.log_to_file) { + hLogFile = CreateFileA(MiceConfig.mice.log_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, 0, NULL); + if (hLogFile == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed to initialise logging: open %s failed: %d", + MiceConfig.mice.log_file, GetLastError()); + ExitProcess(2); + } + } + + OVERLAPPED ov; + HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + ov.Offset = ov.OffsetHigh = 0; + ov.hEvent = hEvent; + + char readBuffer[4096]; + DWORD nRead, nBytes; + while (1) { + nRead = nBytes = 0; + ReadFile(*pSlot, readBuffer, sizeof readBuffer, &nRead, &ov); + GetOverlappedResult(*pSlot, &ov, &nBytes, TRUE); + DWORD nWrote; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), readBuffer, nBytes, &nWrote, NULL); + + if (hLogFile != INVALID_HANDLE_VALUE) { + // The only VT100 sequences being used are colours, so this lazy approach works + BOOL inVt100 = FALSE; + for (int i = 0; i < nBytes; i++) { + if (readBuffer[i] == '\033') { + inVt100 = TRUE; + continue; + } + if (inVt100 && readBuffer[i] == 'm') { + inVt100 = FALSE; + continue; + } + if (!inVt100) WriteFile(hLogFile, &(readBuffer[i]), 1, &nWrote, NULL); + } + FlushFileBuffers(hLogFile); + } + } +} + +static BOOL setup_mailslot(void) { + DWORD pid = GetCurrentProcessId(); + char slotName[MAX_PATH + 1]; + sprintf_s(slotName, MAX_PATH, "\\\\.\\mailslot\\micelog%d", pid); + + HANDLE* pSlot = malloc(sizeof(HANDLE)); + if (pSlot == NULL) { + fprintf(stderr, "malloc(pSlot) failed\n"); + return FALSE; + } + *pSlot = CreateMailslotA(slotName, 0, MAILSLOT_WAIT_FOREVER, NULL); + if (*pSlot == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Creation of mailslot failed %d\n", GetLastError()); + return FALSE; + } + + CreateThread(NULL, 0, MiceMailslotWatcher, pSlot, 0, NULL); + + return TRUE; +} + +/** + * Because logging is via a mailslot, we need to give it a chance to flush messages before + * terminating. While we could engineer something overly fancy, the only situation this ever occurs + * is in the main() function just below. + * + * I'll let the code speak for itself. + */ +static int terminate(int err) { + Sleep(100); + + return err; +} + +HANDLE hGameProc = INVALID_HANDLE_VALUE; +HANDLE hJob = INVALID_HANDLE_VALUE; +BOOL WINAPI MiceHandlerRoutine(DWORD CtrlType) { + if (hJob != INVALID_HANDLE_VALUE) CloseHandle(hJob); + // if (hGameProc != INVALID_HANDLE_VALUE) TerminateProcess(hGameProc, 0); +} + int main(int argc, char* argv[]) { load_mice_config(); - setup_logging(); + if (!setup_mailslot()) return 1; + setup_logging(GetCurrentProcessId()); log_info(plfBoot, "Micetools version: %s", MICE_VERSION); @@ -87,7 +180,7 @@ int main(int argc, char* argv[]) { GetCurrentDirectory(MAX_PATH, workDir); log_info(plfBoot, "Current directory: %s", workDir); - if (!parse_cmdline(argc, argv)) return 0; + if (!parse_cmdline(argc, argv)) return terminate(0); if (exe_name[0] == '\0' && MiceConfig.launcher.game_binary[0] != '\0') { snprintf(exe_name, sizeof exe_name, "%s", MiceConfig.launcher.game_binary); @@ -98,13 +191,13 @@ int main(int argc, char* argv[]) { if (exe_name[0] == '\0') { if (!locate_game(exe_name, MAX_PATH + 1)) { log_error(plfBoot, "Fatal: Failed to locate a game"); - return 0; + return terminate(0); } } else { DWORD dwAttrib = GetFileAttributes(exe_name); if (dwAttrib == INVALID_FILE_ATTRIBUTES || dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { log_error(plfBoot, "Fatal: %s: no such file found", exe_name); - return 0; + return terminate(0); } } @@ -115,19 +208,26 @@ int main(int argc, char* argv[]) { char micepath[MAX_PATH + 1]; if (!locate_library(micepath, MAX_PATH + 1)) { log_error(plfBoot, "Fatal: Failed to locate micelib. Check your mice_dll setting!"); - return 0; + return terminate(0); } - char* extra_injections = MiceConfig.launcher.inject; - HANDLE game_proc = - start_and_inject(exe_name, commandline, micepath, debug_wait, boot_delay, extra_injections, 0); - if (!game_proc) return -1; + hJob = CreateJobObject(NULL, NULL); + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &info, sizeof info); - if (FAILED(WaitForSingleObject(game_proc, INFINITE))) { + char* extra_injections = MiceConfig.launcher.inject; + hGameProc = start_and_inject(hJob, exe_name, commandline, micepath, debug_wait, boot_delay, + extra_injections, 0); + if (!hGameProc) return terminate(-1); + + SetConsoleCtrlHandler(MiceHandlerRoutine, TRUE); + + if (FAILED(WaitForSingleObject(hGameProc, INFINITE))) { log_error(plfBoot, "Fatal: WaitForSingleObject failed: %03x", GetLastError()); } else { log_info(plfBoot, "Shutting down"); - CloseHandle(game_proc); + CloseHandle(hGameProc); } - return 0; + return terminate(0); } diff --git a/src/micetools/lib/mice/config.def b/src/micetools/lib/mice/config.def index 28cb874..d988c3e 100644 --- a/src/micetools/lib/mice/config.def +++ b/src/micetools/lib/mice/config.def @@ -14,8 +14,7 @@ COMMENT("") SECTION(mice, "General mice settings") COMMENT("Trace logging (6) is intensive, and should only be used when debugging") CFG_int(mice, log_level, 4, "1 = Game\n2 = Error\n3 = Warning\n4 = Info\n5 = Misc\n6 = Trace") -CFG_bool(mice, log_to_file, false, "Also log out to log_file") -CFG_int(mice, file_log_level, 5, "1 = Game\n2 = Error\n3 = Warning\n4 = Info\n5 = Misc\n6 = Trace") +CFG_bool(mice, log_to_file, true, "Also log out to log_file") CFG_str(mice, log_file, "log.txt", "The file to log to if log_to_file is enabled") CFG_bool(mice, apply_patches, true, "Load and apply patches from patches_file at runtime") CFG_str(mice, patches_file, "patches.index", "The file to read patches from") diff --git a/src/micetools/lib/mice/exe.c b/src/micetools/lib/mice/exe.c index 7f1da50..2ee7f52 100644 --- a/src/micetools/lib/mice/exe.c +++ b/src/micetools/lib/mice/exe.c @@ -2,18 +2,20 @@ #include +#include "log.h" + bool inject_debug_wait(HANDLE process) { BOOL present; - fprintf(stderr, "Waiting for debugger to attach.\n"); + log_info(plfBoot, "Waiting for debugger to attach."); do { Sleep(1000); if (FAILED(CheckRemoteDebuggerPresent(process, &present))) { - fprintf(stderr, "Fatal: CheckRemoteDebuggerPresent failed: %03x\n", GetLastError()); + log_error(plfBoot, "Fatal: CheckRemoteDebuggerPresent failed: %d", GetLastError()); return false; } } while (!present); - fprintf(stderr, "Debugger attached, resuming\n"); + log_info(plfBoot, "Debugger attached, resuming"); return true; } bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result) { @@ -22,33 +24,33 @@ bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result LPVOID arg_addr = VirtualAllocEx(process, NULL, nchars + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (arg_addr == NULL) { - fprintf(stderr, "Fatal: VirtualAllocEx failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: VirtualAllocEx failed: %d", GetLastError()); return false; } if (FAILED(WriteProcessMemory(process, arg_addr, argument, nchars + 1, NULL))) { - fprintf(stderr, "Fatal: WriteProcessMemory failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: WriteProcessMemory failed: %d", GetLastError()); return false; } HANDLE remote_thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)function, arg_addr, 0, NULL); if (remote_thread == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Fatal: CreateRemoteThread failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: CreateRemoteThread failed: %d", GetLastError()); return false; } if (WaitForSingleObject(remote_thread, INFINITE) != WAIT_OBJECT_0) { - fprintf(stderr, "Fatal: WaitForSingleObject failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: WaitForSingleObject failed: %d", GetLastError()); return false; } if (FAILED(GetExitCodeThread(remote_thread, result))) { - fprintf(stderr, "Fatal: GetExitCodeThread failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: GetExitCodeThread failed: %d", GetLastError()); return false; } if (*result == 0) { - fprintf(stderr, "Fatal: GetExitCodeThread failed: result == 0\n"); + log_error(plfProcesses, "Fatal: GetExitCodeThread failed: result == 0"); return false; } @@ -57,13 +59,13 @@ bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result bool inject_dll(HANDLE process, LPCSTR inject) { HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); if (kernel32 == NULL) { - fprintf(stderr, "Fatal: GetModuleHandleA failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: GetModuleHandleA failed: %d", GetLastError()); return false; } LPVOID addr_LoadLibraryA = (LPVOID)GetProcAddress(kernel32, "LoadLibraryA"); if (addr_LoadLibraryA == NULL) { - fprintf(stderr, "Fatal: GetProcAddress failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: GetProcAddress failed: %d", GetLastError()); return false; } @@ -71,8 +73,8 @@ bool inject_dll(HANDLE process, LPCSTR inject) { return remote_call(process, addr_LoadLibraryA, inject, &result); } -HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, DWORD delay, - LPCSTR extra_injections, DWORD flags) { +HANDLE start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, + DWORD delay, LPCSTR extra_injections, DWORD flags) { STARTUPINFOA startupInfo; PROCESS_INFORMATION processInformation = { 0 }; @@ -87,27 +89,33 @@ HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wa // Does the exe we're starting exist? found = SearchPathA(NULL, path, NULL, 0, NULL, NULL); if (found == 0) { - fprintf(stderr, "Fatal: Cannot start %s: not found\n", path); + log_error(plfProcesses, "Fatal: Cannot start %s: not found", path); goto abort; } - // Does the DLL we want to inject exist? - found = SearchPathA(NULL, inject, NULL, 0, NULL, NULL); - if (found == 0) { - fprintf(stderr, "Fatal: Cannot inject %s: not found\n", inject); - goto abort; + if (inject != NULL) { + // Does the DLL we want to inject exist? + found = SearchPathA(NULL, inject, NULL, 0, NULL, NULL); + if (found == 0) { + log_error(plfProcesses, "Fatal: Cannot inject %s: not found", inject); + goto abort; + } } // Start the binary flags |= CREATE_SUSPENDED; if (!CreateProcessA(path, cmdline, NULL, NULL, FALSE, flags, NULL, NULL, &startupInfo, &processInformation)) { - fprintf(stderr, "Fatal: CreateProcessA failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: CreateProcessA(%s, %s) failed: %d", path, cmdline, + GetLastError()); goto abort; } + if (hJob != INVALID_HANDLE_VALUE) AssignProcessToJobObject(hJob, processInformation.hProcess); + if (debug_wait) if (!inject_debug_wait(processInformation.hProcess)) goto abort; - if (!inject_dll(processInformation.hProcess, inject)) goto abort; + if (inject != NULL) + if (!inject_dll(processInformation.hProcess, inject)) goto abort; if (extra_injections != NULL && extra_injections[0] != '\0') { char* copy = (char*)malloc(strlen(extra_injections) + 1); @@ -125,13 +133,13 @@ HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wa } if (delay) { - fprintf(stderr, "Delaying for %dms\n", delay); + log_info(plfBoot, "Delaying for %dms", delay); Sleep(delay); } // Injection completed, let the program continue execution if (FAILED(ResumeThread(processInformation.hThread))) { - fprintf(stderr, "Fatal: ResumeThread failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: ResumeThread failed: %d", GetLastError()); goto abort; } @@ -140,14 +148,14 @@ HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wa abort: if (processInformation.hProcess && processInformation.hProcess != INVALID_HANDLE_VALUE) { if (!CloseHandle(processInformation.hThread) && GetLastError() != ERROR_INVALID_HANDLE) - fprintf(stderr, "Fatal: CloseHandle(hProcess) failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: CloseHandle(hProcess) failed: %d", GetLastError()); if (!TerminateProcess(processInformation.hProcess, 1)) - fprintf(stderr, "Fatal: TerminateProcess failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: TerminateProcess failed: %d", GetLastError()); } if (processInformation.hThread && processInformation.hThread != INVALID_HANDLE_VALUE) { if (!CloseHandle(processInformation.hThread) && GetLastError() != ERROR_INVALID_HANDLE) - fprintf(stderr, "Fatal: CloseHandle(hThread) failed: %03x\n", GetLastError()); + log_error(plfProcesses, "Fatal: CloseHandle(hThread) failed: %d", GetLastError()); } return INVALID_HANDLE_VALUE; diff --git a/src/micetools/lib/mice/exe.h b/src/micetools/lib/mice/exe.h index c76f066..7b8ef19 100644 --- a/src/micetools/lib/mice/exe.h +++ b/src/micetools/lib/mice/exe.h @@ -2,7 +2,7 @@ #include #include -HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, DWORD delay, - LPCSTR extra_injections, DWORD flags); +HANDLE start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, + DWORD delay, LPCSTR extra_injections, DWORD flags); #define MICELIB "mice.dll" diff --git a/src/micetools/lib/mice/log.c b/src/micetools/lib/mice/log.c index 6efb953..9a7f47d 100644 --- a/src/micetools/lib/mice/log.c +++ b/src/micetools/lib/mice/log.c @@ -17,14 +17,14 @@ #include "log_facilities.def" #undef _LF +static BOOL logIsMaster = FALSE; + extern WCHAR exeName[MAX_PATH + 1]; extern DWORD imageOffset; extern BOOL(WINAPI* TrueWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); -BOOL HAS_COLOUR = FALSE; - char _log_prelude[64]; char* log_prelude() { time_t rawtime; @@ -65,13 +65,12 @@ void __stdcall amLogCallback(DWORD level, char* format) { DWORD pLogcb; DWORD* ppLogcb; +HANDLE hSlot; + static char log_buf[1024]; int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list args) { // Early-return if we have nothing to do - if (MiceConfig.mice.log_level < log_level && - (!MiceConfig.mice.log_to_file || MiceConfig.mice.file_log_level < log_level)) { - return 0; - } + if (MiceConfig.mice.log_level < log_level) return 0; // TODO: These are all horrible bodges // if (wcscmp(exeName, L"mxnetwork.exe") == 0) { @@ -88,30 +87,21 @@ int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list char prefix = LOG_PREFIXES[log_level]; - EnterCriticalSection(&logger_lock); int col_len = strlen(log_colours[log_level]); + EnterCriticalSection(&logger_lock); int log_len = snprintf(log_buf, _countof(log_buf), "%s%s%c:%s:", log_colours[log_level], log_prelude(), prefix, facility->m_name); log_len += vsnprintf(log_buf + log_len, _countof(log_buf) - log_len, format, args); log_len += snprintf(log_buf + log_len, _countof(log_buf) - log_len, "%s\n", COLOR_RESET); log_buf[_countof(log_buf) - 1] = '\0'; - if (MiceConfig.mice.log_level >= log_level) { - HANDLE sout = GetStdHandle(STD_OUTPUT_HANDLE); - WriteFile(sout, log_buf, log_len, NULL, NULL); - // FlushFileBuffers(sout); - } - - if (MiceConfig.mice.log_to_file && MiceConfig.mice.file_log_level >= log_level) { - if (log_file && log_file != INVALID_HANDLE_VALUE) { - // Replace the colour reset with a newline, then skip the prefix when writing - log_buf[log_len - col_len] = '\n'; - log_buf[log_len - col_len + 1] = '\0'; - WriteFile(log_file, log_buf + col_len, log_len - col_len - sizeof(COLOR_RESET), NULL, - NULL); - } + if (hSlot != INVALID_HANDLE_VALUE) { + WriteFile(hSlot, log_buf, log_len, NULL, NULL); + } else { + // This should never happen, but there's no harm being prepared in case it does + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), log_buf, log_len, NULL, NULL); } LeaveCriticalSection(&logger_lock); @@ -178,23 +168,18 @@ int _log_game(PLOG_FACILITY facility, const char* format, ...) { return ret; } -void setup_logging() { - // Force stdio even for GUI applications - // AttachConsole(ATTACH_PARENT_PROCESS); +void setup_logging(DWORD slotPid) { + char slotName[MAX_PATH + 1]; + sprintf_s(slotName, MAX_PATH, "\\\\.\\mailslot\\micelog%d", slotPid); - // Enable colour in CMD - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - if (GetConsoleMode(hConsole, &dwMode)) - HAS_COLOUR = SetConsoleMode(hConsole, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + hSlot = CreateFile(slotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hSlot == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Fatal: Failed to open mailslot %s", slotName); + ExitProcess(2); + } InitializeCriticalSection(&logger_lock); - - if (MiceConfig.mice.log_to_file) { - if (log_file == INVALID_HANDLE_VALUE && MiceConfig.mice.log_file[0] != '\0') - log_file = CreateFileA(MiceConfig.mice.log_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, - CREATE_ALWAYS, 0, NULL); - } } void log_stack(PLOG_FACILITY facility) { diff --git a/src/micetools/lib/mice/log.h b/src/micetools/lib/mice/log.h index 375d79b..59a90b5 100644 --- a/src/micetools/lib/mice/log.h +++ b/src/micetools/lib/mice/log.h @@ -38,7 +38,7 @@ int vlog_game(PLOG_FACILITY facility, const char* format, va_list args); void log_stack(PLOG_FACILITY facility); -void setup_logging(); +void setup_logging(DWORD slotPid); // Disable some logging entirely at build time for speed #define COMPILE_LOG_LEVEL 6