Skip to content

Commit

Permalink
Add the ability to override the Unity's boot.config file path (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
aldelaro5 authored May 28, 2024
1 parent b9f9d8f commit 2b37810
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 6 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ All Doorstop arguments start with `--doorstop-` and always contain an argument.
* `string` = any sequence of characters and numbers. Wrap into `"`s if the string contains spaces

| Argument | Description |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| ------------------------------------------------- |------------------------------------------------------------------------------------------------------|
| `--doorstop-enabled bool` | Enable or disable Doorstop. |
| `--doorstop-redirect-output-log bool` | *Only on Windows*: If `true` Unity's output log is redirected to `<current folder>\output_log.txt` |
| `--doorstop-target-assembly string` | Path to the assembly to load and execute. |
| `--doorstop-boot-config-override string` | Overrides the boot.config file path. |
| `--doorstop-mono-dll-search-path-override string` | Overrides default Mono DLL search path |
| `--doorstop-mono-debug-enabled bool` | If true, Mono debugger server will be enabled |
| `--doorstop-mono-debug-suspend bool` | Whether to suspend the game execution until the debugger is attached. |
Expand Down
8 changes: 8 additions & 0 deletions assets/nix/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ enabled="1"
# NOTE: The entrypoint must be of format `static void Doorstop.Entrypoint.Start()`
target_assembly="Doorstop.dll"

# Overrides the default boot.config file path
boot_config_override=

# If enabled, DOORSTOP_DISABLE env var value is ignored
# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS
ignore_disable_switch="0"
Expand Down Expand Up @@ -197,6 +200,10 @@ while :; do
target_assembly="$2"
shift
;;
--doorstop-boot-config-override)
boot_config_override="$2"
shift
;;
--doorstop-mono-dll-search-path-override)
dll_search_path_override="$2"
shift
Expand Down Expand Up @@ -234,6 +241,7 @@ done
# Move variables to environment
export DOORSTOP_ENABLED="$enabled"
export DOORSTOP_TARGET_ASSEMBLY="$target_assembly"
export DOORSTOP_BOOT_CONFIG_OVERRIDE="$boot_config_override"
export DOORSTOP_IGNORE_DISABLED_ENV="$ignore_disable_switch"
export DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE="$dll_search_path_override"
export DOORSTOP_MONO_DEBUG_ENABLED="$debug_enable"
Expand Down
3 changes: 3 additions & 0 deletions assets/windows/doorstop_config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ target_assembly=Doorstop.dll
# If true, Unity's output log is redirected to <current folder>\output_log.txt
redirect_output_log=false

# Overrides the default boot.config file path
boot_config_override=

