From 73da986d0b9baab1f3ead890f7d189517b0525bb Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 15 Aug 2024 11:34:31 +0200 Subject: [PATCH] refactor: Inject - Split into modules similar to launcher to improve overall structure - Use property (node) API for configuration Remark: "MVP" without IAT hooking working, command line arguments and overriding. Will be added (again) later --- src/main/inject/Module.mk | 9 +- src/main/inject/debug-config.c | 29 +++ src/main/inject/debug-config.h | 18 ++ src/main/inject/debugger-config.c | 52 ++++++ src/main/inject/debugger-config.h | 29 +++ src/main/inject/debugger.c | 212 +++++++++++---------- src/main/inject/debugger.h | 25 +-- src/main/inject/hooks-config.c | 226 ++++++++++++++++++++++ src/main/inject/hooks-config.h | 39 ++++ src/main/inject/inject-config.c | 77 ++++++++ src/main/inject/inject-config.h | 26 +++ src/main/inject/inject.c | 135 ++++++++++++++ src/main/inject/inject.h | 8 + src/main/inject/logger-config.c | 140 ++++++++++++++ src/main/inject/logger-config.h | 43 +++++ src/main/inject/logger.c | 123 ++++++++++++ src/main/inject/logger.h | 10 + src/main/inject/main.c | 298 ++++-------------------------- 18 files changed, 1125 insertions(+), 374 deletions(-) create mode 100644 src/main/inject/debug-config.c create mode 100644 src/main/inject/debug-config.h create mode 100644 src/main/inject/debugger-config.c create mode 100644 src/main/inject/debugger-config.h create mode 100644 src/main/inject/hooks-config.c create mode 100644 src/main/inject/hooks-config.h create mode 100644 src/main/inject/inject-config.c create mode 100644 src/main/inject/inject-config.h create mode 100644 src/main/inject/inject.c create mode 100644 src/main/inject/inject.h create mode 100644 src/main/inject/logger-config.c create mode 100644 src/main/inject/logger-config.h create mode 100644 src/main/inject/logger.c create mode 100644 src/main/inject/logger.h diff --git a/src/main/inject/Module.mk b/src/main/inject/Module.mk index 55d44bd..819c64c 100644 --- a/src/main/inject/Module.mk +++ b/src/main/inject/Module.mk @@ -14,8 +14,15 @@ libs_inject := \ mxml \ src_inject := \ - main.c \ + debug-config.c \ + debugger-config.c \ debugger.c \ + hooks-config.c \ + inject-config.c \ + inject.c \ + logger-config.c \ + logger.c \ + main.c \ options.c \ version.c \ diff --git a/src/main/inject/debug-config.c b/src/main/inject/debug-config.c new file mode 100644 index 0000000..4ae5e34 --- /dev/null +++ b/src/main/inject/debug-config.c @@ -0,0 +1,29 @@ +#define LOG_MODULE "inject-debug-config" + +#include "core/property-node-ext.h" + +#include "iface-core/log.h" + +#include "inject/debug-config.h" + +void debug_config_init(debug_config_t *config) +{ + log_assert(config); + + config->property_configs_log = false; +} + +void debug_config_load( + const core_property_node_t *node, debug_config_t *config) +{ + core_property_node_result_t result; + + log_assert(node); + log_assert(config); + + result = core_property_node_ext_bool_read(node, "property_configs_log", &config->property_configs_log); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(node, "property_api_trace_log", &config->property_configs_log); + core_property_node_fatal_on_error(result); +} \ No newline at end of file diff --git a/src/main/inject/debug-config.h b/src/main/inject/debug-config.h new file mode 100644 index 0000000..5966d20 --- /dev/null +++ b/src/main/inject/debug-config.h @@ -0,0 +1,18 @@ +#ifndef INJECT_DEBUG_CONFIG_H +#define INJECT_DEBUG_CONFIG_H + +#include + +#include "core/property-node.h" + +typedef struct debug_config { + bool property_configs_log; + bool property_api_trace_log; +} debug_config_t; + +void debug_config_init(debug_config_t *config); + +void debug_config_load( + const core_property_node_t *node, debug_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/debugger-config.c b/src/main/inject/debugger-config.c new file mode 100644 index 0000000..eb35aa4 --- /dev/null +++ b/src/main/inject/debugger-config.c @@ -0,0 +1,52 @@ +#define LOG_MODULE "inject-debugger-config" + +#include "core/property-node-ext.h" + +#include "iface-core/log.h" + +#include "inject/debugger-config.h" + +#include "util/str.h" + +static debugger_attach_type_t _debugger_config_str_to_attachtype(const char *str) +{ + log_assert(str); + + if (str_eq(str, "none")) { + return DEBUGGER_ATTACH_TYPE_NONE; + } else if (str_eq(str, "inject")) { + return DEBUGGER_ATTACH_TYPE_INJECT; + } else if (str_eq(str, "external")) { + return DEBUGGER_ATTACH_TYPE_EXTERNAL; + } else { + log_fatal("Invalid debugger attach type for debugger config: %s", str); + } +} + +void debugger_config_init(debugger_config_t *config) +{ + log_assert(config); + + memset(config, 0, sizeof(debugger_config_t)); +} + +void debugger_config_load( + const core_property_node_t *node, debugger_config_t *config) +{ + core_property_node_result_t result; + char buffer[16]; + + log_assert(node); + log_assert(config); + + result = core_property_node_ext_str_read(node, "app/path", config->app.path, sizeof(config->app.path)); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read_or_default( + node, "app/args", config->app.args, sizeof(config->app.args), ""); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(node, "attach_type", buffer, sizeof(buffer)); + core_property_node_fatal_on_error(result); + config->attach_type = _debugger_config_str_to_attachtype(buffer); +} \ No newline at end of file diff --git a/src/main/inject/debugger-config.h b/src/main/inject/debugger-config.h new file mode 100644 index 0000000..7e92ade --- /dev/null +++ b/src/main/inject/debugger-config.h @@ -0,0 +1,29 @@ +#ifndef INJECT_DEBUGGER_CONFIG_H +#define INJECT_DEBUGGER_CONFIG_H + +#include + +#include + +#include "core/property-node.h" + +#include "inject/debugger.h" + +// https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/command-line-string-limitation +#define WINDOWS_CMD_LINE_ARGS_MAX_LEN 8192 + +typedef struct debugger_config { + struct debugger_app_config { + char path[MAX_PATH]; + char args[WINDOWS_CMD_LINE_ARGS_MAX_LEN]; + } app; + + debugger_attach_type_t attach_type; +} debugger_config_t; + +void debugger_config_init(debugger_config_t *config); + +void debugger_config_load( + const core_property_node_t *node, debugger_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/debugger.c b/src/main/inject/debugger.c index 83b9e10..3b378af 100644 --- a/src/main/inject/debugger.c +++ b/src/main/inject/debugger.c @@ -9,6 +9,7 @@ #include #include "core/log-bt.h" +#include "core/log-ext.h" #include "iface-core/log.h" @@ -23,10 +24,11 @@ struct debugger_thread_params { const char *app_name; - char *cmd_line; - bool local_debugger; + const char *cmd_line; }; +static debugger_attach_type_t debugger_attach_type; + static HANDLE debugger_thread_handle; static HANDLE debugger_ready_event; @@ -134,7 +136,7 @@ read_debug_str(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi) free(str); log_warning( - "ERROR: ReadProcessMemory for debug string failed: %08x", + "ReadProcessMemory for debug string failed: %08x", (unsigned int) GetLastError()); str = NULL; } @@ -160,12 +162,12 @@ read_debug_wstr(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi) if (wstr_narrow(wstr, &str)) { str[odsi->nDebugStringLength - 1] = '\0'; } else { - log_warning("ERROR: OutputDebugStringW: UTF-16 conversion failed"); + log_warning("OutputDebugStringW: UTF-16 conversion failed"); str = NULL; } } else { log_warning( - "ERROR: ReadProcessMemory for debug string failed: %08x", + "ReadProcessMemory for debug string failed: %08x", (unsigned int) GetLastError()); str = NULL; } @@ -206,9 +208,10 @@ static bool log_debug_str(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi) } } -static bool debugger_create_process( - bool local_debugger, const char *app_name, char *cmd_line) +static bool debugger_create_process(const char *app_name, const char *cmd_line) { + char cmd_line_cpy[8192]; + log_assert(app_name); log_assert(cmd_line); @@ -222,10 +225,10 @@ static bool debugger_create_process( flags = 0; // CREATE_SUSPENDED that we have plenty of time to set up the debugger and - // theemote process environment with hook dlls. + // the remote process environment with hook dlls. flags |= CREATE_SUSPENDED; - if (local_debugger) { + if (debugger_attach_type == DEBUGGER_ATTACH_TYPE_INJECT) { // DEBUG_PROCESS is required to make this work properly. Otherwise, // weird things like random remote process crashing are happening. Also, // DEBUG_ONLY_THIS_PROCESS is NOT sufficient/ correct here. Maybe I @@ -241,27 +244,25 @@ static bool debugger_create_process( log_misc("Creating remote process %s...", app_name); log_misc("Remote process cmd_line: %s", cmd_line); + str_cpy(cmd_line_cpy, sizeof(cmd_line_cpy), cmd_line); + ok = CreateProcess( - app_name, cmd_line, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi); + app_name, cmd_line_cpy, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi); if (!ok) { log_warning( - "ERROR: Failed to launch hooked EXE: %08x", + "Failed to launch hooked EXE: %08x", (unsigned int) GetLastError()); - free(cmd_line); - return false; } - free(cmd_line); - log_info("Remote process created, pid: %ld", pi.dwProcessId); return true; } -static uint32_t debugger_loop() +static uint32_t _debugger_thread_loop() { DEBUG_EVENT de; DWORD continue_status; @@ -272,7 +273,7 @@ static uint32_t debugger_loop() for (;;) { if (!WaitForDebugEvent(&de, INFINITE)) { log_warning( - "ERROR: WaitForDebugEvent failed: %08x", + "WaitForDebugEvent failed: %08x", (unsigned int) GetLastError()); return 1; } @@ -391,14 +392,14 @@ static uint32_t debugger_loop() if (!ContinueDebugEvent( de.dwProcessId, de.dwThreadId, continue_status)) { log_warning( - "ERROR: ContinueDebugEvent failed: %08x", + "ContinueDebugEvent failed: %08x", (unsigned int) GetLastError()); return 1; } } } -static DWORD WINAPI debugger_proc(LPVOID param) +static DWORD WINAPI _debugger_thread_proc(LPVOID param) { uint32_t debugger_loop_exit_code; @@ -406,11 +407,9 @@ static DWORD WINAPI debugger_proc(LPVOID param) params = (struct debugger_thread_params *) param; - log_misc( - "Debugger thread start (local debugger: %d)", params->local_debugger); + log_misc("Debugger thread start"); - if (!debugger_create_process( - params->local_debugger, params->app_name, params->cmd_line)) { + if (!debugger_create_process(params->app_name, params->cmd_line)) { return 0; } @@ -418,8 +417,8 @@ static DWORD WINAPI debugger_proc(LPVOID param) // Don't run our local debugger loop if the user wants to attach a remote // debugger or debugger is disabled - if (params->local_debugger) { - debugger_loop_exit_code = debugger_loop(); + if (debugger_attach_type == DEBUGGER_ATTACH_TYPE_INJECT) { + debugger_loop_exit_code = _debugger_thread_loop(); free(params); @@ -439,49 +438,7 @@ static DWORD WINAPI debugger_proc(LPVOID param) } } -bool debugger_init(bool local_debugger, const char *app_name, char *cmd_line) -{ - struct debugger_thread_params *thread_params; - - debugger_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL); - - if (!debugger_ready_event) { - free(cmd_line); - - log_warning( - "ERROR: Creating event object failed: %08x", - (unsigned int) GetLastError()); - return false; - } - - // free'd by thread if created successfully - thread_params = xmalloc(sizeof(struct debugger_thread_params)); - - thread_params->app_name = app_name; - thread_params->cmd_line = cmd_line; - thread_params->local_debugger = local_debugger; - - debugger_thread_handle = - CreateThread(NULL, 0, debugger_proc, thread_params, 0, 0); - - if (!debugger_thread_handle) { - free(cmd_line); - free(thread_params); - - log_warning( - "ERROR: Creating debugger thread failed: %08x", - (unsigned int) GetLastError()); - return false; - } - - WaitForSingleObject(debugger_ready_event, INFINITE); - - log_misc("Debugger initialized"); - - return true; -} - -bool debugger_wait_for_remote_debugger() +void _debugger_wait_for_remote_debugger() { BOOL res; @@ -491,10 +448,10 @@ bool debugger_wait_for_remote_debugger() res = FALSE; if (!CheckRemoteDebuggerPresent(pi.hProcess, &res)) { - log_warning( - "ERROR: CheckRemoteDebuggerPresent failed: %08x", + log_fatal( + "CheckRemoteDebuggerPresent failed: %08x", (unsigned int) GetLastError()); - return false; + return; } if (res) { @@ -504,8 +461,72 @@ bool debugger_wait_for_remote_debugger() Sleep(1000); } +} - return true; +void _debugger_resume_process() +{ + log_info("Resuming remote process..."); + + if (ResumeThread(pi.hThread) == -1) { + log_fatal( + "Resuming remote process failed: %08x", + (unsigned int) GetLastError()); + } + + CloseHandle(pi.hThread); +} + +void _debugger_wait_process_exit() +{ + log_misc("Waiting for remote process to exit..."); + + // Wait for the process as we might have a remote debugger attached, so our + // debugger thread exits after creating the process + WaitForSingleObject(pi.hProcess, INFINITE); + + // When the process exits, the debugger gets notified and the thread ends + WaitForSingleObject(debugger_thread_handle, INFINITE); + + log_misc("Remote process exit'd"); +} + +void debugger_init(debugger_attach_type_t attach_type, const char *app_name, const char *cmd_line) +{ + struct debugger_thread_params *thread_params; + + log_assert(app_name); + log_assert(cmd_line); + + debugger_attach_type = attach_type; + + debugger_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (!debugger_ready_event) { + log_fatal( + "Creating event object failed: %08x", + (unsigned int) GetLastError()); + } + + // free'd by thread if created successfully + thread_params = xmalloc(sizeof(struct debugger_thread_params)); + + thread_params->app_name = app_name; + thread_params->cmd_line = cmd_line; + + debugger_thread_handle = + CreateThread(NULL, 0, _debugger_thread_proc, thread_params, 0, 0); + + if (!debugger_thread_handle) { + free(thread_params); + + log_fatal( + "Creating debugger thread failed: %08x", + (unsigned int) GetLastError()); + } + + WaitForSingleObject(debugger_ready_event, INFINITE); + + log_misc("Initialized, attach type: %d", debugger_attach_type); } bool debugger_inject_dll(const char *path_dll) @@ -523,6 +544,10 @@ bool debugger_inject_dll(const char *path_dll) dll_path_length = SearchPath(NULL, path_dll, NULL, MAX_PATH, dll_path, NULL); + if (dll_path_length == 0) { + log_fatal_on_win_last_error("Determining path for dll %s failed", path_dll); + } + dll_path_length++; remote_addr = VirtualAllocEx( @@ -534,7 +559,7 @@ bool debugger_inject_dll(const char *path_dll) if (!remote_addr) { log_warning( - "ERROR: VirtualAllocEx failed: %08x", + "VirtualAllocEx failed: %08x", (unsigned int) GetLastError()); goto alloc_fail; @@ -545,7 +570,7 @@ bool debugger_inject_dll(const char *path_dll) if (!ok) { log_warning( - "ERROR: WriteProcessMemory failed: %08x", + "WriteProcessMemory failed: %08x", (unsigned int) GetLastError()); goto write_fail; @@ -562,7 +587,7 @@ bool debugger_inject_dll(const char *path_dll) if (remote_thread == NULL) { log_warning( - "ERROR: CreateRemoteThread failed: %08x", + "CreateRemoteThread failed: %08x", (unsigned int) GetLastError()); goto inject_fail; @@ -576,9 +601,11 @@ bool debugger_inject_dll(const char *path_dll) if (!ok) { log_warning( - "ERROR: VirtualFreeEx failed: %08x", (unsigned int) GetLastError()); + "VirtualFreeEx failed: %08x", (unsigned int) GetLastError()); } + log_misc("Injecting success: %s", path_dll); + return true; inject_fail: @@ -761,34 +788,19 @@ inject_fail: return false; } -bool debugger_resume_process() +void debugger_run() { - log_info("Resuming remote process..."); - - if (ResumeThread(pi.hThread) == -1) { - log_warning( - "ERROR: Resuming remote process: %08x", - (unsigned int) GetLastError()); - return false; + // Execute this after injecting the DLLs. Some debuggers seem to crash if we + // attach the process before DLL injection (inject's local one doesn't + // crash). However, this means the remote debugger is missing out on all + // injected DLL loads, e.g. calls to DllMain + if (debugger_attach_type == DEBUGGER_ATTACH_TYPE_EXTERNAL) { + _debugger_wait_for_remote_debugger(); } - CloseHandle(pi.hThread); + _debugger_resume_process(); - return true; -} - -void debugger_wait_process_exit() -{ - log_misc("Waiting for remote process to exit..."); - - // Wait for the process as we might have a remote debugger attached, so our - // debugger thread exits after creating the process - WaitForSingleObject(pi.hProcess, INFINITE); - - // When the process exits, the debugger gets notified and the thread ends - WaitForSingleObject(debugger_thread_handle, INFINITE); - - log_misc("Remote process exit'd"); + _debugger_wait_process_exit(); } void debugger_finit(bool failure) diff --git a/src/main/inject/debugger.h b/src/main/inject/debugger.h index 76075f1..b1c82b3 100644 --- a/src/main/inject/debugger.h +++ b/src/main/inject/debugger.h @@ -2,16 +2,11 @@ #include -/** - * Initialize inject's logger backend. - * - * This takes care of hooking and merging the different log - * streams, e.g. inject's local logging and inject's debugger - * receiving remote logging events. - * - * @param log_file_path Path to the file to log to or NULL to - * disable. - */ +typedef enum debugger_attach_type { + DEBUGGER_ATTACH_TYPE_NONE = 0, + DEBUGGER_ATTACH_TYPE_INJECT = 1, + DEBUGGER_ATTACH_TYPE_EXTERNAL = 2, +} debugger_attach_type_t; /** * Initialize the debugger. @@ -30,15 +25,11 @@ * set the parameter local_debugger to false. Then, the debugger * will only create the remote process and monitor it. * - * @param local_debugger True to attach inject's local debugger, - * false to allow attaching a remote - * debugger with enhanced features. + * @param attach_type Setup the debugger to attach with the given type of debugger * @param app_name Name of the application to spawn and debug. * @param cmd_line Command line string to pass to application. - * @return true on success, false on error. On error, no remote - * application and local debugger is started. */ -bool debugger_init(bool local_debugger, const char *app_name, char *cmd_line); +void debugger_init(debugger_attach_type_t attach_type, const char *app_name, const char *cmd_line); /** * Inject a DLL into the remote process. @@ -48,6 +39,8 @@ bool debugger_init(bool local_debugger, const char *app_name, char *cmd_line); */ bool debugger_inject_dll(const char *path_dll); +void debugger_run(); + /** * Inject a DLL into the remote process by replacing its reference in * the import table. diff --git a/src/main/inject/hooks-config.c b/src/main/inject/hooks-config.c new file mode 100644 index 0000000..7328b91 --- /dev/null +++ b/src/main/inject/hooks-config.c @@ -0,0 +1,226 @@ +#define LOG_MODULE "inject-hooks-config" + +#include "core/property-ext.h" +#include "core/property-node-ext.h" + +#include "iface-core/log.h" + +#include "inject/hooks-config.h" + +#define HOOKS_CONFIG_MAX_LAYER_CONFIG_NODES 8 + +static core_property_t * +_hooks_config_layered_config_nodes_load(const core_property_node_t *node) +{ + char kind[64]; + char file[MAX_PATH]; + int cnt; + + core_property_node_t cur; + core_property_node_t tmp; + core_property_t *config_property[HOOKS_CONFIG_MAX_LAYER_CONFIG_NODES]; + core_property_t *merged_property; + core_property_node_result_t result; + core_property_result_t prop_result; + + log_assert(node); + + cnt = 0; + result = core_property_node_search(node, "config", &cur); + + if (result != CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + core_property_node_fatal_on_error(result); + } + + while (result != CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + if (cnt >= HOOKS_CONFIG_MAX_LAYER_CONFIG_NODES) { + log_fatal( + "Exceeding max supported config nodes for layering, max is %d", + HOOKS_CONFIG_MAX_LAYER_CONFIG_NODES); + } + + result = + core_property_node_attr_read(&cur, "kind", kind, sizeof(kind)); + + if (CORE_PROPERTY_NODE_RESULT_IS_ERROR(result)) { + log_fatal("Failed reading 'kind' attribute value of config node"); + } + + if (!strcmp(kind, "file")) { + core_property_node_str_read(&cur, file, sizeof(file)); + + prop_result = core_property_file_load(file, &config_property[cnt]); + core_property_fatal_on_error(prop_result); + } else if (!strcmp(kind, "inline")) { + // The nested child is the actual root of the inline, not the outer + // node + result = core_property_node_child_get(&cur, &tmp); + memcpy(&cur, &tmp, sizeof(core_property_node_t)); + + if (result != CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + core_property_node_fatal_on_error(result); + } + + result = + core_property_node_ext_extract(&cur, &config_property[cnt]); + core_property_node_fatal_on_error(result); + } else { + log_fatal( + "Unsupported 'kind' attribute value '%s' of config node", kind); + } + + cnt++; + result = core_property_node_next_result_search(&cur, &tmp); + memcpy(&cur, &tmp, sizeof(core_property_node_t)); + + if (result != CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + core_property_node_fatal_on_error(result); + } + } + + if (cnt == 0) { + prop_result = core_property_str_load("", &merged_property); + core_property_fatal_on_error(prop_result); + } else { + prop_result = + core_property_ext_many_merge(config_property, cnt, &merged_property); + core_property_fatal_on_error(prop_result); + + for (int i = 0; i < cnt; i++) { + core_property_free(&config_property[i]); + } + } + + return merged_property; +} + +static void _hooks_config_hooks_load( + const core_property_node_t *node, + struct hooks_hook_config *configs) +{ + core_property_node_result_t result; + core_property_node_t child; + core_property_node_t tmp; + uint8_t processed_hooks; + + processed_hooks = 0; + + result = core_property_node_search(node, "hook", &child); + + do { + if (processed_hooks >= HOOKS_CONFIG_MAX_HOOKS) { + log_fatal("Cannot load more hooks, max supported capacity reached"); + } + + if (result == CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + return; + } else { + core_property_node_fatal_on_error(result); + } + + result = core_property_node_ext_bool_read(&child, "enable", &configs[processed_hooks].enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(&child, "path", configs[processed_hooks].path, sizeof(configs[processed_hooks].path)); + core_property_node_fatal_on_error(result); + + configs[processed_hooks].config = _hooks_config_layered_config_nodes_load(&child); + + result = core_property_node_next_result_search(&child, &tmp); + memcpy(&child, &tmp, sizeof(core_property_node_t)); + + processed_hooks++; + } while (true); +} + +static void _hooks_config_iats_load( + const core_property_node_t *node, + struct hooks_iat_config *configs) +{ + core_property_node_result_t result; + core_property_node_t child; + core_property_node_t tmp; + uint8_t processed_hooks; + + processed_hooks = 0; + + result = core_property_node_search(node, "iat", &child); + + do { + if (processed_hooks >= HOOKS_CONFIG_MAX_HOOKS) { + log_fatal("Cannot load more hooks, max supported capacity reached"); + } + + if (result == CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND) { + return; + } else { + core_property_node_fatal_on_error(result); + } + + result = core_property_node_ext_bool_read(&child, "enable", &configs[processed_hooks].enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(&child, "source_name", configs[processed_hooks].source_name, sizeof(configs[processed_hooks].source_name)); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(&child, "path", configs[processed_hooks].path, sizeof(configs[processed_hooks].path)); + core_property_node_fatal_on_error(result); + + configs[processed_hooks].config = _hooks_config_layered_config_nodes_load(&child); + + result = core_property_node_next_result_search(&child, &tmp); + memcpy(&child, &tmp, sizeof(core_property_node_t)); + + processed_hooks++; + } while (true); +} + +void hooks_config_init(hooks_config_t *config) +{ + log_assert(config); + + memset(config, 0, sizeof(hooks_config_t)); +} + +void hooks_config_load( + const core_property_node_t *node, hooks_config_t *config) +{ + log_assert(node); + log_assert(config); + + _hooks_config_hooks_load(node, config->hooks); + _hooks_config_iats_load(node, config->iats); +} + +bool hooks_config_hook_is_valid(const struct hooks_hook_config *hook) +{ + log_assert(hook); + + return strlen(hook->path) > 0; +} + +bool hooks_config_iat_is_valid(const struct hooks_iat_config *hook) +{ + log_assert(hook); + + return strlen(hook->path) > 0; +} + +void hooks_config_fini(hooks_config_t *config) +{ + uint32_t i; + + log_assert(config); + + for (i = 0; i < HOOKS_CONFIG_MAX_HOOKS; i++) { + if (hooks_config_hook_is_valid(&config->hooks[i])) { + core_property_free(&config->hooks[i].config); + } + } + + for (i = 0; i < HOOKS_CONFIG_MAX_HOOKS; i++) { + if (hooks_config_iat_is_valid(&config->iats[i])) { + core_property_free(&config->iats[i].config); + } + } +} \ No newline at end of file diff --git a/src/main/inject/hooks-config.h b/src/main/inject/hooks-config.h new file mode 100644 index 0000000..87441cd --- /dev/null +++ b/src/main/inject/hooks-config.h @@ -0,0 +1,39 @@ +#ifndef INJECT_HOOKS_CONFIG_H +#define INJECT_HOOKS_CONFIG_H + +#include + +#include + +#include "core/property.h" +#include "core/property-node.h" + +#define HOOKS_CONFIG_MAX_HOOKS 16 + +typedef struct hooks_config { + struct hooks_hook_config { + bool enable; + char path[MAX_PATH]; + core_property_t *config; + } hooks[HOOKS_CONFIG_MAX_HOOKS]; + + struct hooks_iat_config { + bool enable; + char source_name[MAX_PATH]; + char path[MAX_PATH]; + core_property_t *config; + } iats[HOOKS_CONFIG_MAX_HOOKS]; +} hooks_config_t; + +void hooks_config_init(hooks_config_t *config); + +void hooks_config_load( + const core_property_node_t *node, hooks_config_t *config); + +bool hooks_config_hook_is_valid(const struct hooks_hook_config *hook); + +bool hooks_config_iat_is_valid(const struct hooks_iat_config *hook); + +void hooks_config_fini(hooks_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/inject-config.c b/src/main/inject/inject-config.c new file mode 100644 index 0000000..9f9e8d4 --- /dev/null +++ b/src/main/inject/inject-config.c @@ -0,0 +1,77 @@ +#define LOG_MODULE "inject-config" + +#include "core/property-ext.h" +#include "core/property-node.h" +#include "core/property.h" + +#include "core/property-mxml-internal.h" +#include "core/property-node-ext.h" + +#include "iface-core/log.h" + +#include "inject/debug-config.h" +#include "inject/debugger-config.h" +#include "inject/hooks-config.h" +#include "inject/inject-config.h" +#include "inject/logger-config.h" + +void inject_config_init(struct inject_config *config) +{ + log_assert(config); + + config->version = 1; + + debug_config_init(&config->debug); + debugger_config_init(&config->debugger); + hooks_config_init(&config->hooks); + logger_config_init(&config->logger); +} + +void inject_config_file_load(const char *path, inject_config_t *config) +{ + core_property_result_t result_prop; + core_property_node_result_t result; + core_property_t *property; + core_property_node_t root_node; + core_property_node_t child_node; + + log_info("Loading configuration file: %s", path); + + result_prop = core_property_file_load(path, &property); + core_property_fatal_on_error(result_prop); + + result = core_property_root_node_get(property, &root_node); + core_property_node_fatal_on_error(result); + + result = core_property_node_search(&root_node, "debug", &child_node); + core_property_node_fatal_on_error(result); + debug_config_load(&child_node, &config->debug); + + result = core_property_node_search(&root_node, "debugger", &child_node); + core_property_node_fatal_on_error(result); + debugger_config_load(&child_node, &config->debugger); + + result = core_property_node_search(&root_node, "hooks", &child_node); + core_property_node_fatal_on_error(result); + hooks_config_load(&child_node, &config->hooks); + + result = core_property_node_search(&root_node, "logger", &child_node); + core_property_node_fatal_on_error(result); + logger_config_load(&child_node, &config->logger); + + if (config->debug.property_configs_log) { + core_property_ext_log(property, log_misc_func); + } + + core_property_free(&property); + + log_misc("Loading done"); +} + +void inject_config_fini(inject_config_t *config) +{ + log_assert(config); + + // Other configs don't have a fini + hooks_config_fini(&config->hooks); +} \ No newline at end of file diff --git a/src/main/inject/inject-config.h b/src/main/inject/inject-config.h new file mode 100644 index 0000000..3b1747d --- /dev/null +++ b/src/main/inject/inject-config.h @@ -0,0 +1,26 @@ +#ifndef INJECT_CONFIG_H +#define INJECT_CONFIG_H + +#include + +#include "inject/debug-config.h" +#include "inject/debugger-config.h" +#include "inject/hooks-config.h" +#include "inject/logger-config.h" + +typedef struct inject_config { + uint32_t version; + + hooks_config_t hooks; + logger_config_t logger; + debugger_config_t debugger; + debug_config_t debug; +} inject_config_t; + +void inject_config_init(struct inject_config *config); + +void inject_config_file_load(const char *path, inject_config_t *config); + +void inject_config_fini(inject_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/inject.c b/src/main/inject/inject.c new file mode 100644 index 0000000..8a15612 --- /dev/null +++ b/src/main/inject/inject.c @@ -0,0 +1,135 @@ +#define LOG_MODULE "inject" + +#include "iface-core/log.h" + +#include "inject/debugger.h" +#include "inject/inject-config.h" +#include "inject/logger.h" +#include "inject/version.h" + +#include "util/os.h" +#include "util/signal.h" + +static void _inject_fini(); + +static void _inject_signal_handler_shutdown() +{ + _inject_fini(); + exit(0); +} + +static void _inject_header_log() +{ + log_info( + "\n" + " _ _ _ \n" + " (_)_ __ (_) ___ ___| |_ \n" + " | | '_ \\ | |/ _ \\/ __| __|\n" + " | | | | || | __/ (__| |_ \n" + " |_|_| |_|/ |\\___|\\___|\\__|\n" + " |__/ "); + + log_info( + "build date %s, gitrev %s", inject_build_date, inject_gitrev); +} + +static void _inject_iat_hook_dlls(const struct hooks_iat_config *configs) +{ + uint32_t i; + + log_assert(configs); + + log_info("Injecting IAT hook DLLs..."); + + for (i = 0; i < HOOKS_CONFIG_MAX_HOOKS; i++) { + if (hooks_config_iat_is_valid(&configs[i])) { + if (configs[i].enable) { + if (!debugger_replace_dll_iat(configs[i].source_name, configs[i].path)) { + log_fatal("Injecting iat hook failed: %s=%s", configs[i].source_name, configs[i].path); + } + } else { + log_warning("iat hook disabled: %s=%s", configs[i].source_name, configs[i].path); + } + } + } +} + +static void _inject_hook_dlls(const struct hooks_hook_config *configs) +{ + uint32_t i; + + log_assert(configs); + + log_info("Injecting hook DLLs..."); + + for (i = 0; i < HOOKS_CONFIG_MAX_HOOKS; i++) { + if (hooks_config_hook_is_valid(&configs[i])) { + if (configs[i].enable) { + if (!debugger_inject_dll(configs[i].path)) { + log_fatal("Injecting hook failed: %s", configs[i].path); + } + } else { + log_warning("Hook disabled: %s", configs[i].path); + } + } + } +} + +static void _inject_init(const inject_config_t *config) +{ + log_assert(config); + + logger_init(&config->logger); + + _inject_header_log(); + os_version_log(); + + signal_exception_handler_init(); + // Cleanup remote process on CTRL+C + signal_register_shutdown_handler(_inject_signal_handler_shutdown); + + debugger_init( + config->debugger.attach_type, + config->debugger.app.path, + config->debugger.app.args); + + log_misc("<<< init"); +} + +static void _inject_run(const inject_config_t *config) +{ + log_assert(config); + + log_misc(">>> run"); + + _inject_hook_dlls(config->hooks.hooks); + _inject_iat_hook_dlls(config->hooks.iats); + + debugger_run(); + + log_misc("<<< run"); +} + +static void _inject_fini() +{ + log_misc(">>> fini"); + + debugger_finit(false); + + logger_fini(); +} + +// TODO run inject module only with inject configuration +// config must be bootstrapped using an early env +// in main with a early logger setup etc. +// apply the same to launcher +void inject_main(const inject_config_t *config) +{ + log_assert(config); + + log_misc(">>> main"); + + _inject_init(config); + _inject_run(config); + _inject_fini(); +} \ No newline at end of file diff --git a/src/main/inject/inject.h b/src/main/inject/inject.h new file mode 100644 index 0000000..187f28b --- /dev/null +++ b/src/main/inject/inject.h @@ -0,0 +1,8 @@ +#ifndef INJECT_INJECT_H +#define INJECT_INJECT_H + +#include "inject/inject-config.h" + +void inject_main(const inject_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/logger-config.c b/src/main/inject/logger-config.c new file mode 100644 index 0000000..cd0b151 --- /dev/null +++ b/src/main/inject/logger-config.c @@ -0,0 +1,140 @@ +#define LOG_MODULE "inject-logger-config" + +#include "core/property-node-ext.h" + +#include "iface-core/log.h" + +#include "inject/logger-config.h" + +#include "util/str.h" + +static enum core_log_bt_log_level _logger_config_str_to_loglevel(const char *str) +{ + log_assert(str); + + if (str_eq(str, "off")) { + return CORE_LOG_BT_LOG_LEVEL_OFF; + } else if (str_eq(str, "fatal")) { + return CORE_LOG_BT_LOG_LEVEL_FATAL; + } else if (str_eq(str, "warning")) { + return CORE_LOG_BT_LOG_LEVEL_WARNING; + } else if (str_eq(str, "info")) { + return CORE_LOG_BT_LOG_LEVEL_INFO; + } else if (str_eq(str, "misc")) { + return CORE_LOG_BT_LOG_LEVEL_MISC; + } else { + log_fatal("Invalid log level string in config: %s", str); + } +} + +static enum core_log_sink_async_overflow_policy _logger_config_str_to_overflowpolicy(const char *str) +{ + log_assert(str); + + if (str_eq(str, "discard_new")) { + return CORE_LOG_SINK_ASYNC_OVERFLOW_POLICY_DISCARD_NEW; + } else if (str_eq(str, "block")) { + return CORE_LOG_SINK_ASYNC_OVERFLOW_POLICY_BLOCK; + } else { + log_fatal("Invalid overflow policy string in config: %s", str); + } +} + +static void _logger_config_sink_async_load(const core_property_node_t *node, struct logger_sink_async_config *config) +{ + core_property_node_result_t result; + core_property_node_t child; + char buffer[16]; + + log_assert(node); + log_assert(config); + + result = core_property_node_search(node, "sinks/async", &child); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "enable", &config->enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_u8_read(&child, "queue_length", &config->queue_length); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(&child, "overflow_policy", buffer, sizeof(buffer)); + core_property_node_fatal_on_error(result); + config->overflow_policy = _logger_config_str_to_overflowpolicy(buffer); +} + +static void _logger_config_sink_console_load(const core_property_node_t *node, struct logger_sink_console_config *config) +{ + core_property_node_result_t result; + core_property_node_t child; + + log_assert(node); + log_assert(config); + + result = core_property_node_search(node, "sinks/console", &child); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "enable", &config->enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "color", &config->color); + core_property_node_fatal_on_error(result); +} + +static void _logger_config_sink_file_load(const core_property_node_t *node, struct logger_sink_file_config *config) +{ + core_property_node_result_t result; + core_property_node_t child; + + log_assert(node); + log_assert(config); + + result = core_property_node_search(node, "sinks/file", &child); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "enable", &config->enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(&child, "path", config->path, sizeof(config->path)); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "append", &config->append); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_bool_read(&child, "rotate", &config->rotate); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_u8_read(&child, "max_rotations", &config->max_rotations); + core_property_node_fatal_on_error(result); +} + +void logger_config_init(logger_config_t *config) +{ + log_assert(config); + + memset(config, 0, sizeof(logger_config_t)); +} + +void logger_config_load( + const core_property_node_t *node, logger_config_t *config) +{ + core_property_node_result_t result; + char buffer[16]; + + log_assert(node); + log_assert(config); + + result = core_property_node_ext_bool_read(node, "enable", &config->enable); + core_property_node_fatal_on_error(result); + + result = core_property_node_ext_str_read(node, "level", buffer, sizeof(buffer)); + core_property_node_fatal_on_error(result); + config->level = _logger_config_str_to_loglevel(buffer); + + result = core_property_node_ext_u32_read(node, "msg_buffer_size_bytes", &config->msg_buffer_size_bytes); + core_property_node_fatal_on_error(result); + + _logger_config_sink_async_load(node, &config->sink_async); + _logger_config_sink_console_load(node, &config->sink_console); + _logger_config_sink_file_load(node, &config->sink_file); +} diff --git a/src/main/inject/logger-config.h b/src/main/inject/logger-config.h new file mode 100644 index 0000000..66ae1b8 --- /dev/null +++ b/src/main/inject/logger-config.h @@ -0,0 +1,43 @@ +#ifndef INJECT_LOGGER_CONFIG_H +#define INJECT_LOGGER_CONFIG_H + +#include +#include + +#include + +#include "core/log-bt.h" +#include "core/log-sink-async.h" +#include "core/property-node.h" + +typedef struct logger_config { + bool enable; + enum core_log_bt_log_level level; + uint32_t msg_buffer_size_bytes; + + struct logger_sink_async_config { + bool enable; + uint8_t queue_length; + enum core_log_sink_async_overflow_policy overflow_policy; + } sink_async; + + struct logger_sink_console_config { + bool enable; + bool color; + } sink_console; + + struct logger_sink_file_config { + bool enable; + char path[MAX_PATH]; + bool append; + bool rotate; + uint8_t max_rotations; + } sink_file; +} logger_config_t; + +void logger_config_init(logger_config_t *config); + +void logger_config_load( + const core_property_node_t *node, logger_config_t *config); + +#endif \ No newline at end of file diff --git a/src/main/inject/logger.c b/src/main/inject/logger.c new file mode 100644 index 0000000..3393b32 --- /dev/null +++ b/src/main/inject/logger.c @@ -0,0 +1,123 @@ +#define LOG_MODULE "inject-logger" + +#include "core/log-bt.h" +#include "core/log-sink-async.h" +#include "core/log-sink-file.h" +#include "core/log-sink-list.h" +#include "core/log-sink-null.h" +#include "core/log-sink-std.h" + +#include "iface-core/log.h" + +#include "inject/logger-config.h" + +static void _logger_null_sink_init() +{ + core_log_sink_t sink; + + core_log_sink_null_open(&sink); + + // Size doesn't matter (but must be valid) + // logger is entirely disabled + core_log_bt_init(1024, &sink); + core_log_bt_level_set(CORE_LOG_BT_LOG_LEVEL_OFF); +} + +static bool _logger_sinks_create( + const logger_config_t *config, + core_log_sink_t *root_sink) +{ + core_log_sink_t target_sinks[2]; + core_log_sink_t list_sink; + uint8_t target_sink_count; + + log_assert(config); + log_assert(root_sink); + + target_sink_count = 0; + + // Fixed order to ensure logger's first sink to write to is + // async and async sinks to console and file + + if (config->sink_console.enable) { + core_log_sink_std_err_open( + config->sink_console.color, + &target_sinks[target_sink_count]); + + target_sink_count++; + } + + if (config->sink_file.enable) { + core_log_sink_file_open( + config->sink_file.path, + config->sink_file.append, + config->sink_file.rotate, + config->sink_file.max_rotations, + &target_sinks[target_sink_count]); + + target_sink_count++; + } + + if (target_sink_count > 0) { + // Compose to single sink + core_log_sink_list_open( + target_sinks, + target_sink_count, + &list_sink); + + // Async sink only makes sense if at least one other + // sink is enabled + if (config->sink_async.enable) { + core_log_sink_async_open( + config->msg_buffer_size_bytes, + config->sink_async.queue_length, + config->sink_async.overflow_policy, + &list_sink, + root_sink); + } else { + // "Sync" with list of sinks + memcpy(root_sink, &list_sink, sizeof(core_log_sink_t)); + } + + return true; + } else { + memset(root_sink, 0, sizeof(core_log_sink_t)); + return false; + } +} + +static void _logger_with_sinks_init(const logger_config_t *config) +{ + core_log_sink_t sink; + bool has_sinks; + + log_assert(config); + + has_sinks = _logger_sinks_create(config, &sink); + + if (has_sinks) { + core_log_bt_init(config->msg_buffer_size_bytes, &sink); + core_log_bt_level_set(config->level); + } else { + // Consider this equivalent to disabling logging entirely + _logger_null_sink_init(); + } +} + +void logger_init(const logger_config_t *config) +{ + log_assert(config); + + if (!config->enable) { + _logger_null_sink_init(); + } else { + _logger_with_sinks_init(config); + } + + core_log_bt_core_api_set(); +} + +void logger_fini() +{ + core_log_bt_fini(); +} \ No newline at end of file diff --git a/src/main/inject/logger.h b/src/main/inject/logger.h new file mode 100644 index 0000000..fcec261 --- /dev/null +++ b/src/main/inject/logger.h @@ -0,0 +1,10 @@ +#ifndef INJECT_LOGGER_H +#define INJECT_LOGGER_H + +#include "inject/logger-config.h" + +void logger_init(const logger_config_t *config); + +void logger_fini(); + +#endif \ No newline at end of file diff --git a/src/main/inject/main.c b/src/main/inject/main.c index d2b8249..6bf3b65 100644 --- a/src/main/inject/main.c +++ b/src/main/inject/main.c @@ -1,281 +1,65 @@ -#define LOG_MODULE "inject" +#define LOG_MODULE "main" -#include - -#include -#include -#include #include -#include -#include - -#include "cconfig/cconfig-util.h" -#include "cconfig/cmd.h" #include "core/boot.h" -#include "core/log-bt-ext.h" -#include "core/log-bt.h" -#include "core/log-sink-file.h" -#include "core/log-sink-list.h" -#include "core/log-sink-mutex.h" -#include "core/log-sink-std.h" -#include "core/thread-crt.h" -#include "iface-core/log.h" -#include "iface-core/thread.h" +#include "inject/inject-config.h" +#include "inject/inject.h" -#include "inject/debugger.h" -#include "inject/options.h" -#include "inject/version.h" - -#include "util/cmdline.h" -#include "util/debug.h" -#include "util/mem.h" -#include "util/os.h" -#include "util/signal.h" -#include "util/str.h" - -static void _inject_log_header() +static void _bootstrap_options(int argc, char **argv) { - log_info( - "\n" - " _ _ _ \n" - " (_)_ __ (_) ___ ___| |_ \n" - " | | '_ \\ | |/ _ \\/ __| __|\n" - " | | | | || | __/ (__| |_ \n" - " |_|_| |_|/ |\\___|\\___|\\__|\n" - " |__/ "); - - log_info( - "inject build date %s, gitrev %s", inject_build_date, inject_gitrev); + // TODO use options here + if (argc < 2) { + printf("Not enough args\n"); + exit(1); + } } -void _inject_log_init( - const char *log_file_path, enum core_log_bt_log_level level) +static void _bootstrap_config(const char *path, inject_config_t *config) { - if (log_file_path) { - core_log_bt_ext_init_async_with_stderr_and_file(log_file_path, false, true, 10); - } else { - core_log_bt_ext_init_async_with_stderr(); - } - - core_log_bt_core_api_set(); - - core_log_bt_level_set(level); -} - -static bool init_options(int argc, char **argv, struct options *options) -{ - options_init(options); - - if (argc < 3 || !options_read_cmdline(options, argc, argv)) { - options_print_usage(); - return false; - } - - return true; -} - -static bool verify_hook_dll_and_exec_args_and_count_hooks( - int argc, char **argv, uint32_t *hooks, uint32_t *exec_arg_pos) -{ - log_assert(argc >= 0); - log_assert(argv); - log_assert(hooks); - log_assert(exec_arg_pos); - - *hooks = 0; - *exec_arg_pos = 0; - - for (int i = 1; i < argc; i++) { - if (str_ends_with(argv[i], "dll")) { - (*hooks)++; - } else if (str_ends_with(argv[i], "exe")) { - *exec_arg_pos = i; - break; - } - } - - if (!(*hooks)) { - log_warning("ERROR: No Hook DLL(s) specified before executable"); - return false; - } - - if (!*exec_arg_pos) { - log_warning("ERROR: No executable specified"); - return false; - } - - log_misc("%d hook(s) dll detected", *hooks); - log_misc("Executable: %s", argv[*exec_arg_pos]); - - return true; -} - -static bool -verify_hook_dlls_exist(int argc, char **argv, uint32_t hook_dll_count) -{ - log_assert(argc >= 0); - log_assert(argv); - - char dll_path[MAX_PATH]; - DWORD dll_path_length; - - for (uint32_t i = 0; i < hook_dll_count; i++) { - char *iat_hook = strstr(argv[i + 1], "="); - - if (iat_hook) { - dll_path_length = - SearchPath(NULL, iat_hook + 1, NULL, MAX_PATH, dll_path, NULL); - } else { - dll_path_length = - SearchPath(NULL, argv[i + 1], NULL, MAX_PATH, dll_path, NULL); - } - - if (dll_path_length == 0) { - log_warning( - "ERROR: Hook DLL not found: %08x", - (unsigned int) GetLastError()); - - return false; - } - } - - return true; -} - -static bool inject_iat_hook_dlls(uint32_t hooks, char **argv) -{ - log_assert(argv); - - log_info("Injecting IAT hook DLLs..."); - - for (int i = 0; i < hooks; i++) { - char *iat_hook = strstr(argv[i + 1], "="); - - if (!iat_hook) - continue; - - *iat_hook = '\0'; - debugger_replace_dll_iat(argv[i + 1], iat_hook + 1); - *iat_hook = '='; - } - - return true; -} - -static bool inject_hook_dlls(uint32_t hooks, char **argv) -{ - log_assert(argv); - - log_info("Injecting hook DLLs..."); - - for (int i = 0; i < hooks; i++) { - char *iat_hook = strstr(argv[i + 1], "="); - - if (iat_hook) - continue; - - if (!debugger_inject_dll(argv[i + 1])) { - return false; - } - } - - return true; -} - -static void signal_shutdown_handler() -{ - debugger_finit(true); - core_log_bt_fini(); + inject_config_init(config); + inject_config_file_load(path, config); } int main(int argc, char **argv) { - struct options options; - uint32_t hooks; - uint32_t exec_arg_pos; - char *cmd_line; - bool local_debugger; + const char *config_path; + inject_config_t config; core_boot("inject"); - if (!init_options(argc, argv, &options)) { - goto init_options_fail; - } + config_path = argv[1]; - core_thread_crt_core_api_set(); + // TODO make configurable + // core_property_trace_log_enable(true); + // core_property_node_trace_log_enable(true); - // TODO expose log level - _inject_log_init( - strlen(options.log_file) > 0 ? options.log_file : NULL, - CORE_LOG_BT_LOG_LEVEL_MISC); + _bootstrap_options(argc, argv); + _bootstrap_config(config_path, &config); - _inject_log_header(); - os_version_log(); + inject_main(&config); - signal_exception_handler_init(); - // Cleanup remote process on CTRL+C - signal_register_shutdown_handler(signal_shutdown_handler); + return 0; - if (!verify_hook_dll_and_exec_args_and_count_hooks( - argc, argv, &hooks, &exec_arg_pos)) { - goto verify_fail; - } - if (!verify_hook_dlls_exist(argc, argv, hooks)) { - goto verify_2_fail; - } + // inject + // general configuration stuff + // --config (-c) inject-09.xml + // override any parameters with key-value params + // --param (-p) logger.level=asdf + + // some shortcuts for params commonly used + // --loglevel (-l) + // --logfile (-y) + // --remotedebugger (-r) + // --debugger (-d) + // --configslog (-s) + + // -- hook.dll... app.exe [hooks options...] - // buffer consumed by debugger_init - cmd_line = args_join(argc - exec_arg_pos, argv + exec_arg_pos); - - local_debugger = options.debug && !options.remote_debugger; - - if (!debugger_init(local_debugger, argv[exec_arg_pos], cmd_line)) { - goto debugger_init_fail; - } - - if (!inject_iat_hook_dlls(hooks, argv)) { - goto inject_hook_dlls_fail; - } - - if (!inject_hook_dlls(hooks, argv)) { - goto inject_hook_dlls_fail; - } - - // Execute this after injecting the DLLs. Some debuggers seem to crash if we - // attach the process before DLL injection (inject's local one doesn't - // crash). However, this means the remote debugger is missing out on all - // injected DLL loads, e.g. calls to DllMain - if (options.remote_debugger) { - if (!debugger_wait_for_remote_debugger()) { - goto debugger_wait_for_remote_debugger_fail; - } - } - - if (!debugger_resume_process()) { - goto debugger_resume_process_fail; - } - - debugger_wait_process_exit(); - - debugger_finit(false); - - core_log_bt_fini(); - - return EXIT_SUCCESS; - -debugger_resume_process_fail: -debugger_wait_for_remote_debugger_fail: -inject_hook_dlls_fail: - debugger_finit(true); - -debugger_init_fail: -verify_2_fail: -verify_fail: - core_log_bt_fini(); - -init_options_fail: - return EXIT_FAILURE; -} + // TODO + // - options + // - load config + // - apply overrides to config, hook dlls are added +} \ No newline at end of file