mirror of
https://github.com/Jacobwasbeast/LegacyWeaveLoader.git
synced 2026-07-15 23:28:08 +00:00
Use exact decorated symbol names verified against Minecraft.Client.pdb
Verified all hook targets exist in the compiled game's PDB: - ?MinecraftWorld_RunStaticCtors@@YAXXZ - ?tick@Minecraft@@QEAAX_N0@Z - ?init@Minecraft@@QEAAXXZ - ?ExitGame@CConsoleMinecraftApp@@UEAAXXZ (replaces destroy which is inlined) Minecraft::destroy/run_end/stop are not public symbols (likely inlined), so the shutdown hook now targets CConsoleMinecraftApp::ExitGame instead.
This commit is contained in:
@@ -25,7 +25,7 @@ class Program
|
||||
|
||||
if (string.IsNullOrEmpty(config.GameExePath) || !File.Exists(config.GameExePath))
|
||||
{
|
||||
Console.Write("Enter path to MinecraftLegacy.exe: ");
|
||||
Console.Write("Enter path to Minecraft.Client.exe: ");
|
||||
string? input = Console.ReadLine()?.Trim().Trim('"');
|
||||
|
||||
if (string.IsNullOrEmpty(input) || !File.Exists(input))
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
namespace GameHooks
|
||||
{
|
||||
RunStaticCtors_fn Original_RunStaticCtors = nullptr;
|
||||
MinecraftTick_fn Original_MinecraftTick = nullptr;
|
||||
MinecraftInit_fn Original_MinecraftInit = nullptr;
|
||||
MinecraftDestroy_fn Original_MinecraftDestroy = nullptr;
|
||||
MinecraftTick_fn Original_MinecraftTick = nullptr;
|
||||
MinecraftInit_fn Original_MinecraftInit = nullptr;
|
||||
ExitGame_fn Original_ExitGame = nullptr;
|
||||
|
||||
void Hooked_RunStaticCtors()
|
||||
{
|
||||
printf("[LegacyForge] RunStaticCtors hook fired -- calling PreInit\n");
|
||||
printf("[LegacyForge] Hook: RunStaticCtors -- calling PreInit\n");
|
||||
DotNetHost::CallPreInit();
|
||||
|
||||
Original_RunStaticCtors();
|
||||
|
||||
printf("[LegacyForge] RunStaticCtors complete -- calling Init\n");
|
||||
printf("[LegacyForge] Hook: RunStaticCtors complete -- calling Init\n");
|
||||
DotNetHost::CallInit();
|
||||
}
|
||||
|
||||
@@ -34,15 +34,15 @@ namespace GameHooks
|
||||
{
|
||||
Original_MinecraftInit(thisPtr);
|
||||
|
||||
printf("[LegacyForge] Minecraft::init complete -- calling PostInit\n");
|
||||
printf("[LegacyForge] Hook: Minecraft::init complete -- calling PostInit\n");
|
||||
DotNetHost::CallPostInit();
|
||||
}
|
||||
|
||||
void __fastcall Hooked_MinecraftDestroy(void* thisPtr)
|
||||
void __fastcall Hooked_ExitGame(void* thisPtr)
|
||||
{
|
||||
printf("[LegacyForge] Minecraft::destroy -- calling Shutdown\n");
|
||||
printf("[LegacyForge] Hook: ExitGame -- calling Shutdown\n");
|
||||
DotNetHost::CallShutdown();
|
||||
|
||||
Original_MinecraftDestroy(thisPtr);
|
||||
Original_ExitGame(thisPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
/// Function pointer typedefs matching the game's function signatures.
|
||||
/// On x64 MSVC, member functions use __fastcall-like convention (this in rcx).
|
||||
/// Function pointer typedefs matching the game's actual function signatures.
|
||||
/// x64 MSVC uses __fastcall-like convention (this in rcx, args in rdx/r8/r9).
|
||||
///
|
||||
/// Verified against Minecraft.Client.pdb:
|
||||
/// ?MinecraftWorld_RunStaticCtors@@YAXXZ -- void __cdecl ()
|
||||
/// ?tick@Minecraft@@QEAAX_N0@Z -- void __thiscall (bool, bool)
|
||||
/// ?init@Minecraft@@QEAAXXZ -- void __thiscall ()
|
||||
/// ?ExitGame@CConsoleMinecraftApp@@UEAAXXZ -- void __thiscall ()
|
||||
|
||||
typedef void (*RunStaticCtors_fn)();
|
||||
typedef void (__fastcall *MinecraftTick_fn)(void* thisPtr, bool bFirst, bool bUpdateTextures);
|
||||
typedef void (__fastcall *MinecraftInit_fn)(void* thisPtr);
|
||||
typedef void (__fastcall *MinecraftDestroy_fn)(void* thisPtr);
|
||||
typedef void (__fastcall *ExitGame_fn)(void* thisPtr);
|
||||
|
||||
namespace GameHooks
|
||||
{
|
||||
extern RunStaticCtors_fn Original_RunStaticCtors;
|
||||
extern MinecraftTick_fn Original_MinecraftTick;
|
||||
extern MinecraftInit_fn Original_MinecraftInit;
|
||||
extern MinecraftDestroy_fn Original_MinecraftDestroy;
|
||||
extern MinecraftTick_fn Original_MinecraftTick;
|
||||
extern MinecraftInit_fn Original_MinecraftInit;
|
||||
extern ExitGame_fn Original_ExitGame;
|
||||
|
||||
void Hooked_RunStaticCtors();
|
||||
void __fastcall Hooked_MinecraftTick(void* thisPtr, bool bFirst, bool bUpdateTextures);
|
||||
void __fastcall Hooked_MinecraftInit(void* thisPtr);
|
||||
void __fastcall Hooked_MinecraftDestroy(void* thisPtr);
|
||||
void __fastcall Hooked_ExitGame(void* thisPtr);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ bool HookManager::Install(const SymbolResolver& symbols)
|
||||
printf("[LegacyForge] Failed to hook RunStaticCtors\n");
|
||||
return false;
|
||||
}
|
||||
printf("[LegacyForge] Hooked RunStaticCtors\n");
|
||||
}
|
||||
|
||||
// Hook Minecraft::tick
|
||||
@@ -34,6 +35,7 @@ bool HookManager::Install(const SymbolResolver& symbols)
|
||||
printf("[LegacyForge] Failed to hook Minecraft::tick\n");
|
||||
return false;
|
||||
}
|
||||
printf("[LegacyForge] Hooked Minecraft::tick\n");
|
||||
}
|
||||
|
||||
// Hook Minecraft::init
|
||||
@@ -46,17 +48,21 @@ bool HookManager::Install(const SymbolResolver& symbols)
|
||||
printf("[LegacyForge] Failed to hook Minecraft::init\n");
|
||||
return false;
|
||||
}
|
||||
printf("[LegacyForge] Hooked Minecraft::init\n");
|
||||
}
|
||||
|
||||
// Hook Minecraft::destroy
|
||||
if (symbols.pMinecraftDestroy)
|
||||
// Hook CConsoleMinecraftApp::ExitGame (optional -- for graceful shutdown)
|
||||
if (symbols.pExitGame)
|
||||
{
|
||||
if (MH_CreateHook(symbols.pMinecraftDestroy,
|
||||
reinterpret_cast<void*>(&GameHooks::Hooked_MinecraftDestroy),
|
||||
reinterpret_cast<void**>(&GameHooks::Original_MinecraftDestroy)) != MH_OK)
|
||||
if (MH_CreateHook(symbols.pExitGame,
|
||||
reinterpret_cast<void*>(&GameHooks::Hooked_ExitGame),
|
||||
reinterpret_cast<void**>(&GameHooks::Original_ExitGame)) != MH_OK)
|
||||
{
|
||||
printf("[LegacyForge] Failed to hook Minecraft::destroy\n");
|
||||
return false;
|
||||
printf("[LegacyForge] Warning: Failed to hook ExitGame (shutdown hook unavailable)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[LegacyForge] Hooked ExitGame\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,22 @@
|
||||
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
// Exact MSVC-decorated symbol names from the game's PDB.
|
||||
// These are verified against the actual Minecraft.Client.pdb.
|
||||
static const char* SYM_RUN_STATIC_CTORS = "?MinecraftWorld_RunStaticCtors@@YAXXZ";
|
||||
static const char* SYM_MINECRAFT_TICK = "?tick@Minecraft@@QEAAX_N0@Z";
|
||||
static const char* SYM_MINECRAFT_INIT = "?init@Minecraft@@QEAAXXZ";
|
||||
static const char* SYM_EXIT_GAME = "?ExitGame@CConsoleMinecraftApp@@UEAAXXZ";
|
||||
|
||||
bool SymbolResolver::Initialize()
|
||||
{
|
||||
m_process = GetCurrentProcess();
|
||||
|
||||
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_DEBUG);
|
||||
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_DEBUG);
|
||||
|
||||
if (!SymInitialize(m_process, nullptr, TRUE))
|
||||
{
|
||||
printf("[LegacyForge] SymInitialize failed (error %lu)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,7 +27,7 @@ bool SymbolResolver::Initialize()
|
||||
return true;
|
||||
}
|
||||
|
||||
void* SymbolResolver::Resolve(const char* functionName)
|
||||
void* SymbolResolver::Resolve(const char* decoratedName)
|
||||
{
|
||||
if (!m_initialized) return nullptr;
|
||||
|
||||
@@ -30,85 +38,36 @@ void* SymbolResolver::Resolve(const char* functionName)
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
if (SymFromName(m_process, functionName, symbol))
|
||||
if (SymFromName(m_process, decoratedName, symbol))
|
||||
{
|
||||
return reinterpret_cast<void*>(symbol->Address);
|
||||
}
|
||||
|
||||
printf("[LegacyForge] SymFromName failed for '%s' (error %lu)\n", decoratedName, GetLastError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct EnumContext
|
||||
{
|
||||
const char* targetName;
|
||||
void* result;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK EnumSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
|
||||
{
|
||||
auto* ctx = static_cast<EnumContext*>(UserContext);
|
||||
if (strstr(pSymInfo->Name, ctx->targetName) != nullptr)
|
||||
{
|
||||
ctx->result = reinterpret_cast<void*>(pSymInfo->Address);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool SymbolResolver::ResolveGameFunctions()
|
||||
{
|
||||
// MinecraftWorld_RunStaticCtors is a free function (not mangled in complex ways)
|
||||
pRunStaticCtors = Resolve("MinecraftWorld_RunStaticCtors");
|
||||
if (!pRunStaticCtors)
|
||||
{
|
||||
// Try with wildcard enumeration
|
||||
EnumContext ctx = { "MinecraftWorld_RunStaticCtors", nullptr };
|
||||
SymEnumSymbols(m_process, 0, "*MinecraftWorld_RunStaticCtors*", EnumSymbolsCallback, &ctx);
|
||||
pRunStaticCtors = ctx.result;
|
||||
}
|
||||
pRunStaticCtors = Resolve(SYM_RUN_STATIC_CTORS);
|
||||
pMinecraftTick = Resolve(SYM_MINECRAFT_TICK);
|
||||
pMinecraftInit = Resolve(SYM_MINECRAFT_INIT);
|
||||
pExitGame = Resolve(SYM_EXIT_GAME);
|
||||
|
||||
// Minecraft::tick -- MSVC mangles this. Try undecorated name first.
|
||||
pMinecraftTick = Resolve("Minecraft::tick");
|
||||
if (!pMinecraftTick)
|
||||
{
|
||||
EnumContext ctx = { "Minecraft::tick", nullptr };
|
||||
SymEnumSymbols(m_process, 0, "*Minecraft*tick*", EnumSymbolsCallback, &ctx);
|
||||
pMinecraftTick = ctx.result;
|
||||
}
|
||||
if (pRunStaticCtors) printf("[LegacyForge] RunStaticCtors @ %p\n", pRunStaticCtors);
|
||||
else printf("[LegacyForge] MISSING: MinecraftWorld_RunStaticCtors\n");
|
||||
|
||||
// Minecraft::init
|
||||
pMinecraftInit = Resolve("Minecraft::init");
|
||||
if (!pMinecraftInit)
|
||||
{
|
||||
EnumContext ctx = { "Minecraft::init", nullptr };
|
||||
SymEnumSymbols(m_process, 0, "*Minecraft*init*", EnumSymbolsCallback, &ctx);
|
||||
pMinecraftInit = ctx.result;
|
||||
}
|
||||
if (pMinecraftTick) printf("[LegacyForge] Minecraft::tick @ %p\n", pMinecraftTick);
|
||||
else printf("[LegacyForge] MISSING: Minecraft::tick\n");
|
||||
|
||||
// Minecraft::destroy
|
||||
pMinecraftDestroy = Resolve("Minecraft::destroy");
|
||||
if (!pMinecraftDestroy)
|
||||
{
|
||||
EnumContext ctx = { "Minecraft::destroy", nullptr };
|
||||
SymEnumSymbols(m_process, 0, "*Minecraft*destroy*", EnumSymbolsCallback, &ctx);
|
||||
pMinecraftDestroy = ctx.result;
|
||||
}
|
||||
if (pMinecraftInit) printf("[LegacyForge] Minecraft::init @ %p\n", pMinecraftInit);
|
||||
else printf("[LegacyForge] MISSING: Minecraft::init\n");
|
||||
|
||||
bool allResolved = pRunStaticCtors && pMinecraftTick && pMinecraftInit && pMinecraftDestroy;
|
||||
if (pExitGame) printf("[LegacyForge] ExitGame @ %p\n", pExitGame);
|
||||
else printf("[LegacyForge] MISSING: CConsoleMinecraftApp::ExitGame\n");
|
||||
|
||||
if (pRunStaticCtors) printf("[LegacyForge] Resolved RunStaticCtors @ %p\n", pRunStaticCtors);
|
||||
else printf("[LegacyForge] MISSING: MinecraftWorld_RunStaticCtors\n");
|
||||
|
||||
if (pMinecraftTick) printf("[LegacyForge] Resolved Minecraft::tick @ %p\n", pMinecraftTick);
|
||||
else printf("[LegacyForge] MISSING: Minecraft::tick\n");
|
||||
|
||||
if (pMinecraftInit) printf("[LegacyForge] Resolved Minecraft::init @ %p\n", pMinecraftInit);
|
||||
else printf("[LegacyForge] MISSING: Minecraft::init\n");
|
||||
|
||||
if (pMinecraftDestroy) printf("[LegacyForge] Resolved Minecraft::destroy @ %p\n", pMinecraftDestroy);
|
||||
else printf("[LegacyForge] MISSING: Minecraft::destroy\n");
|
||||
|
||||
return allResolved;
|
||||
// RunStaticCtors, tick, and init are critical. ExitGame is optional (graceful shutdown).
|
||||
return pRunStaticCtors && pMinecraftTick && pMinecraftInit;
|
||||
}
|
||||
|
||||
void SymbolResolver::Cleanup()
|
||||
|
||||
@@ -12,10 +12,10 @@ public:
|
||||
|
||||
void* Resolve(const char* functionName);
|
||||
|
||||
void* pRunStaticCtors = nullptr;
|
||||
void* pMinecraftTick = nullptr;
|
||||
void* pMinecraftInit = nullptr;
|
||||
void* pMinecraftDestroy = nullptr;
|
||||
void* pRunStaticCtors = nullptr; // MinecraftWorld_RunStaticCtors
|
||||
void* pMinecraftTick = nullptr; // Minecraft::tick(bool, bool)
|
||||
void* pMinecraftInit = nullptr; // Minecraft::init()
|
||||
void* pExitGame = nullptr; // CConsoleMinecraftApp::ExitGame()
|
||||
|
||||
private:
|
||||
HANDLE m_process = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user