# If enabled, DOORSTOP_DISABLE env var value is ignored
# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS
ignore_disable_switch=false
Expand Down
7 changes: 4 additions & 3 deletions src/bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,14 @@ void il2cpp_doorstop_bootstrap() {
strcat(app_paths_env, config.clr_corlib_dir);
strcat(app_paths_env, PATH_SEP);
strcat(app_paths_env, target_dir);
char *app_paths_env_n = narrow(app_paths_env);
const char *app_paths_env_n = narrow(app_paths_env);

LOG("App path: %s", app_path);
LOG("Target dir: %s", target_dir);
LOG("Target name: %s", target_name);
LOG("APP_PATHS: %s", app_paths_env);

char *props = "APP_PATHS";
const char *props = "APP_PATHS";

setenv(TEXT("DOORSTOP_INITIALIZED"), TEXT("TRUE"), TRUE);
setenv(TEXT("DOORSTOP_INVOKE_DLL_PATH"), config.target_assembly, TRUE);
Expand All @@ -293,7 +293,8 @@ void il2cpp_doorstop_bootstrap() {

void (*startup)() = NULL;
result = coreclr.create_delegate(host, domain_id, target_name_n,
"Doorstop.Entrypoint", "Start", &startup);
"Doorstop.Entrypoint", "Start",
(void **)&startup);
if (result != 0) {
LOG("Failed to get entrypoint delegate: 0x%08x", result);
return;
Expand Down
2 changes: 2 additions & 0 deletions src/config/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void cleanup_config() {
}

FREE_NON_NULL(config.target_assembly);
FREE_NON_NULL(config.boot_config_override);
FREE_NON_NULL(config.mono_dll_search_path_override);
FREE_NON_NULL(config.clr_corlib_dir);
FREE_NON_NULL(config.clr_runtime_coreclr_path);
Expand All @@ -27,6 +28,7 @@ void init_config_defaults() {
config.mono_debug_suspend = FALSE;
config.mono_debug_address = NULL;
config.target_assembly = NULL;
config.boot_config_override = NULL;
config.mono_dll_search_path_override = NULL;
config.clr_corlib_dir = NULL;
config.clr_runtime_coreclr_path = NULL;
Expand Down
6 changes: 6 additions & 0 deletions src/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ typedef struct {
*/
char_t *target_assembly;

/**
* @brief Path to a custom boot.config file to use. If enabled, this file
* takes precedence over the default one in the Data folder.
*/
char_t *boot_config_override;

/**
* @brief Path to use as the main DLL search path. If enabled, this folder
* takes precedence over the default Managed folder.
Expand Down
3 changes: 2 additions & 1 deletion src/nix/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void get_env_bool(const char_t *name, bool_t *target) {
}
}

void try_get_env(const char_t *name, const char_t *def, char_t **target) {
void try_get_env(const char_t *name, char_t *def, char_t **target) {
char_t *value = getenv(name);
if (value != NULL && strlen(value) > 0) {
*target = strdup(value);
Expand All @@ -37,6 +37,7 @@ void load_config() {
try_get_env("DOORSTOP_MONO_DEBUG_ADDRESS", TEXT("127.0.0.1:10000"),
&config.mono_debug_address);
get_env_path("DOORSTOP_TARGET_ASSEMBLY", &config.target_assembly);
get_env_path("DOORSTOP_BOOT_CONFIG_OVERRIDE", &config.boot_config_override);
try_get_env("DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE", TEXT(""),
&config.mono_dll_search_path_override);
get_env_path("DOORSTOP_CLR_RUNTIME_CORECLR_PATH",
Expand Down
53 changes: 52 additions & 1 deletion src/nix/entrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,31 @@ int fclose_hook(FILE *stream) {
return fclose(stream);
}

char_t *default_boot_config_path = NULL;
#if !defined(__APPLE__)
FILE *fopen64_hook(char *filename, char *mode) {
char *actual_file_name = filename;

if (strcmp(filename, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

return fopen64(actual_file_name, mode);
}
#endif

FILE *fopen_hook(char *filename, char *mode) {
char *actual_file_name = filename;

if (strcmp(filename, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

return fopen(actual_file_name, mode);
}

int dup2_hook(int od, int nd) {
// Newer versions of Unity redirect stdout to player.log, we don't want
// that
Expand All @@ -80,7 +105,8 @@ __attribute__((constructor)) void doorstop_ctor() {

void *unity_player = plthook_handle_by_name("UnityPlayer");

if (unity_player && PLTHOOK_OPEN_BY_HANDLE_OR_ADDRESS(&hook, unity_player) == 0) {
if (unity_player &&
PLTHOOK_OPEN_BY_HANDLE_OR_ADDRESS(&hook, unity_player) == 0) {
LOG("Found UnityPlayer, hooking into it instead");
} else if (plthook_open(&hook, NULL) != 0) {
LOG("Failed to open current process PLT! Cannot run Doorstop! "
Expand All @@ -94,6 +120,31 @@ __attribute__((constructor)) void doorstop_ctor() {
printf("Failed to hook dlsym, ignoring it. Error: %s\n",
plthook_error());

if (config.boot_config_override) {
if (file_exists(config.boot_config_override)) {
default_boot_config_path = calloc(MAX_PATH, sizeof(char_t));
memset(default_boot_config_path, 0, MAX_PATH * sizeof(char_t));
strcat(default_boot_config_path, get_working_dir());
strcat(default_boot_config_path, TEXT("/"));
strcat(default_boot_config_path,
get_file_name(program_path(), FALSE));
strcat(default_boot_config_path, TEXT("_Data/boot.config"));

#if !defined(__APPLE__)
if (plthook_replace(hook, "fopen64", &fopen64_hook, NULL) != 0)
printf("Failed to hook fopen64, ignoring it. Error: %s\n",
plthook_error());
#endif
if (plthook_replace(hook, "fopen", &fopen_hook, NULL) != 0)
printf("Failed to hook fopen, ignoring it. Error: %s\n",
plthook_error());
} else {
LOG("The boot.config file won't be overriden because the provided "
"one does not exist: %s",
config.boot_config_override);
}
}

if (plthook_replace(hook, "fclose", &fclose_hook, NULL) != 0)
printf("Failed to hook fclose, ignoring it. Error: %s\n",
plthook_error());
Expand Down
2 changes: 2 additions & 0 deletions src/util/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ typedef int bool_t;

#define TRUE 1
#define FALSE 0
#ifndef NULL
#define NULL 0
#endif

#define TEXT(text) text

Expand Down
4 changes: 4 additions & 0 deletions src/windows/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ static inline void init_config_file() {
TEXT("false"), &config.redirect_output_log);
load_path_file(config_path, TEXT("General"), TEXT("target_assembly"),
DEFAULT_TARGET_ASSEMBLY, &config.target_assembly);
load_path_file(config_path, TEXT("General"), TEXT("boot_config_override"),
NULL, &config.boot_config_override);

load_str_file(config_path, TEXT("UnityMono"),
TEXT("dll_search_path_override"), TEXT(""),
Expand Down Expand Up @@ -144,6 +146,8 @@ static inline void init_cmd_args() {
config.redirect_output_log, load_bool_argv);
PARSE_ARG(TEXT("--doorstop-target-assembly"), config.target_assembly,
load_path_argv);
PARSE_ARG(TEXT("--doorstop-boot-config-override"),
config.boot_config_override, load_path_argv);

PARSE_ARG(TEXT("--doorstop-mono-dll-search-path-override"),
config.mono_dll_search_path_override, load_path_argv);
Expand Down
85 changes: 85 additions & 0 deletions src/windows/entrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ bool_t fix_cwd() {
#define LOG_FILE_CMD_END L"\\output_log.txt\""
#define LOG_FILE_CMD_END_LEN STR_LEN(LOG_FILE_CMD_END)

char_t *default_boot_config_path = NULL;

char_t *new_cmdline_args = NULL;
char *new_cmdline_args_narrow = NULL;

Expand All @@ -63,6 +65,70 @@ bool_t WINAPI close_handle_hook(void *handle) {
return CloseHandle(handle);
}

HANDLE WINAPI create_file_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile) {
LPCWSTR actual_file_name = lpFileName;

char_t *normalised_path = calloc(strlen(lpFileName) + 1, sizeof(char_t));
memset(normalised_path, 0, (strlen(lpFileName) + 1) * sizeof(char_t));
strcpy(normalised_path, lpFileName);
for (size_t i = 0; i < strlen(normalised_path); i++) {
if (normalised_path[i] == L'/') {
normalised_path[i] = L'\\';
}
}

if (strcmpi(normalised_path, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

free(normalised_path);

return CreateFileW(actual_file_name, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}

HANDLE WINAPI create_file_hook_narrow(
void *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
void *actual_file_name = lpFileName;

char_t *widened_filename = widen(lpFileName);
char_t *normalised_path =
calloc(strlen(widened_filename) + 1, sizeof(char_t));
memset(normalised_path, 0, (strlen(widened_filename) + 1) * sizeof(char_t));
strcpy(normalised_path, widened_filename);
free(widened_filename);

for (size_t i = 0; i < strlen(normalised_path); i++) {
if (normalised_path[i] == L'/') {
normalised_path[i] = L'\\';
}
}

if (strcmpi(normalised_path, default_boot_config_path) == 0) {
char *narrowed_boot_config_override =
narrow(config.boot_config_override);
memcpy(actual_file_name, narrowed_boot_config_override,
strlen(config.boot_config_override));
free(narrowed_boot_config_override);
LOG("Overriding boot.config to %s", actual_file_name);
}

free(normalised_path);

return CreateFileA(actual_file_name, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}

void capture_mono_path(void *handle) {
char_t *result;
get_module_path(handle, &result, NULL, 0);
Expand Down Expand Up @@ -146,6 +212,25 @@ void inject(DoorstopPaths const *paths) {

HOOK_SYS(target_module, GetProcAddress, get_proc_address_detour);
HOOK_SYS(target_module, CloseHandle, close_handle_hook);
if (config.boot_config_override) {
if (file_exists(config.boot_config_override)) {
default_boot_config_path = calloc(MAX_PATH, sizeof(char_t));
memset(default_boot_config_path, 0, MAX_PATH * sizeof(char_t));
strcat(default_boot_config_path, get_working_dir());
strcat(default_boot_config_path, TEXT("\\"));
strcat(default_boot_config_path,
get_file_name(program_path(), FALSE));
strcat(default_boot_config_path, TEXT("_Data\\boot.config"));

HOOK_SYS(target_module, CreateFileW, create_file_hook);
HOOK_SYS(target_module, CreateFileA, create_file_hook_narrow);
} else {
LOG("The boot.config file won't be overriden because the provided "
"one does not exist: %s",
config.boot_config_override);
}
}

HOOK_SYS(app_module, GetCommandLineW, get_command_line_hook);
HOOK_SYS(app_module, GetCommandLineA, get_command_line_hook_narrow);

Expand Down

0 comments on commit 2b37810

Please sign in to comment.