1
0
mirror of synced 2024-12-18 07:15:53 +01:00

Rework launcher logic

This commit is contained in:
Bottersnike 2023-03-30 15:48:11 +01:00
parent 6c94aad9b2
commit b015c0cce4
No known key found for this signature in database
11 changed files with 268 additions and 135 deletions

View File

@ -112,7 +112,7 @@ void init_injection(HMODULE hModule) {
load_mice_config(); load_mice_config();
// We're in a new context now, so need to reconfigure // 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); log_info(plfBoot, "Handover complete. Now executing within %ls", exeName);
init_com_devices(); init_com_devices();

View File

@ -261,7 +261,7 @@ unsigned char jvs_exchange(jvs_board_t* board, unsigned char* inData, short inCo
jvs_read(gpioByteIndex); jvs_read(gpioByteIndex);
jvs_read(gpioByteData); jvs_read(gpioByteData);
log_warning(plfMxJvs, "GPIO%d Unhandled: [%02x]=%02x", 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; break;
case JVS_CMD_COIN_DECREASE: case JVS_CMD_COIN_DECREASE:

View File

@ -6,26 +6,35 @@ const wchar_t* HOOK_BINARIES[] = {
L"app\\GmSync.exe", L"app\\GmSync.exe",
}; };
#define DISABLE_PROC_SPAWNING
BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPCSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation) { 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); log_info(plfProcesses, "CreateProcessA %s %s", lpApplicationName, lpCommandLine);
return TrueCreateProcessA("mxAuthDisc.bat", "", lpProcessAttributes, // return TrueCreateProcessA("mxAuthDisc.bat", "", lpProcessAttributes, lpThreadAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, // bInheritHandles, dwCreationFlags, lpEnvironment,
lpCurrentDirectory, lpStartupInfo, lpProcessInformation); // lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); // HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL);
SetEvent(fake_evt); // SetEvent(fake_evt);
HANDLE hProcess = start_and_inject(INVALID_HANDLE_VALUE, lpApplicationName, lpCommandLine,
MICELIB, FALSE, 0, "", 0);
if (lpProcessInformation) { if (lpProcessInformation) {
lpProcessInformation->hProcess = fake_evt; lpProcessInformation->hProcess = hProcess;
} }
return TRUE; return TRUE;
} }
BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
@ -34,61 +43,72 @@ BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
DWORD dwCreationFlags, LPVOID lpEnvironment, DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation) { 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 %ls", lpApplicationName, lpCommandLine);
// log_info(plfProcesses, "CreateProcessW %ls", lpApplicationName);
// lpProcessInformation->hThread = GetDummyHandle();
// return TRUE;
CHAR applicationName[MAX_PATH + 1]; CHAR applicationName[MAX_PATH + 1];
WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof applicationName, WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof applicationName,
NULL, NULL); NULL, NULL);
HANDLE child; int nMultiChars = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, NULL, 0, NULL, NULL);
CHAR commandLine[MAX_PATH + 1]; LPSTR commandLine = malloc(nMultiChars);
WCHAR commandLineW[MAX_PATH + 1]; WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, nMultiChars, NULL, NULL);
WCHAR micePathW[MAX_PATH + 1];
GetModuleFileNameW(NULL, micePathW, MAX_PATH);
HANDLE fake_evt = CreateEvent(NULL, TRUE, FALSE, NULL); // WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof
SetEvent(fake_evt); // 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) { if (lpProcessInformation) {
lpProcessInformation->hProcess = fake_evt; lpProcessInformation->hProcess = hProcess;
lpProcessInformation->hThread = GetDummyHandle(); lpProcessInformation->hThread = GetDummyHandle();
} }
log_game(plfProcesses, "hP: %08x", hProcess);
return TRUE; return TRUE;
if (lpCommandLine != NULL) { // if (lpCommandLine != NULL) {
log_error(plfProcesses, "!!"); // log_error(plfProcesses, "!!");
return FALSE; // return FALSE;
// WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, sizeof commandLine, NULL, // // WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, sizeof commandLine,
// NULL); // NULL,
// child = start_and_inject(applicationName, commandLine, MICELIB, false, 0, NULL, // // NULL);
// CREATE_NEW_CONSOLE); // // child = start_and_inject(INVALID_HANDLE_VALUE, applicationName, commandLine, MICELIB,
} else { // // false, 0, NULL,
dwCreationFlags |= CREATE_NEW_CONSOLE; // // CREATE_NEW_CONSOLE);
wsprintfW(commandLineW, L"mice -b %ls", lpApplicationName); // } else {
printf("%ls %ls\n", micePathW, commandLineW); // dwCreationFlags |= CREATE_NEW_CONSOLE;
BOOL ret = // wsprintfW(commandLineW, L"mice -b %ls", lpApplicationName);
TrueCreateProcessW(L"mice.cmd", commandLineW, lpProcessAttributes, lpThreadAttributes, // printf("%ls %ls\n", micePathW, commandLineW);
bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, // BOOL ret =
lpStartupInfo, lpProcessInformation); // TrueCreateProcessW(L"mice.cmd", commandLineW, lpProcessAttributes,
printf("%d\n", ret); // lpThreadAttributes,
return ret; // bInheritHandles, dwCreationFlags, lpEnvironment,
// CHAR commandLine[] // lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
// child = // printf("%d\n", ret);
// start_and_inject(applicationName, NULL, MICELIB, false, 0, NULL, CREATE_NEW_CONSOLE); // return ret;
} // // CHAR commandLine[]
// // child =
// // start_and_inject(INVALID_HANDLE_VALUE, applicationName, NULL, MICELIB, false, 0,
// // NULL, CREATE_NEW_CONSOLE);
// }
return !FAILED(child); // return !FAILED(child);
// #endif // // #endif
} }
BOOL WINAPI FakeGetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { BOOL WINAPI FakeGetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) {

View File

@ -13,6 +13,7 @@ BOOL RemoveDataForHandle(HANDLE hObject, DWORD type);
HANDLE GetDummyHandle(); HANDLE GetDummyHandle();
void BytesToHex(char* hex_buffer, BYTE* bytes, DWORD nbytes); void BytesToHex(char* hex_buffer, BYTE* bytes, DWORD nbytes);
void PrintStack(void); void PrintStack(void);
DWORD GetParentProcessId(void);
BOOL PathEqual(LPCSTR path1, LPCSTR path2); BOOL PathEqual(LPCSTR path1, LPCSTR path2);
BOOL PathPrefix(LPCSTR path, LPCSTR prefix); BOOL PathPrefix(LPCSTR path, LPCSTR prefix);

View File

@ -1,6 +1,7 @@
#define _WIN32_WINNT 0x0600 #define _WIN32_WINNT 0x0600
#include <Windows.h> #include <Windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include <tlhelp32.h>
#include "../hooks/files.h" #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) { void* open_mapped_file(LPCWSTR path, DWORD size, HANDLE* file, HANDLE* file_mapping) {
make_dirs(path); make_dirs(path);
*file = _CreateFileW(path, GENERIC_READ | GENERIC_WRITE, *file = _CreateFileW(path, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); FILE_ATTRIBUTE_NORMAL, NULL);
if (*file == INVALID_HANDLE_VALUE) { if (*file == INVALID_HANDLE_VALUE) {
log_error(plfMisc, "Failed to CreateFileW(%ls): %d", path, GetLastError()); log_error(plfMisc, "Failed to CreateFileW(%ls): %d", path, GetLastError());
return NULL; return NULL;
} }
*file_mapping = *file_mapping = CreateFileMappingW(*file, NULL, PAGE_READWRITE, 0, size, NULL);
CreateFileMappingW(*file, NULL, PAGE_READWRITE, 0, size, NULL);
if (*file_mapping == INVALID_HANDLE_VALUE) { if (*file_mapping == INVALID_HANDLE_VALUE) {
CloseHandle(*file); CloseHandle(*file);
@ -179,10 +179,30 @@ void* open_mapped_file(LPCWSTR path, DWORD size, HANDLE* file, HANDLE* file_mapp
if (mapping == NULL || GetLastError()) { if (mapping == NULL || GetLastError()) {
log_error(plfMisc, "Failed to MapViewOfFileEx: %d", GetLastError()); log_error(plfMisc, "Failed to MapViewOfFileEx: %d", GetLastError());
CloseHandle(*file); CloseHandle(*file);
CloseHandle(*file_mapping); CloseHandle(*file_mapping);
*file = INVALID_HANDLE_VALUE; *file = INVALID_HANDLE_VALUE;
*file_mapping = INVALID_HANDLE_VALUE; *file_mapping = INVALID_HANDLE_VALUE;
return NULL; return NULL;
} }
return mapping; 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());
}

View File

@ -10,7 +10,7 @@ int boot_delay = 0;
char exe_name[MAX_PATH + 1] = ""; char exe_name[MAX_PATH + 1] = "";
char commandline[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, "Usage: %s [-h] [-t] [-b executable.exe] [-d]", exe);
log_info(plfBoot, " -h: Print this help message and exit"); log_info(plfBoot, " -h: Print this help message and exit");
log_info(plfBoot, " -b: Specify the game binary to use"); log_info(plfBoot, " -b: Specify the game binary to use");
@ -19,7 +19,7 @@ void print_help(char* exe) {
exit(0); exit(0);
} }
bool parse_cmdline(int argc, char* argv[]) { static bool parse_cmdline(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0) { if (strcmp(argv[i], "-h") == 0) {
print_help(argv[0]); print_help(argv[0]);
@ -77,9 +77,102 @@ bool parse_cmdline(int argc, char* argv[]) {
return true; 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[]) { int main(int argc, char* argv[]) {
load_mice_config(); load_mice_config();
setup_logging(); if (!setup_mailslot()) return 1;
setup_logging(GetCurrentProcessId());
log_info(plfBoot, "Micetools version: %s", MICE_VERSION); log_info(plfBoot, "Micetools version: %s", MICE_VERSION);
@ -87,7 +180,7 @@ int main(int argc, char* argv[]) {
GetCurrentDirectory(MAX_PATH, workDir); GetCurrentDirectory(MAX_PATH, workDir);
log_info(plfBoot, "Current directory: %s", 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') { if (exe_name[0] == '\0' && MiceConfig.launcher.game_binary[0] != '\0') {
snprintf(exe_name, sizeof exe_name, "%s", MiceConfig.launcher.game_binary); 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 (exe_name[0] == '\0') {
if (!locate_game(exe_name, MAX_PATH + 1)) { if (!locate_game(exe_name, MAX_PATH + 1)) {
log_error(plfBoot, "Fatal: Failed to locate a game"); log_error(plfBoot, "Fatal: Failed to locate a game");
return 0; return terminate(0);
} }
} else { } else {
DWORD dwAttrib = GetFileAttributes(exe_name); DWORD dwAttrib = GetFileAttributes(exe_name);
if (dwAttrib == INVALID_FILE_ATTRIBUTES || dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { if (dwAttrib == INVALID_FILE_ATTRIBUTES || dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
log_error(plfBoot, "Fatal: %s: no such file found", exe_name); 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]; char micepath[MAX_PATH + 1];
if (!locate_library(micepath, MAX_PATH + 1)) { if (!locate_library(micepath, MAX_PATH + 1)) {
log_error(plfBoot, "Fatal: Failed to locate micelib. Check your mice_dll setting!"); log_error(plfBoot, "Fatal: Failed to locate micelib. Check your mice_dll setting!");
return 0; return terminate(0);
} }
char* extra_injections = MiceConfig.launcher.inject; hJob = CreateJobObject(NULL, NULL);
HANDLE game_proc = JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
start_and_inject(exe_name, commandline, micepath, debug_wait, boot_delay, extra_injections, 0); info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!game_proc) return -1; 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()); log_error(plfBoot, "Fatal: WaitForSingleObject failed: %03x", GetLastError());
} else { } else {
log_info(plfBoot, "Shutting down"); log_info(plfBoot, "Shutting down");
CloseHandle(game_proc); CloseHandle(hGameProc);
} }
return 0; return terminate(0);
} }

View File

@ -14,8 +14,7 @@ COMMENT("")
SECTION(mice, "General mice settings") SECTION(mice, "General mice settings")
COMMENT("Trace logging (6) is intensive, and should only be used when debugging") 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_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_bool(mice, log_to_file, true, "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_str(mice, log_file, "log.txt", "The file to log to if log_to_file is enabled") 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_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") CFG_str(mice, patches_file, "patches.index", "The file to read patches from")

View File

@ -2,18 +2,20 @@
#include <stdlib.h> #include <stdlib.h>
#include "log.h"
bool inject_debug_wait(HANDLE process) { bool inject_debug_wait(HANDLE process) {
BOOL present; BOOL present;
fprintf(stderr, "Waiting for debugger to attach.\n"); log_info(plfBoot, "Waiting for debugger to attach.");
do { do {
Sleep(1000); Sleep(1000);
if (FAILED(CheckRemoteDebuggerPresent(process, &present))) { if (FAILED(CheckRemoteDebuggerPresent(process, &present))) {
fprintf(stderr, "Fatal: CheckRemoteDebuggerPresent failed: %03x\n", GetLastError()); log_error(plfBoot, "Fatal: CheckRemoteDebuggerPresent failed: %d", GetLastError());
return false; return false;
} }
} while (!present); } while (!present);
fprintf(stderr, "Debugger attached, resuming\n"); log_info(plfBoot, "Debugger attached, resuming");
return true; return true;
} }
bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result) { 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 = LPVOID arg_addr =
VirtualAllocEx(process, NULL, nchars + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); VirtualAllocEx(process, NULL, nchars + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (arg_addr == NULL) { if (arg_addr == NULL) {
fprintf(stderr, "Fatal: VirtualAllocEx failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: VirtualAllocEx failed: %d", GetLastError());
return false; return false;
} }
if (FAILED(WriteProcessMemory(process, arg_addr, argument, nchars + 1, NULL))) { 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; return false;
} }
HANDLE remote_thread = HANDLE remote_thread =
CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)function, arg_addr, 0, NULL); CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)function, arg_addr, 0, NULL);
if (remote_thread == INVALID_HANDLE_VALUE) { if (remote_thread == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Fatal: CreateRemoteThread failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: CreateRemoteThread failed: %d", GetLastError());
return false; return false;
} }
if (WaitForSingleObject(remote_thread, INFINITE) != WAIT_OBJECT_0) { 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; return false;
} }
if (FAILED(GetExitCodeThread(remote_thread, result))) { if (FAILED(GetExitCodeThread(remote_thread, result))) {
fprintf(stderr, "Fatal: GetExitCodeThread failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: GetExitCodeThread failed: %d", GetLastError());
return false; return false;
} }
if (*result == 0) { if (*result == 0) {
fprintf(stderr, "Fatal: GetExitCodeThread failed: result == 0\n"); log_error(plfProcesses, "Fatal: GetExitCodeThread failed: result == 0");
return false; return false;
} }
@ -57,13 +59,13 @@ bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result
bool inject_dll(HANDLE process, LPCSTR inject) { bool inject_dll(HANDLE process, LPCSTR inject) {
HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
if (kernel32 == NULL) { if (kernel32 == NULL) {
fprintf(stderr, "Fatal: GetModuleHandleA failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: GetModuleHandleA failed: %d", GetLastError());
return false; return false;
} }
LPVOID addr_LoadLibraryA = (LPVOID)GetProcAddress(kernel32, "LoadLibraryA"); LPVOID addr_LoadLibraryA = (LPVOID)GetProcAddress(kernel32, "LoadLibraryA");
if (addr_LoadLibraryA == NULL) { if (addr_LoadLibraryA == NULL) {
fprintf(stderr, "Fatal: GetProcAddress failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: GetProcAddress failed: %d", GetLastError());
return false; return false;
} }
@ -71,8 +73,8 @@ bool inject_dll(HANDLE process, LPCSTR inject) {
return remote_call(process, addr_LoadLibraryA, inject, &result); return remote_call(process, addr_LoadLibraryA, inject, &result);
} }
HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, DWORD delay, HANDLE start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait,
LPCSTR extra_injections, DWORD flags) { DWORD delay, LPCSTR extra_injections, DWORD flags) {
STARTUPINFOA startupInfo; STARTUPINFOA startupInfo;
PROCESS_INFORMATION processInformation = { 0 }; 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? // Does the exe we're starting exist?
found = SearchPathA(NULL, path, NULL, 0, NULL, NULL); found = SearchPathA(NULL, path, NULL, 0, NULL, NULL);
if (found == 0) { 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; goto abort;
} }
// Does the DLL we want to inject exist? if (inject != NULL) {
found = SearchPathA(NULL, inject, NULL, 0, NULL, NULL); // Does the DLL we want to inject exist?
if (found == 0) { found = SearchPathA(NULL, inject, NULL, 0, NULL, NULL);
fprintf(stderr, "Fatal: Cannot inject %s: not found\n", inject); if (found == 0) {
goto abort; log_error(plfProcesses, "Fatal: Cannot inject %s: not found", inject);
goto abort;
}
} }
// Start the binary // Start the binary
flags |= CREATE_SUSPENDED; flags |= CREATE_SUSPENDED;
if (!CreateProcessA(path, cmdline, NULL, NULL, FALSE, flags, NULL, NULL, &startupInfo, if (!CreateProcessA(path, cmdline, NULL, NULL, FALSE, flags, NULL, NULL, &startupInfo,
&processInformation)) { &processInformation)) {
fprintf(stderr, "Fatal: CreateProcessA failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: CreateProcessA(%s, %s) failed: %d", path, cmdline,
GetLastError());
goto abort; goto abort;
} }
if (hJob != INVALID_HANDLE_VALUE) AssignProcessToJobObject(hJob, processInformation.hProcess);
if (debug_wait) if (debug_wait)
if (!inject_debug_wait(processInformation.hProcess)) goto abort; 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') { if (extra_injections != NULL && extra_injections[0] != '\0') {
char* copy = (char*)malloc(strlen(extra_injections) + 1); 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) { if (delay) {
fprintf(stderr, "Delaying for %dms\n", delay); log_info(plfBoot, "Delaying for %dms", delay);
Sleep(delay); Sleep(delay);
} }
// Injection completed, let the program continue execution // Injection completed, let the program continue execution
if (FAILED(ResumeThread(processInformation.hThread))) { if (FAILED(ResumeThread(processInformation.hThread))) {
fprintf(stderr, "Fatal: ResumeThread failed: %03x\n", GetLastError()); log_error(plfProcesses, "Fatal: ResumeThread failed: %d", GetLastError());
goto abort; goto abort;
} }
@ -140,14 +148,14 @@ HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wa
abort: abort:
if (processInformation.hProcess && processInformation.hProcess != INVALID_HANDLE_VALUE) { if (processInformation.hProcess && processInformation.hProcess != INVALID_HANDLE_VALUE) {
if (!CloseHandle(processInformation.hThread) && GetLastError() != ERROR_INVALID_HANDLE) 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)) 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 (processInformation.hThread && processInformation.hThread != INVALID_HANDLE_VALUE) {
if (!CloseHandle(processInformation.hThread) && GetLastError() != ERROR_INVALID_HANDLE) 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; return INVALID_HANDLE_VALUE;

View File

@ -2,7 +2,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait, DWORD delay, HANDLE start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BOOL debug_wait,
LPCSTR extra_injections, DWORD flags); DWORD delay, LPCSTR extra_injections, DWORD flags);
#define MICELIB "mice.dll" #define MICELIB "mice.dll"

