Add DMM version.dll proxy hook for CGSS

Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
2026-06-20 08:08:30 +08:00
commit 58aa9be073
15 changed files with 683 additions and 0 deletions
+121
View File
@@ -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<void*>(addr),
reinterpret_cast<void*>(&get_scheme_type_hook),
reinterpret_cast<void**>(&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<void*>(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);
}
+133
View File
@@ -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 <typename T>
void resolve(HMODULE module, const char* name, T& out) {
out = reinterpret_cast<T>(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;
}
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <stdint.h>
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);
}
+55
View File
@@ -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);
}
+85
View File
@@ -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<Fn>(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<Fn>(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<Fn>(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<void*>(GetProcAddress(g_original, "GetFileVersionInfoA"));
GetFileVersionInfoSizeA_Original = reinterpret_cast<void*>(GetProcAddress(g_original, "GetFileVersionInfoSizeA"));
VerQueryValueA_Original = reinterpret_cast<void*>(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;
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <windows.h>
#include <cstring>
#include <MinHook.h>
#include "il2cpp_symbols.hpp"
void hook_log(const char* message);
+4
View File
@@ -0,0 +1,4 @@
EXPORTS
GetFileVersionInfoA=GetFileVersionInfoA_EXPORT
GetFileVersionInfoSizeA=GetFileVersionInfoSizeA_EXPORT
VerQueryValueA=VerQueryValueA_EXPORT
+38
View File
@@ -0,0 +1,38 @@
#include <winver.h>
#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