commit 58aa9be073eca200bf03d38f74c5d17860b61b66 Author: Sean Du Date: Sat Jun 20 05:55:47 2026 +0800 Add DMM version.dll proxy hook for CGSS Signed-off-by: Sean Du diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba01f37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +cgss-dmm-hook.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..26f1d42 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/minhook"] + path = deps/minhook + url = https://github.com/TsudaKageyu/minhook.git +[submodule "deps/rapidjson"] + path = deps/rapidjson + url = https://github.com/Tencent/rapidjson.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b9b6a49 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.20) + +project(cgss_http_hook LANGUAGES C CXX RC ASM_MASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +add_library(version SHARED + src/proxy.cpp + src/hook.cpp + src/log.cpp + src/il2cpp_symbols.cpp + src/version.rc + src/version.def + deps/minhook/src/buffer.c + deps/minhook/src/hook.c + deps/minhook/src/trampoline.c + deps/minhook/src/hde/hde64.c +) + +target_include_directories(version PRIVATE + src + deps/minhook/include + deps/minhook/src + deps/minhook/src/hde +) + +target_compile_definitions(version PRIVATE + NOMINMAX + WIN32_LEAN_AND_MEAN +) + +target_link_libraries(version PRIVATE + user32 + kernel32 +) + +set_target_properties(version PROPERTIES + OUTPUT_NAME "version" + PREFIX "" +) + +if(MINGW) + target_compile_options(version PRIVATE + "$<$:-fno-exceptions>" + "$<$:-fno-rtti>" + ) + target_link_options(version PRIVATE + "-static-libgcc" + "-static-libstdc++" + ) +endif() diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0f1c505 --- /dev/null +++ b/Makefile @@ -0,0 +1,71 @@ +TARGET := build/version.dll +BUILD_DIR := build +OBJ_DIR := $(BUILD_DIR)/obj +BUILD ?= release + +CC := x86_64-w64-mingw32-gcc +CXX := x86_64-w64-mingw32-g++ +WINDRES := x86_64-w64-mingw32-windres + +INCLUDES := -Isrc -Ideps/minhook/include -Ideps/minhook/src -Ideps/minhook/src/hde +DEFINES := -DNOMINMAX -DWIN32_LEAN_AND_MEAN +CFLAGS := -std=gnu11 $(DEFINES) $(INCLUDES) +CXXFLAGS := -std=gnu++20 -fno-exceptions -fno-rtti $(DEFINES) $(INCLUDES) +LDFLAGS := -shared -static-libgcc -static-libstdc++ +LDLIBS := -luser32 -lkernel32 + +ifeq ($(BUILD),debug) + DEFINES += -D_DEBUG + CFLAGS += -O0 -g + CXXFLAGS += -O0 -g +else + CFLAGS += -O2 -ffunction-sections -fdata-sections + CXXFLAGS += -O2 -ffunction-sections -fdata-sections + LDFLAGS += -Wl,--gc-sections -s +endif + +C_SRCS := \ + deps/minhook/src/buffer.c \ + deps/minhook/src/hook.c \ + deps/minhook/src/trampoline.c \ + deps/minhook/src/hde/hde64.c + +CXX_SRCS := \ + src/proxy.cpp \ + src/hook.cpp \ + src/log.cpp \ + src/il2cpp_symbols.cpp + +RC_SRCS := src/version.rc +DEF_FILE := src/version.def + +C_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(C_SRCS)) +CXX_OBJS := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(CXX_SRCS)) +RC_OBJS := $(patsubst %.rc,$(OBJ_DIR)/%.res,$(RC_SRCS)) +OBJS := $(C_OBJS) $(CXX_OBJS) $(RC_OBJS) + +.PHONY: all release debug clean + +all: $(TARGET) +release: $(TARGET) +debug: + $(MAKE) BUILD=debug + +$(TARGET): $(OBJS) $(DEF_FILE) + @mkdir -p $(dir $@) + $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(DEF_FILE) $(LDLIBS) + +$(OBJ_DIR)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +$(OBJ_DIR)/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +$(OBJ_DIR)/%.res: %.rc + @mkdir -p $(dir $@) + $(WINDRES) $(DEFINES) $(INCLUDES) $< -O coff -o $@ + +clean: + rm -rf $(BUILD_DIR) diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7ccf72 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# cgss-http-hook + +面向 Windows / DMM 版 CGSS 客户端的最小 `version.dll` 代理工程。 + +**用途** +- 转发 `UnityPlayer.dll` 实际导入的少量 `version.dll` 导出函数 +- 等待 IL2CPP 运行时初始化稳定 +- 运行时 hook `Stage.NetworkUtil.GetSchemeType()`,强制返回 `0` + +**目录结构** +- `src/`:代理、日志、IL2CPP 符号解析、hook 逻辑 +- `deps/minhook/`:内置的 MinHook 源码 + +**说明** +- 本项目不依赖 `Il2CppDumper` 或 `script.json` +- `GameAssembly.dll` 出现时,IL2CPP runtime 可能还不能安全访问,因此 hook 线程会额外等待一小段时间 +- 运行时日志会写到游戏目录下的 `cgss-http-hook.log` +- 生成的 DLL 包含 Windows 版本资源,定义在 `src/version.rc` + +**构建** +```sh +cmake -S . -B build -G Ninja -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \ + -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ +cmake --build build +``` + +**输出** +- `build/version.dll` + +**使用方法** +1. 将 `build/version.dll` 复制到 `imascgstage.exe` 同目录 +2. 启动游戏 +3. 如果 hook 未生效,查看 `cgss-http-hook.log` diff --git a/deps/minhook b/deps/minhook new file mode 160000 index 0000000..d94c64d --- /dev/null +++ b/deps/minhook @@ -0,0 +1 @@ +Subproject commit d94c64d32ea37bc4f5ee47d580709f70c6fb6080 diff --git a/deps/rapidjson b/deps/rapidjson new file mode 160000 index 0000000..24b5e7a --- /dev/null +++ b/deps/rapidjson @@ -0,0 +1 @@ +Subproject commit 24b5e7a8b27f42fa16b96fc70aade9106cf7102f diff --git a/src/hook.cpp b/src/hook.cpp new file mode 100644 index 0000000..955f354 --- /dev/null +++ b/src/hook.cpp @@ -0,0 +1,121 @@ +#include "stdinclude.hpp" + +namespace { + using GetSchemeTypeFn = int (*)(); + constexpr int kHttpSchemeType = 0; + constexpr DWORD kHookPollIntervalMs = 500; + constexpr DWORD kGameAssemblySettleDelayMs = 5000; + constexpr int kHookPollMaxAttempts = 600; + + volatile LONG g_hook_installed = 0; + GetSchemeTypeFn g_orig_get_scheme_type = nullptr; + volatile LONG g_logged_gameassembly = 0; + volatile LONG g_exports_state = 0; + ULONGLONG g_gameassembly_seen_tick = 0; + volatile LONG g_scheme_hook_called = 0; + + int get_scheme_type_hook() { + if (InterlockedCompareExchange(&g_scheme_hook_called, 1, 0) == 0) { + hook_log("[cgss-http-hook] GetSchemeType hook invoked"); + } + return kHttpSchemeType; + } + + void patch_custom_preference_scheme() { + auto custom_preference = il2cpp_symbols::get_class( + "Assembly-CSharp.dll", "Cute", "CustomPreference" + ); + if (!custom_preference) { + hook_log("[cgss-http-hook] failed to resolve Cute.CustomPreference"); + return; + } + + if (il2cpp_symbols::set_static_int_field(custom_preference, "_schemeType", kHttpSchemeType)) { + hook_log("[cgss-http-hook] patched Cute.CustomPreference._schemeType=0"); + } else { + hook_log("[cgss-http-hook] failed to patch Cute.CustomPreference._schemeType"); + } + } + + void try_install_hook() { + if (InterlockedCompareExchange(&g_hook_installed, 1, 1) != 0) { + return; + } + + auto game_assembly = GetModuleHandleW(L"GameAssembly.dll"); + if (!game_assembly) { + return; + } + if (InterlockedCompareExchange(&g_logged_gameassembly, 1, 0) == 0) { + hook_log("[cgss-http-hook] GameAssembly.dll loaded"); + g_gameassembly_seen_tick = GetTickCount64(); + } + if (g_gameassembly_seen_tick != 0 && + GetTickCount64() - g_gameassembly_seen_tick < kGameAssemblySettleDelayMs) { + return; + } + + il2cpp_symbols::init(game_assembly); + if (!il2cpp_symbols::ready()) { + if (InterlockedCompareExchange(&g_exports_state, 1, 0) == 0) { + hook_log("[cgss-http-hook] il2cpp exports not ready"); + } + return; + } + LONG prev_exports_state = InterlockedExchange(&g_exports_state, 2); + if (prev_exports_state != 2) { + hook_log("[cgss-http-hook] il2cpp exports ready"); + } + + patch_custom_preference_scheme(); + + auto addr = il2cpp_symbols::get_method_pointer( + "Assembly-CSharp.dll", "Stage", "NetworkUtil", "GetSchemeType", 0 + ); + if (!addr) { + hook_log("[cgss-http-hook] failed to resolve Stage.NetworkUtil.GetSchemeType"); + return; + } + + auto mh_status = MH_Initialize(); + if (mh_status != MH_OK && mh_status != MH_ERROR_ALREADY_INITIALIZED) { + hook_log("[cgss-http-hook] MH_Initialize failed"); + return; + } + + auto create_status = MH_CreateHook( + reinterpret_cast(addr), + reinterpret_cast(&get_scheme_type_hook), + reinterpret_cast(&g_orig_get_scheme_type) + ); + if (create_status != MH_OK && create_status != MH_ERROR_ALREADY_CREATED) { + hook_log("[cgss-http-hook] MH_CreateHook failed"); + return; + } + + auto enable_status = MH_EnableHook(reinterpret_cast(addr)); + if (enable_status != MH_OK && enable_status != MH_ERROR_ENABLED) { + hook_log("[cgss-http-hook] MH_EnableHook failed"); + return; + } + + InterlockedExchange(&g_hook_installed, 1); + hook_log("[cgss-http-hook] hooked Stage.NetworkUtil.GetSchemeType"); + } + + DWORD WINAPI init_thread(void*) { + for (int i = 0; i < kHookPollMaxAttempts; ++i) { + try_install_hook(); + if (InterlockedCompareExchange(&g_hook_installed, 1, 1) != 0) { + return 0; + } + Sleep(kHookPollIntervalMs); + } + hook_log("[cgss-http-hook] timed out waiting for hook target"); + return 0; + } +} + +void start_hook_thread() { + CreateThread(nullptr, 0, init_thread, nullptr, 0, nullptr); +} diff --git a/src/il2cpp_symbols.cpp b/src/il2cpp_symbols.cpp new file mode 100644 index 0000000..16f7556 --- /dev/null +++ b/src/il2cpp_symbols.cpp @@ -0,0 +1,133 @@ +#include "stdinclude.hpp" + +il2cpp_domain_get_t il2cpp_symbols::il2cpp_domain_get = nullptr; +il2cpp_domain_assembly_open_t il2cpp_symbols::il2cpp_domain_assembly_open = nullptr; +il2cpp_assembly_get_image_t il2cpp_symbols::il2cpp_assembly_get_image = nullptr; +il2cpp_class_from_name_t il2cpp_symbols::il2cpp_class_from_name = nullptr; +il2cpp_class_get_method_from_name_t il2cpp_symbols::il2cpp_class_get_method_from_name = nullptr; +il2cpp_class_get_methods_t il2cpp_symbols::il2cpp_class_get_methods = nullptr; +il2cpp_class_get_field_from_name_t il2cpp_symbols::il2cpp_class_get_field_from_name = nullptr; +il2cpp_method_get_name_t il2cpp_symbols::il2cpp_method_get_name = nullptr; +il2cpp_thread_attach_t il2cpp_symbols::il2cpp_thread_attach = nullptr; +il2cpp_thread_current_t il2cpp_symbols::il2cpp_thread_current = nullptr; +il2cpp_runtime_class_init_t il2cpp_symbols::il2cpp_runtime_class_init = nullptr; +il2cpp_field_get_flags_t il2cpp_symbols::il2cpp_field_get_flags = nullptr; +il2cpp_field_static_set_value_t il2cpp_symbols::il2cpp_field_static_set_value = nullptr; +void* il2cpp_symbols::il2cpp_domain = nullptr; + +namespace { + template + void resolve(HMODULE module, const char* name, T& out) { + out = reinterpret_cast(GetProcAddress(module, name)); + } +} + +void il2cpp_symbols::init(HMODULE game_module) { + resolve(game_module, "il2cpp_domain_get", il2cpp_domain_get); + resolve(game_module, "il2cpp_domain_assembly_open", il2cpp_domain_assembly_open); + resolve(game_module, "il2cpp_assembly_get_image", il2cpp_assembly_get_image); + resolve(game_module, "il2cpp_class_from_name", il2cpp_class_from_name); + resolve(game_module, "il2cpp_class_get_method_from_name", il2cpp_class_get_method_from_name); + resolve(game_module, "il2cpp_class_get_methods", il2cpp_class_get_methods); + resolve(game_module, "il2cpp_class_get_field_from_name", il2cpp_class_get_field_from_name); + resolve(game_module, "il2cpp_method_get_name", il2cpp_method_get_name); + resolve(game_module, "il2cpp_thread_attach", il2cpp_thread_attach); + resolve(game_module, "il2cpp_thread_current", il2cpp_thread_current); + resolve(game_module, "il2cpp_runtime_class_init", il2cpp_runtime_class_init); + resolve(game_module, "il2cpp_field_get_flags", il2cpp_field_get_flags); + resolve(game_module, "il2cpp_field_static_set_value", il2cpp_field_static_set_value); +} + +bool il2cpp_symbols::ready() { + return il2cpp_domain_get && il2cpp_domain_assembly_open && il2cpp_assembly_get_image && + il2cpp_class_from_name && il2cpp_class_get_method_from_name && + il2cpp_class_get_field_from_name && il2cpp_thread_attach && + il2cpp_runtime_class_init && il2cpp_field_get_flags && il2cpp_field_static_set_value; +} + +bool il2cpp_symbols::domain_ready() { + if (!il2cpp_domain_get) { + return false; + } + if (!il2cpp_domain) { + il2cpp_domain = il2cpp_domain_get(); + } + return il2cpp_domain != nullptr; +} + +bool il2cpp_symbols::attach_thread() { + if (!ready()) { + return false; + } + if (!domain_ready()) { + return false; + } + if (il2cpp_thread_current && il2cpp_thread_current()) { + return true; + } + il2cpp_thread_attach(il2cpp_domain); + return true; +} + +void* il2cpp_symbols::get_class(const char* assembly_name, const char* namespaze, const char* klass_name) { + if (!attach_thread()) { + hook_log("[cgss-http-hook] il2cpp domain not ready"); + return nullptr; + } + if (!ready() || !il2cpp_domain) { + return nullptr; + } + + auto assembly = il2cpp_domain_assembly_open(il2cpp_domain, assembly_name); + if (!assembly) { + hook_log("[cgss-http-hook] assembly not found"); + return nullptr; + } + auto image = il2cpp_assembly_get_image(assembly); + if (!image) { + hook_log("[cgss-http-hook] image not found"); + return nullptr; + } + auto klass = il2cpp_class_from_name(image, namespaze, klass_name); + if (!klass) { + hook_log("[cgss-http-hook] class not found"); + return nullptr; + } + return klass; +} + +bool il2cpp_symbols::set_static_int_field(void* klass, const char* field_name, int value) { + if (!klass || !field_name || !attach_thread()) { + return false; + } + + il2cpp_runtime_class_init(klass); + auto field = il2cpp_class_get_field_from_name(klass, field_name); + if (!field) { + hook_log("[cgss-http-hook] field not found"); + return false; + } + + auto flags = il2cpp_field_get_flags(field); + if ((flags & 0x10U) == 0) { + hook_log("[cgss-http-hook] field is not static"); + return false; + } + + il2cpp_field_static_set_value(field, &value); + return true; +} + +uintptr_t il2cpp_symbols::get_method_pointer(const char* assembly_name, const char* namespaze, + const char* klass_name, const char* method_name, int args_count) { + auto klass = get_class(assembly_name, namespaze, klass_name); + if (!klass) { + return 0; + } + auto method = il2cpp_class_get_method_from_name(klass, method_name, args_count); + if (!method) { + hook_log("[cgss-http-hook] method not found"); + return 0; + } + return method->methodPointer; +} diff --git a/src/il2cpp_symbols.hpp b/src/il2cpp_symbols.hpp new file mode 100644 index 0000000..62dbefc --- /dev/null +++ b/src/il2cpp_symbols.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +struct MethodInfo; +struct FieldInfo; + +typedef void* (*il2cpp_domain_get_t)(); +typedef void* (*il2cpp_domain_assembly_open_t)(void* domain, const char* name); +typedef void* (*il2cpp_assembly_get_image_t)(void* assembly); +typedef void* (*il2cpp_class_from_name_t)(void* image, const char* namespaze, const char* name); +typedef MethodInfo* (*il2cpp_class_get_method_from_name_t)(void* klass, const char* name, int argsCount); +typedef MethodInfo* (*il2cpp_class_get_methods_t)(void* klass, void** iter); +typedef FieldInfo* (*il2cpp_class_get_field_from_name_t)(void* klass, const char* name); +typedef const char* (*il2cpp_method_get_name_t)(MethodInfo* method); +typedef void (*il2cpp_thread_attach_t)(void* domain); +typedef void* (*il2cpp_thread_current_t)(); +typedef void (*il2cpp_runtime_class_init_t)(void* klass); +typedef uint32_t (*il2cpp_field_get_flags_t)(FieldInfo* field); +typedef void (*il2cpp_field_static_set_value_t)(FieldInfo* field, void* value); + +struct MethodInfo +{ + uintptr_t methodPointer; + uintptr_t invoker_method; + const char* name; + uintptr_t klass; + const void* return_type; + const void* parameters; + uintptr_t methodDefinition; + uintptr_t genericContainer; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t is_generic : 1; + uint8_t is_inflated : 1; + uint8_t wrapper_type : 1; + uint8_t is_marshaled_from_native : 1; +}; + +namespace il2cpp_symbols { + extern il2cpp_domain_get_t il2cpp_domain_get; + extern il2cpp_domain_assembly_open_t il2cpp_domain_assembly_open; + extern il2cpp_assembly_get_image_t il2cpp_assembly_get_image; + extern il2cpp_class_from_name_t il2cpp_class_from_name; + extern il2cpp_class_get_method_from_name_t il2cpp_class_get_method_from_name; + extern il2cpp_class_get_methods_t il2cpp_class_get_methods; + extern il2cpp_class_get_field_from_name_t il2cpp_class_get_field_from_name; + extern il2cpp_method_get_name_t il2cpp_method_get_name; + extern il2cpp_thread_attach_t il2cpp_thread_attach; + extern il2cpp_thread_current_t il2cpp_thread_current; + extern il2cpp_runtime_class_init_t il2cpp_runtime_class_init; + extern il2cpp_field_get_flags_t il2cpp_field_get_flags; + extern il2cpp_field_static_set_value_t il2cpp_field_static_set_value; + extern void* il2cpp_domain; + + void init(HMODULE game_module); + bool ready(); + bool domain_ready(); + bool attach_thread(); + void* get_class(const char* assembly_name, const char* namespaze, const char* klass_name); + bool set_static_int_field(void* klass, const char* field_name, int value); + uintptr_t get_method_pointer(const char* assembly_name, const char* namespaze, + const char* klass_name, const char* method_name, int args_count); +} diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 0000000..fa06662 --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,55 @@ +#include "stdinclude.hpp" + +namespace { + HANDLE g_log_file = INVALID_HANDLE_VALUE; + + HANDLE get_log_file() { + if (g_log_file != INVALID_HANDLE_VALUE) { + return g_log_file; + } + + char path[MAX_PATH] = {}; + DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH); + if (len == 0 || len >= MAX_PATH) { + return INVALID_HANDLE_VALUE; + } + + char* last_sep = path; + for (char* p = path; *p != '\0'; ++p) { + if (*p == '\\' || *p == '/') { + last_sep = p; + } + } + *last_sep = '\0'; + lstrcatA(path, "\\cgss-http-hook.log"); + + g_log_file = CreateFileA( + path, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + return g_log_file; + } +} + +void hook_log(const char* message) { + if (!message) { + return; + } + + OutputDebugStringA(message); + OutputDebugStringA("\r\n"); + + HANDLE log_file = get_log_file(); + if (log_file == INVALID_HANDLE_VALUE) { + return; + } + + DWORD ignored = 0; + WriteFile(log_file, message, lstrlenA(message), &ignored, nullptr); + WriteFile(log_file, "\r\n", 2, &ignored, nullptr); +} diff --git a/src/proxy.cpp b/src/proxy.cpp new file mode 100644 index 0000000..381cde3 --- /dev/null +++ b/src/proxy.cpp @@ -0,0 +1,85 @@ +#include "stdinclude.hpp" + +extern "C" { + void* GetFileVersionInfoA_Original = nullptr; + void* GetFileVersionInfoSizeA_Original = nullptr; + void* VerQueryValueA_Original = nullptr; + + BOOL WINAPI GetFileVersionInfoA_EXPORT(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData) { + using Fn = BOOL(WINAPI*)(LPCSTR, DWORD, DWORD, LPVOID); + if (!GetFileVersionInfoA_Original) { + SetLastError(ERROR_PROC_NOT_FOUND); + return FALSE; + } + return reinterpret_cast(GetFileVersionInfoA_Original)(lptstrFilename, dwHandle, dwLen, lpData); + } + + DWORD WINAPI GetFileVersionInfoSizeA_EXPORT(LPCSTR lptstrFilename, LPDWORD lpdwHandle) { + using Fn = DWORD(WINAPI*)(LPCSTR, LPDWORD); + if (!GetFileVersionInfoSizeA_Original) { + SetLastError(ERROR_PROC_NOT_FOUND); + return 0; + } + return reinterpret_cast(GetFileVersionInfoSizeA_Original)(lptstrFilename, lpdwHandle); + } + + BOOL WINAPI VerQueryValueA_EXPORT(LPCVOID pBlock, LPCSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen) { + using Fn = BOOL(WINAPI*)(LPCVOID, LPCSTR, LPVOID*, PUINT); + if (!VerQueryValueA_Original) { + SetLastError(ERROR_PROC_NOT_FOUND); + return FALSE; + } + return reinterpret_cast(VerQueryValueA_Original)(pBlock, lpSubBlock, lplpBuffer, puLen); + } +} + +void start_hook_thread(); + +namespace { + constexpr DWORD kProxyInitDelayMs = 1000; + HMODULE g_original = nullptr; + + void load_original_version_dll() { + if (g_original) { + return; + } + + char system_dir[MAX_PATH] = {}; + GetSystemDirectoryA(system_dir, MAX_PATH); + char path[MAX_PATH] = {}; + lstrcpynA(path, system_dir, MAX_PATH); + lstrcatA(path, "\\version.dll"); + + g_original = LoadLibraryA(path); + if (!g_original) { + hook_log("[cgss-http-hook] failed to load original version.dll"); + return; + } + + GetFileVersionInfoA_Original = reinterpret_cast(GetProcAddress(g_original, "GetFileVersionInfoA")); + GetFileVersionInfoSizeA_Original = reinterpret_cast(GetProcAddress(g_original, "GetFileVersionInfoSizeA")); + VerQueryValueA_Original = reinterpret_cast(GetProcAddress(g_original, "VerQueryValueA")); + + if (!GetFileVersionInfoA_Original || !GetFileVersionInfoSizeA_Original || !VerQueryValueA_Original) { + hook_log("[cgss-http-hook] failed to resolve version exports"); + return; + } + + hook_log("[cgss-http-hook] version.dll initialized"); + } + + DWORD WINAPI init_thread(void*) { + Sleep(kProxyInitDelayMs); + load_original_version_dll(); + start_hook_thread(); + return 0; + } +} + +BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls(module); + CreateThread(nullptr, 0, init_thread, nullptr, 0, nullptr); + } + return TRUE; +} diff --git a/src/stdinclude.hpp b/src/stdinclude.hpp new file mode 100644 index 0000000..9dcf882 --- /dev/null +++ b/src/stdinclude.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +#include + +#include "il2cpp_symbols.hpp" + +void hook_log(const char* message); diff --git a/src/version.def b/src/version.def new file mode 100644 index 0000000..b9e0cdf --- /dev/null +++ b/src/version.def @@ -0,0 +1,4 @@ +EXPORTS + GetFileVersionInfoA=GetFileVersionInfoA_EXPORT + GetFileVersionInfoSizeA=GetFileVersionInfoSizeA_EXPORT + VerQueryValueA=VerQueryValueA_EXPORT diff --git a/src/version.rc b/src/version.rc new file mode 100644 index 0000000..2ee7d94 --- /dev/null +++ b/src/version.rc @@ -0,0 +1,38 @@ +#include + +#define VER_FILEVERSION 1,0,0,0 +#define VER_FILEVERSION_STR "1.0.0.0\0" +#define VER_PRODUCTVERSION 1,0,0,0 +#define VER_PRODUCTVERSION_STR "1.0.0.0\0" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VER_FILEVERSION + PRODUCTVERSION VER_PRODUCTVERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "YumeMichi\0" + VALUE "FileDescription", "cgss-http-hook version.dll proxy\0" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "cgss-http-hook\0" + VALUE "OriginalFilename", "version.dll\0" + VALUE "ProductName", "cgss-http-hook\0" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END +END