From d423058cbded51e956b5176f4104376f3e300166 Mon Sep 17 00:00:00 2001 From: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:04:01 +0000 Subject: [PATCH 1/6] APM3: Fix amdaemon breakage when Unity Doorstop is present (#76) Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/76 Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com> Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com> --- dist/apm3/launch.bat | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dist/apm3/launch.bat b/dist/apm3/launch.bat index 6bb1fd9..7b153e4 100644 --- a/dist/apm3/launch.bat +++ b/dist/apm3/launch.bat @@ -17,8 +17,10 @@ if exist %tmp%\SequenceSetting.json ( :BEGIN pushd %~dp0 +set DOORSTOP_DISABLE=TRUE qprocess amdaemon.exe > NUL IF %ERRORLEVEL% NEQ 0 start /min "AM Daemon" inject -d -k apm3hook.dll amdaemon.exe -c daemon_config\common.json daemon_config\server.json config_hook.json +set DOORSTOP_DISABLE= REM Add "-screen-fullscreen 0 -popupWindow" if you want to run in windowed mode inject -d -k apm3hook.dll APMV3System -logFile output_log.txt From 83a9a4942979e119e9789da091a1a8dbbd66f0b2 Mon Sep 17 00:00:00 2001 From: puniru Date: Fri, 22 Aug 2025 15:03:19 +0900 Subject: [PATCH 2/6] idac: allow changing up and down for custom boards --- games/idacio/config.c | 2 ++ games/idacio/config.h | 2 ++ games/idacio/di.c | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/games/idacio/config.c b/games/idacio/config.c index f862840..a325fa8 100644 --- a/games/idacio/config.c +++ b/games/idacio/config.c @@ -60,6 +60,8 @@ void idac_di_config_load(struct idac_di_config *cfg, const wchar_t *filename) cfg->view_chg = GetPrivateProfileIntW(L"dinput", L"viewChg", 0, filename); cfg->left = GetPrivateProfileIntW(L"dinput", L"left", 0, filename); cfg->right = GetPrivateProfileIntW(L"dinput", L"right", 0, filename); + cfg->up = GetPrivateProfileIntW(L"dinput", L"up", 0, filename); + cfg->down = GetPrivateProfileIntW(L"dinput", L"down", 0, filename); cfg->shift_dn = GetPrivateProfileIntW(L"dinput", L"shiftDn", 0, filename); cfg->shift_up = GetPrivateProfileIntW(L"dinput", L"shiftUp", 0, filename); diff --git a/games/idacio/config.h b/games/idacio/config.h index 88a1d6a..df70404 100644 --- a/games/idacio/config.h +++ b/games/idacio/config.h @@ -18,6 +18,8 @@ struct idac_di_config { uint8_t view_chg; uint8_t left; uint8_t right; + uint8_t up; + uint8_t down; uint8_t shift_dn; uint8_t shift_up; uint8_t gear[6]; diff --git a/games/idacio/di.c b/games/idacio/di.c index 8a52e9c..e676062 100644 --- a/games/idacio/di.c +++ b/games/idacio/di.c @@ -71,6 +71,8 @@ static uint8_t idac_di_view_chg; static uint8_t idac_di_start; static uint8_t idac_di_left; static uint8_t idac_di_right; +static uint8_t idac_di_up; +static uint8_t idac_di_down; static uint8_t idac_di_gear[6]; static bool idac_di_use_pedals; static bool idac_di_reverse_brake_axis; @@ -247,6 +249,16 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg) return E_INVALIDARG; } + if (cfg->up > 32) { + dprintf("Wheel: Invalid up button: %i\n", cfg->up); + return E_INVALIDARG; + } + + if (cfg->down > 32) { + dprintf("Wheel: Invalid down button: %i\n", cfg->down); + return E_INVALIDARG; + } + if (cfg->shift_dn > 32) { dprintf("Wheel: Invalid shift down button: %i\n", cfg->shift_dn); @@ -282,6 +294,8 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg) dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: Left button . . . . : %i\n", cfg->left); dprintf("Wheel: Right button . . . : %i\n", cfg->right); + dprintf("Wheel: Up button . . . . . : %i\n", cfg->up); + dprintf("Wheel: Down button . . . : %i\n", cfg->down); dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); dprintf("Wheel: Shift Up button . . : %i\n", cfg->shift_up); dprintf("Wheel: Reverse Brake Axis : %i\n", cfg->reverse_brake_axis); @@ -317,6 +331,8 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg) idac_di_view_chg = cfg->view_chg; idac_di_left = cfg->left; idac_di_right = cfg->right; + idac_di_up = cfg->up; + idac_di_down = cfg->down; idac_di_shift_dn = cfg->shift_dn; idac_di_shift_up = cfg->shift_up; idac_di_reverse_brake_axis = cfg->reverse_brake_axis; @@ -480,6 +496,14 @@ static void idac_di_get_buttons(uint8_t *gamebtn_out) gamebtn |= IDAC_IO_GAMEBTN_RIGHT; } + if (idac_di_up && state.st.rgbButtons[idac_di_up - 1]) { + gamebtn |= IDAC_IO_GAMEBTN_UP; + } + + if (idac_di_down && state.st.rgbButtons[idac_di_down - 1]) { + gamebtn |= IDAC_IO_GAMEBTN_DOWN; + } + *gamebtn_out = gamebtn; } From eb96660fdf6c4a49013628ad99e460ad76b04df3 Mon Sep 17 00:00:00 2001 From: puniru Date: Wed, 27 Aug 2025 13:30:00 +0900 Subject: [PATCH 3/6] idac: update config for up and down dpad buttons --- dist/idac/segatools.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dist/idac/segatools.ini b/dist/idac/segatools.ini index f7d0218..f596979 100644 --- a/dist/idac/segatools.ini +++ b/dist/idac/segatools.ini @@ -235,6 +235,9 @@ viewChg=2 ; This is not possible on most devices, so we set the left and right button again. left=7 right=8 +; Additional mapping for up and down buttons in DPad. +up=4 +down=3 ; Button mappings for the simulated six-speed shifter. shiftDn=6 shiftUp=5 From 19cd7cb8df31df96614699115f2d935dbcdc18c8 Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Sun, 5 Oct 2025 11:01:13 +0200 Subject: [PATCH 4/6] common: add OpenSSL Intel SHA ext hook --- common/platform/config.c | 12 ++++ common/platform/config.h | 2 + common/platform/meson.build | 2 + common/platform/openssl.c | 106 ++++++++++++++++++++++++++++++++++++ common/platform/openssl.h | 12 ++++ common/platform/platform.c | 7 +++ common/platform/platform.h | 2 + doc/config/common.md | 19 ++++++- 8 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 common/platform/openssl.c create mode 100644 common/platform/openssl.h diff --git a/common/platform/config.c b/common/platform/config.c index adc6451..7461430 100644 --- a/common/platform/config.c +++ b/common/platform/config.c @@ -23,6 +23,8 @@ #include "platform/platform.h" #include "platform/vfs.h" #include "platform/system.h" +#include "platform/openssl.h" + void platform_config_load(struct platform_config *cfg, const wchar_t *filename) { @@ -41,6 +43,7 @@ void platform_config_load(struct platform_config *cfg, const wchar_t *filename) nusec_config_load(&cfg->nusec, filename); vfs_config_load(&cfg->vfs, filename); system_config_load(&cfg->system, filename); + openssl_config_load(&cfg->openssl, filename); } void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename) @@ -362,3 +365,12 @@ void epay_config_load(struct epay_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"epay", L"enable", 1, filename); cfg->hook = GetPrivateProfileIntW(L"epay", L"hook", 1, filename); } + +void openssl_config_load(struct openssl_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"openssl", L"enable", 1, filename); + cfg->override = GetPrivateProfileIntW(L"openssl", L"override", 0, filename); +} diff --git a/common/platform/config.h b/common/platform/config.h index e945378..efad5a3 100644 --- a/common/platform/config.h +++ b/common/platform/config.h @@ -19,6 +19,7 @@ #include "platform/platform.h" #include "platform/vfs.h" #include "platform/system.h" +#include "platform/openssl.h" void platform_config_load( struct platform_config *cfg, @@ -36,3 +37,4 @@ void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename); void pcbid_config_load(struct pcbid_config *cfg, const wchar_t *filename); void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename); void system_config_load(struct system_config *cfg, const wchar_t *filename); +void openssl_config_load(struct openssl_config *cfg, const wchar_t *filename); diff --git a/common/platform/meson.build b/common/platform/meson.build index 0e54b6c..0e51ddd 100644 --- a/common/platform/meson.build +++ b/common/platform/meson.build @@ -38,5 +38,7 @@ platform_lib = static_library( 'vfs.h', 'system.c', 'system.h', + 'openssl.c', + 'openssl.h' ], ) diff --git a/common/platform/openssl.c b/common/platform/openssl.c new file mode 100644 index 0000000..e10843e --- /dev/null +++ b/common/platform/openssl.c @@ -0,0 +1,106 @@ +#include + +#include +#include +#include +#include +#include + +#include "hook/table.h" + +#include "hooklib/config.h" + +#include "platform/openssl.h" + +#include "util/dprintf.h" + +/* API hooks */ + +static char * __cdecl hook_getenv(const char *name); + +/* Link pointers */ + +static char * (__cdecl *next_getenv)(const char *name); + +static bool openssl_hook_initted; +static struct openssl_config openssl_config; + +static const struct hook_symbol openssl_hooks[] = { + { + .name = "getenv", + .patch = hook_getenv, + .link = (void **) &next_getenv + }, +}; + +int check_intel_sha_extension() { + int cpui[4] = {0}; + + __cpuid(cpui, 0); + int nIds_ = cpui[0]; + + char vendor[0x20] = {0}; + *((int*)vendor) = cpui[1]; + *((int*)(vendor + 4)) = cpui[3]; + *((int*)(vendor + 8)) = cpui[2]; + + // Intel CPU + int isIntel = (strcmp(vendor, "GenuineIntel") == 0); + + if (isIntel && nIds_ >= 7) { + __cpuidex(cpui, 7, 0); + // SHA extension + return (cpui[1] & (1 << 29)) != 0; + } + + return 0; +} + +HRESULT openssl_hook_init(const struct openssl_config *cfg) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + if (openssl_hook_initted) { + return S_FALSE; + } + + if (cfg->override) { + dprintf("OpenSSL: hook enabled.\n"); + } else if (check_intel_sha_extension()) { + dprintf("OpenSSL: Intel CPU SHA extension detected, hook enabled.\n"); + } else { + return S_FALSE; + } + + openssl_hook_initted = true; + + memcpy(&openssl_config, cfg, sizeof(*cfg)); + hook_table_apply( + NULL, + "msvcr110.dll", + openssl_hooks, + _countof(openssl_hooks) + ); + + return S_OK; +} + +static char * __cdecl hook_getenv(const char *name) +{ + /* Intercept OpenSSL CPU-cap override */ + if (name && strcmp(name, "OPENSSL_ia32cap") == 0) { + static char override[] = "~0x20000000"; + + dprintf("OpenSSL: Overriding OPENSSL_ia32cap -> %s\n", override); + + return override; + } + + char *real_val = next_getenv(name); + + return real_val; +} diff --git a/common/platform/openssl.h b/common/platform/openssl.h new file mode 100644 index 0000000..ebb2e46 --- /dev/null +++ b/common/platform/openssl.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +struct openssl_config { + bool enable; + bool override; +}; + +HRESULT openssl_hook_init(const struct openssl_config *cfg); diff --git a/common/platform/platform.c b/common/platform/platform.c index c882760..d1f380d 100644 --- a/common/platform/platform.c +++ b/common/platform/platform.c @@ -14,6 +14,7 @@ #include "platform/platform.h" #include "platform/vfs.h" #include "platform/system.h" +#include "platform/openssl.h" HRESULT platform_hook_init( const struct platform_config *cfg, @@ -94,5 +95,11 @@ HRESULT platform_hook_init( return hr; } + hr = openssl_hook_init(&cfg->openssl); + + if (FAILED(hr)) { + return hr; + } + return S_OK; } diff --git a/common/platform/platform.h b/common/platform/platform.h index 0b69f12..dc8dfc2 100644 --- a/common/platform/platform.h +++ b/common/platform/platform.h @@ -14,6 +14,7 @@ #include "platform/pcbid.h" #include "platform/vfs.h" #include "platform/system.h" +#include "platform/openssl.h" struct platform_config { struct amvideo_config amvideo; @@ -28,6 +29,7 @@ struct platform_config { struct nusec_config nusec; struct vfs_config vfs; struct system_config system; + struct openssl_config openssl; }; HRESULT platform_hook_init( diff --git a/doc/config/common.md b/doc/config/common.md index 68adfc4..d2eb385 100644 --- a/doc/config/common.md +++ b/doc/config/common.md @@ -663,4 +663,21 @@ Enables the Thinca emulation. This will allow you to enable E-Money on compatibl Default: `1` -Enables hooking of respective Thinca DLL functions to emulate the existence of E-Money. This cannot be used with a real E-Money server. \ No newline at end of file +Enables hooking of respective Thinca DLL functions to emulate the existence of E-Money. This cannot be used with a real E-Money server. + +## `[openssl]` + +Configure the OpenSSL SHA extension bug hook. + +### `enable` + +Default: `1` + +Enables the OpenSSL hook to fix the SHA extension bug on Intel CPUs. + +### `override` + +Default: `0` + +Enables the override to always hook the OpenSSL env variable. By default the +hook is only applied to Intel CPUs with the SHA extension present. From 6fe8744b0f03620233baef5e56d1a6b4db319c8f Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Sun, 12 Oct 2025 12:42:06 +0200 Subject: [PATCH 5/6] common: add OpenSSL patch comments, bump capnhook --- common/platform/openssl.c | 36 +++++++++++++++++++++++------------- subprojects/capnhook.wrap | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/common/platform/openssl.c b/common/platform/openssl.c index e10843e..8d52592 100644 --- a/common/platform/openssl.c +++ b/common/platform/openssl.c @@ -33,24 +33,33 @@ static const struct hook_symbol openssl_hooks[] = { }, }; -int check_intel_sha_extension() { +static int check_intel_sha_extension() { + int n_ids; + int is_intel; + char vendor[0x20] = {0}; int cpui[4] = {0}; + /* OpenSSL 1.0.2 beta to 1.0.2k contain bugs that crash "AM Daemon" due to + bad SHA values on processors with the SHA extensions, such as Intel 10th + gen CPUs or higher. + + Credits: kagaminehaku */ + __cpuid(cpui, 0); - int nIds_ = cpui[0]; + n_ids = cpui[0]; - char vendor[0x20] = {0}; - *((int*)vendor) = cpui[1]; - *((int*)(vendor + 4)) = cpui[3]; - *((int*)(vendor + 8)) = cpui[2]; + *((int *) vendor) = cpui[1]; + *((int *) (vendor + 4)) = cpui[3]; + *((int *) (vendor + 8)) = cpui[2]; - // Intel CPU - int isIntel = (strcmp(vendor, "GenuineIntel") == 0); + /* Check that the CPU vendor is Intel */ + is_intel = (strcmp(vendor, "GenuineIntel") == 0); - if (isIntel && nIds_ >= 7) { + if (is_intel && n_ids >= 7) { __cpuidex(cpui, 7, 0); - // SHA extension - return (cpui[1] & (1 << 29)) != 0; + + /* SHA extensions supported */ + return (cpui[1] & (1 << 29)) != 0; } return 0; @@ -70,7 +79,7 @@ HRESULT openssl_hook_init(const struct openssl_config *cfg) if (cfg->override) { dprintf("OpenSSL: hook enabled.\n"); - } else if (check_intel_sha_extension()) { + } else if (!check_intel_sha_extension()) { dprintf("OpenSSL: Intel CPU SHA extension detected, hook enabled.\n"); } else { return S_FALSE; @@ -91,7 +100,8 @@ HRESULT openssl_hook_init(const struct openssl_config *cfg) static char * __cdecl hook_getenv(const char *name) { - /* Intercept OpenSSL CPU-cap override */ + /* Overrides OpenSSL's Intel CPU identification. This disables the OpenSSL + code check for SHA extensions. */ if (name && strcmp(name, "OPENSSL_ia32cap") == 0) { static char override[] = "~0x20000000"; diff --git a/subprojects/capnhook.wrap b/subprojects/capnhook.wrap index 4c349ab..8c6369d 100644 --- a/subprojects/capnhook.wrap +++ b/subprojects/capnhook.wrap @@ -1,4 +1,4 @@ [wrap-git] directory = capnhook url = https://gitea.tendokyu.moe/TeamTofuShop/capnhook -revision = dafd0aa336ab80ba87a82370a9dc95a3389ef5e5 +revision = 252465b23f51f36206e614190496663425f4ee5d From 146550e68cf7185e9a7ed1db7a81d4f262a3fd48 Mon Sep 17 00:00:00 2001 From: goldfisher11 Date: Sun, 2 Nov 2025 08:47:18 +0000 Subject: [PATCH 6/6] spike_develop (#80) Co-authored-by: goldfisher11 Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/80 Reviewed-by: Dniel97 Co-authored-by: goldfisher11 Co-committed-by: goldfisher11 --- common/hooklib/spike.c | 185 ++++++++++++++++++++++++++++++---------- common/hooklib/spike.h | 1 + games/idachook/config.c | 7 ++ games/idachook/indrun.c | 14 +++ games/idachook/indrun.h | 1 + 5 files changed, 163 insertions(+), 45 deletions(-) diff --git a/common/hooklib/spike.c b/common/hooklib/spike.c index 007c6ef..a66057d 100644 --- a/common/hooklib/spike.c +++ b/common/hooklib/spike.c @@ -1,19 +1,16 @@ #include - +#include #include #include #include #include #include #include - +#include #include "hook/pe.h" - #include "hooklib/spike.h" - #include "util/dprintf.h" -static void spike_hook_read_config(const wchar_t *spike_file); /* Spike functions. Their "style" is named after the libc function they bear the closest resemblance to. */ @@ -92,37 +89,127 @@ static void spike_fn_perror( OutputDebugStringA(line); } -/* Spike inserters */ - -static void spike_insert_jmp(ptrdiff_t rva, void *proc) +BOOL is_current_module_x64() { - uint8_t *base; - uint8_t *target; - uint8_t *func_ptr; - uint32_t delta; + HMODULE hModule = GetModuleHandleW(NULL); + MODULEINFO moduleInfo = {0}; + if (!GetModuleInformation(GetCurrentProcess(), hModule, &moduleInfo, + sizeof(moduleInfo))) { + return FALSE; + } - base = (uint8_t *) GetModuleHandleW(NULL); + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleInfo.lpBaseOfDll; + PIMAGE_NT_HEADERS ntHeaders = + (PIMAGE_NT_HEADERS)((BYTE *)moduleInfo.lpBaseOfDll + + dosHeader->e_lfanew); - target = base + rva; - func_ptr = proc; - delta = func_ptr - target - 4; /* -4: EIP delta, after end of target insn */ - - pe_patch(target, &delta, sizeof(delta)); + if (ntHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { + return TRUE; + } else if (ntHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { + return FALSE; + } else { + return FALSE; + } } -static void spike_insert_ptr(ptrdiff_t rva, void *ptr) -{ +BOOL is_valid_rva(LPCWSTR module_name, uintptr_t rva) { + HMODULE module_base = GetModuleHandleW(module_name); + if (!module_base) { + return false; + } + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)module_base; + PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)((uint8_t *)module_base + dos_header->e_lfanew); + DWORD module_size = nt_headers->OptionalHeader.SizeOfImage; + return (rva < module_size); +} + +/* Spike inserters */ + +static void spike_insert_jmp(LPCWSTR module_name, uintptr_t rva, void *proc) { + uint8_t *base = (uint8_t *)GetModuleHandleW(module_name); + if ( !(is_valid_rva(module_name, rva))) { + dprintf("spike: Invalid RVA 0x%llx for module %S\n", (unsigned long long)rva, module_name); + return; + } + uint8_t *target = base + rva; + uint8_t *func_ptr = (uint8_t *)proc; + uintptr_t target_addr = (uintptr_t)target; + uintptr_t func_addr = (uintptr_t)func_ptr; + + if (is_current_module_x64()) { + uint64_t relativeOffset = (uint64_t)(func_addr - target_addr - 5); + uint8_t absoluteJump[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0}; + memcpy(absoluteJump + 2, &func_ptr, 8); + pe_patch(target, absoluteJump, sizeof(absoluteJump)); + } + else { + uint32_t jumpOffset = (uint32_t)(func_addr - target_addr - 5); + uint8_t relativeJump[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; + memcpy(relativeJump + 1, &jumpOffset, 4); + pe_patch(target, relativeJump, sizeof(relativeJump)); + } +} + +static void spike_insert_ptr(LPCWSTR module_name, uintptr_t rva, void *ptr) { uint8_t *base; uint8_t *target; - - base = (uint8_t *) GetModuleHandleW(NULL); + if (!(is_valid_rva(module_name, rva))) { + dprintf("spike: Invalid RVA 0x%llx for module %S\n", (unsigned long long)rva, module_name); + return; + } + base = (uint8_t *)GetModuleHandleW(module_name); target = base + rva; pe_patch(target, &ptr, sizeof(ptr)); } -static void spike_insert_log_levels(ptrdiff_t rva, size_t count) -{ +static void spike_insert_nop(LPCWSTR module_name, uintptr_t rva, size_t count) { + uint8_t *base; + uint8_t *target; + uint8_t *value; + if (!(is_valid_rva(module_name, rva))) { + dprintf("spike: Invalid RVA 0x%llx for module %S\n", (unsigned long long)rva, module_name); + return; + } + base = (uint8_t *)GetModuleHandleW(module_name); + target = base + rva; + value = (uint8_t *)malloc(count); + if (value == NULL) { + return; + } + memset(value, 0x90, count); + + HRESULT ret = pe_patch(target, value, count); + free(value); +} + +static void spike_insert_data(LPCWSTR module_name, uintptr_t rva, const wchar_t *patch_str) { + uint8_t patch_data[32]; + int length = 0; + int byte_count = wcslen(patch_str) / 2; + uint8_t *base; + uint8_t *target; + if (!(is_valid_rva(module_name, rva))) { + dprintf("spike: Invalid RVA 0x%llx for module %S\n", (unsigned long long)rva, module_name); + return; + } + base = (uint8_t *)GetModuleHandleW(module_name); + target = base + rva; + for (int i = 0; i < byte_count; i++) { + unsigned int temp; + if (swscanf(patch_str + i * 2, L"%2x", &temp) == 1) { + patch_data[i] = (unsigned char)temp; + length++; + } else { + break; + } + } + + pe_patch(target, patch_data, length); +} + +static void spike_insert_log_levels(uintptr_t rva, size_t count) { uint8_t *base; uint32_t *levels; size_t i; @@ -155,7 +242,7 @@ void spike_hook_init(const wchar_t *ini_file) basename = slash + 1; } else { basename = module; - } + } /* Check our INI file to see if any spikes are configured for this EXE. Normally we separate out config reading into a separate module... */ @@ -170,19 +257,19 @@ void spike_hook_init(const wchar_t *ini_file) if (path[0] != L'\0') { dprintf("Spiking %S using config from %S\n", basename, path); - spike_hook_read_config(path); + spike_hook_read_config(basename, path); } } -static void spike_hook_read_config(const wchar_t *spike_file) -{ +void spike_hook_read_config(const wchar_t *target, const wchar_t *spike_file) { int match; int count; - int rva; - char line[80]; + unsigned long long rva; + char line[256]; + wchar_t filename[MAX_PATH]; + wchar_t patch_data[64]; char *ret; FILE *f; - f = _wfopen(spike_file, L"r"); if (f == NULL) { @@ -202,46 +289,54 @@ static void spike_hook_read_config(const wchar_t *spike_file) continue; } - match = sscanf(line, "levels %i %i", &rva, &count); + match = sscanf(line, "levels %lli %i", &rva, &count); if (match == 2) { - spike_insert_log_levels((ptrdiff_t) rva, count); + spike_insert_log_levels((uintptr_t)rva, count); } - match = sscanf(line, "j_vprintf %i", &rva); + match = sscanf(line, "j_vprintf %lli", &rva); if (match == 1) { - spike_insert_jmp((ptrdiff_t) rva, spike_fn_vprintf); + spike_insert_jmp(target, (uintptr_t)rva, spike_fn_vprintf); } - match = sscanf(line, "j_vwprintf %i", &rva); + match = sscanf(line, "j_vwprintf %lli", &rva); if (match == 1) { - spike_insert_jmp((ptrdiff_t) rva, spike_fn_vwprintf); + spike_insert_jmp(target, (uintptr_t)rva, spike_fn_vwprintf); } - match = sscanf(line, "j_printf %i", &rva); + match = sscanf(line, "j_printf %lli", &rva); if (match == 1) { - spike_insert_jmp((ptrdiff_t) rva, spike_fn_printf); + spike_insert_jmp(target, (uintptr_t)rva, spike_fn_printf); } - match = sscanf(line, "j_puts %i", &rva); + match = sscanf(line, "j_puts %lli", &rva); if (match == 1) { - spike_insert_jmp((ptrdiff_t) rva, spike_fn_puts); + spike_insert_jmp(target, (uintptr_t)rva, spike_fn_puts); } - match = sscanf(line, "j_perror %i", &rva); + match = sscanf(line, "j_perror %lli", &rva); if (match == 1) { - spike_insert_jmp((ptrdiff_t) rva, spike_fn_perror); + spike_insert_jmp(target, (uintptr_t)rva, spike_fn_perror); } - match = sscanf(line, "c_fputs %i", &rva); /* c == "callback" */ + match = sscanf(line, "c_fputs %lli", &rva); /* c == "callback" */ if (match == 1) { - spike_insert_ptr((ptrdiff_t) rva, spike_fn_fputs); + spike_insert_ptr(target, (uintptr_t)rva, spike_fn_fputs); + } + match = sscanf(line, "patch_memory_nop %255ls %lli %i", filename, &rva, &count); + if (match == 3 && (_wcsicmp(filename, target) == 0)) { + spike_insert_nop((LPCWSTR)filename, (uintptr_t)rva, count); + } + match = sscanf(line, "patch_memory_data %255ls %lli %64ls", filename, &rva, patch_data); + if (match == 3 && (_wcsicmp(filename, target) == 0)) { + spike_insert_data((LPCWSTR)filename, (uintptr_t)rva, patch_data); } } diff --git a/common/hooklib/spike.h b/common/hooklib/spike.h index 779d22f..d986293 100644 --- a/common/hooklib/spike.h +++ b/common/hooklib/spike.h @@ -3,3 +3,4 @@ #include void spike_hook_init(const wchar_t *ini_file); +void spike_hook_read_config(const wchar_t *target, const wchar_t *spike_file); diff --git a/games/idachook/config.c b/games/idachook/config.c index a009f2a..b96f1bf 100644 --- a/games/idachook/config.c +++ b/games/idachook/config.c @@ -76,6 +76,13 @@ void indrun_config_load( assert(filename != NULL); cfg->enable = GetPrivateProfileIntW(L"indrun", L"enable", 1, filename); + GetPrivateProfileStringW( + L"indrun", + L"spike", + L"", + cfg->patch_file, + _countof(cfg->patch_file), + filename); } void idac_hook_config_load( diff --git a/games/idachook/indrun.c b/games/idachook/indrun.c index 758a677..4c6884d 100644 --- a/games/idachook/indrun.c +++ b/games/idachook/indrun.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "hook/table.h" @@ -9,11 +10,14 @@ #include "util/dprintf.h" #include "indrun.h" +#include "hooklib/spike.h" static const wchar_t *target_modules[] = { L"IndRun.dll", }; +static wchar_t spike_file[MAX_PATH] = L""; + static const size_t target_modules_len = _countof(target_modules); static void dll_hook_insert_hooks(HMODULE target); @@ -88,11 +92,17 @@ static const struct hook_symbol indrun_kernel32_syms[] = { void indrun_hook_init(struct indrun_config *cfg) { assert(cfg != NULL); + wchar_t full_path[MAX_PATH]; if (!cfg->enable) { return; } + if (wcscmp(cfg->patch_file, L"") != 0) { + GetCurrentDirectoryW(MAX_PATH, full_path); + swprintf_s(spike_file, MAX_PATH, L"%ls\\%ls", full_path, cfg->patch_file); + } + dprintf("IDAC: Hooks enabled.\n"); // GameProject-Win64-Shipping.exe hooks @@ -176,6 +186,10 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name) dll_hook_insert_hooks(result); app_hook_insert_hooks(result); + + if (wcscmp(spike_file, L"") != 0) { + spike_hook_read_config(target_module, spike_file); + } } } diff --git a/games/idachook/indrun.h b/games/idachook/indrun.h index 4605285..83650b2 100644 --- a/games/idachook/indrun.h +++ b/games/idachook/indrun.h @@ -4,6 +4,7 @@ struct indrun_config { bool enable; + wchar_t patch_file[MAX_PATH]; }; void indrun_hook_init(struct indrun_config *cfg);