View File

@ -17,14 +17,14 @@
#include "log_facilities.def" #include "log_facilities.def"
#undef _LF #undef _LF
static BOOL logIsMaster = FALSE;
extern WCHAR exeName[MAX_PATH + 1]; extern WCHAR exeName[MAX_PATH + 1];
extern DWORD imageOffset; extern DWORD imageOffset;
extern BOOL(WINAPI* TrueWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, extern BOOL(WINAPI* TrueWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
BOOL HAS_COLOUR = FALSE;
char _log_prelude[64]; char _log_prelude[64];
char* log_prelude() { char* log_prelude() {
time_t rawtime; time_t rawtime;
@ -65,13 +65,12 @@ void __stdcall amLogCallback(DWORD level, char* format) {
DWORD pLogcb; DWORD pLogcb;
DWORD* ppLogcb; DWORD* ppLogcb;
HANDLE hSlot;
static char log_buf[1024]; static char log_buf[1024];
int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list args) { int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list args) {
// Early-return if we have nothing to do // Early-return if we have nothing to do
if (MiceConfig.mice.log_level < log_level && if (MiceConfig.mice.log_level < log_level) return 0;
(!MiceConfig.mice.log_to_file || MiceConfig.mice.file_log_level < log_level)) {
return 0;
}
// TODO: These are all horrible bodges // TODO: These are all horrible bodges
// if (wcscmp(exeName, L"mxnetwork.exe") == 0) { // 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]; char prefix = LOG_PREFIXES[log_level];
EnterCriticalSection(&logger_lock);
int col_len = strlen(log_colours[log_level]); 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], int log_len = snprintf(log_buf, _countof(log_buf), "%s%s%c:%s:", log_colours[log_level],
log_prelude(), prefix, facility->m_name); log_prelude(), prefix, facility->m_name);
log_len += vsnprintf(log_buf + log_len, _countof(log_buf) - log_len, format, args); 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_len += snprintf(log_buf + log_len, _countof(log_buf) - log_len, "%s\n", COLOR_RESET);
log_buf[_countof(log_buf) - 1] = '\0'; log_buf[_countof(log_buf) - 1] = '\0';
if (MiceConfig.mice.log_level >= log_level) { if (hSlot != INVALID_HANDLE_VALUE) {
HANDLE sout = GetStdHandle(STD_OUTPUT_HANDLE); WriteFile(hSlot, log_buf, log_len, NULL, NULL);
WriteFile(sout, log_buf, log_len, NULL, NULL); } else {
// FlushFileBuffers(sout); // 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);
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);
}
} }
LeaveCriticalSection(&logger_lock); LeaveCriticalSection(&logger_lock);
@ -178,23 +168,18 @@ int _log_game(PLOG_FACILITY facility, const char* format, ...) {
return ret; return ret;
} }
void setup_logging() { void setup_logging(DWORD slotPid) {
// Force stdio even for GUI applications char slotName[MAX_PATH + 1];
// AttachConsole(ATTACH_PARENT_PROCESS); sprintf_s(slotName, MAX_PATH, "\\\\.\\mailslot\\micelog%d", slotPid);
// Enable colour in CMD hSlot = CreateFile(slotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING,
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwMode = 0; if (hSlot == INVALID_HANDLE_VALUE) {
if (GetConsoleMode(hConsole, &dwMode)) fprintf(stderr, "Fatal: Failed to open mailslot %s", slotName);
HAS_COLOUR = SetConsoleMode(hConsole, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); ExitProcess(2);
}
InitializeCriticalSection(&logger_lock); 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) { void log_stack(PLOG_FACILITY facility) {

View File

@ -38,7 +38,7 @@ int vlog_game(PLOG_FACILITY facility, const char* format, va_list args);
void log_stack(PLOG_FACILITY facility); void log_stack(PLOG_FACILITY facility);
void setup_logging(); void setup_logging(DWORD slotPid);
// Disable some logging entirely at build time for speed // Disable some logging entirely at build time for speed
#define COMPILE_LOG_LEVEL 6 #define COMPILE_LOG_LEVEL 6