Merge branch 'develop'

This commit is contained in:
Dniel97
2025-11-04 11:02:28 +01:00
19 changed files with 368 additions and 47 deletions
+140 -45
View File
@@ -1,19 +1,16 @@
#include <windows.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <psapi.h>
#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);
}
}
+1
View File
@@ -3,3 +3,4 @@
#include <stddef.h>
void spike_hook_init(const wchar_t *ini_file);
void spike_hook_read_config(const wchar_t *target, const wchar_t *spike_file);
+12
View File
@@ -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);
}
+2
View File
@@ -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);
+2
View File
@@ -38,5 +38,7 @@ platform_lib = static_library(
'vfs.h',
'system.c',
'system.h',
'openssl.c',
'openssl.h'
],
)
+116
View File
@@ -0,0 +1,116 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <intrin.h>
#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
},
};
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);
n_ids = cpui[0];
*((int *) vendor) = cpui[1];
*((int *) (vendor + 4)) = cpui[3];
*((int *) (vendor + 8)) = cpui[2];
/* Check that the CPU vendor is Intel */
is_intel = (strcmp(vendor, "GenuineIntel") == 0);
if (is_intel && n_ids >= 7) {
__cpuidex(cpui, 7, 0);
/* SHA extensions supported */
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)
{
/* 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";
dprintf("OpenSSL: Overriding OPENSSL_ia32cap -> %s\n", override);
return override;
}
char *real_val = next_getenv(name);
return real_val;
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct openssl_config {
bool enable;
bool override;
};
HRESULT openssl_hook_init(const struct openssl_config *cfg);
+7
View File
@@ -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;
}
+2
View File
@@ -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(
+2
View File
@@ -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
+3
View File
@@ -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
+18 -1
View File
@@ -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.
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.
+7
View File
@@ -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(
+14
View File
@@ -1,6 +1,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#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);
}
}
}
+1
View File
@@ -4,6 +4,7 @@
struct indrun_config {
bool enable;
wchar_t patch_file[MAX_PATH];
};
void indrun_hook_init(struct indrun_config *cfg);
+2
View File
@@ -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);
+2
View File
@@ -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];
+24
View File
@@ -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;
}
+1 -1
View File
@@ -1,4 +1,4 @@
[wrap-git]
directory = capnhook
url = https://gitea.tendokyu.moe/TeamTofuShop/capnhook
revision = dafd0aa336ab80ba87a82370a9dc95a3389ef5e5
revision = 252465b23f51f36206e614190496663425f4ee5d