2023-06-25 11:37:52 +02:00
|
|
|
//===========================================================================//
|
|
|
|
//
|
|
|
|
// Purpose: SDK loader stub
|
|
|
|
//
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// The game module cannot be imported directly by the executable, as the
|
|
|
|
// internal memalloc system is not initialized before the entry point is
|
|
|
|
// called. So we need to load the SDK dll after the entry point is called,
|
|
|
|
// but before LauncherMain is called.
|
|
|
|
//
|
|
|
|
// The executable exports table has been restructured; the exported function
|
|
|
|
// 'GetDenuvoTimeTicketRequest' has been swapped with 'CreateGlobalMemAlloc',
|
|
|
|
// the exported 'IEngineAPI' interface accessor has been replaced with
|
|
|
|
// 'g_pMemAllocSingleton', and the exported function 'LauncherMain' has been
|
|
|
|
// swapped with 'WinMain', so we can obtain the addresses without hardcoding.
|
|
|
|
//
|
|
|
|
// These changes allow us to load the SDK in the following order:
|
|
|
|
// - Create game process
|
|
|
|
// - Import this loader stub by its dummy export.
|
|
|
|
// - Immediately hook 'WinMain', by getting a pointer to it from its exports.
|
|
|
|
// - Determine if, and which SDK module to load.
|
|
|
|
//
|
|
|
|
// Since WinMain is called before anything of the game is, we can still hook
|
|
|
|
// and modify anything of the game before it starts. With the above order of
|
|
|
|
// initialization, we can now replace the standard memalloc system with that
|
|
|
|
// of the game, by:
|
|
|
|
//
|
|
|
|
// - Redefining the standard C functions to use the internal memalloc system.
|
|
|
|
// - Checking if the memalloc system has been initialized, and create if not.
|
|
|
|
//===========================================================================//
|
|
|
|
#include "loader.h"
|
|
|
|
#include "tier0/module.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Image statics
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static const PEB64* s_ProcessEnvironmentBlock = nullptr;
|
|
|
|
static const IMAGE_DOS_HEADER* s_DosHeader = nullptr;
|
|
|
|
static const IMAGE_NT_HEADERS64* s_NtHeaders = nullptr;
|
|
|
|
static HMODULE s_SdkModule = NULL;
|
|
|
|
|
2024-04-03 01:34:25 +02:00
|
|
|
typedef void (*InitFunc)(void);
|
|
|
|
static InitFunc s_SdkInitFunc = NULL;
|
|
|
|
static InitFunc s_SdkShutdownFunc = NULL;
|
|
|
|
|
2023-06-25 11:37:52 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
2024-04-03 01:34:25 +02:00
|
|
|
// LauncherMain function pointer
|
2023-06-25 11:37:52 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
2024-04-03 01:34:25 +02:00
|
|
|
static int (*v_LauncherMain)(HINSTANCE, HINSTANCE, LPSTR, int) = nullptr;
|
2023-06-25 11:37:52 +02:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Terminates the process with an error when called
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-19 16:00:36 +02:00
|
|
|
static void FatalError(const char* fmt, ...)
|
2023-06-25 11:37:52 +02:00
|
|
|
{
|
|
|
|
va_list vArgs;
|
|
|
|
va_start(vArgs, fmt);
|
|
|
|
|
|
|
|
char errorBuf[1024];
|
|
|
|
vsnprintf(errorBuf, sizeof(errorBuf), fmt, vArgs);
|
|
|
|
|
|
|
|
errorBuf[sizeof(errorBuf) - 1] = '\0';
|
|
|
|
va_end(vArgs);
|
|
|
|
|
|
|
|
MessageBoxA(NULL, errorBuf, "Loader Error", MB_ICONERROR | MB_OK);
|
|
|
|
TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Loads the SDK module
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-19 16:00:36 +02:00
|
|
|
static void InitGameSDK(const LPSTR lpCmdLine)
|
2023-06-25 11:37:52 +02:00
|
|
|
{
|
|
|
|
if (V_strstr(lpCmdLine, "-noworkerdll"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
char moduleName[MAX_PATH];
|
|
|
|
|
|
|
|
if (!GetModuleFileNameA((HMODULE)s_DosHeader,
|
|
|
|
moduleName, sizeof(moduleName)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Prune the path.
|
|
|
|
const char* pModuleName = strrchr(moduleName, '\\') + 1;
|
2023-07-19 16:00:36 +02:00
|
|
|
const bool bDedicated = V_stricmp(pModuleName, SERVER_GAME_DLL) == NULL;
|
2023-06-25 11:37:52 +02:00
|
|
|
|
|
|
|
// The dedicated server has its own SDK module,
|
|
|
|
// so we need to check whether we are running
|
|
|
|
// the base game or the dedicated server.
|
2023-07-19 16:00:36 +02:00
|
|
|
if (!bDedicated)
|
|
|
|
{
|
|
|
|
// Load the client dll if '-noserverdll' is passed,
|
|
|
|
// as this command lime parameter prevents the
|
|
|
|
// server dll from initializing in the engine.
|
|
|
|
if (V_strstr(lpCmdLine, "-noserverdll"))
|
|
|
|
s_SdkModule = LoadLibraryA(CLIENT_WORKER_DLL);
|
|
|
|
else
|
|
|
|
s_SdkModule = LoadLibraryA(MAIN_WORKER_DLL);
|
|
|
|
}
|
|
|
|
else
|
2023-07-02 02:46:58 +02:00
|
|
|
s_SdkModule = LoadLibraryA(SERVER_WORKER_DLL);
|
2023-06-25 11:37:52 +02:00
|
|
|
|
|
|
|
if (!s_SdkModule)
|
|
|
|
{
|
|
|
|
Assert(0);
|
|
|
|
FatalError("Failed to load SDK: error code = %08x\n", GetLastError());
|
2024-04-03 01:34:25 +02:00
|
|
|
|
|
|
|
return;
|
2023-06-25 11:37:52 +02:00
|
|
|
}
|
2024-04-03 01:34:25 +02:00
|
|
|
|
|
|
|
s_SdkInitFunc = (InitFunc)GetProcAddress(s_SdkModule, "SDK_Init");
|
|
|
|
if (s_SdkInitFunc)
|
|
|
|
s_SdkShutdownFunc = (InitFunc)GetProcAddress(s_SdkModule, "SDK_Shutdown");
|
|
|
|
|
|
|
|
if (!s_SdkInitFunc || !s_SdkShutdownFunc)
|
|
|
|
{
|
|
|
|
Assert(0);
|
|
|
|
FatalError("Loaded SDK is invalid: error code = %08x\n", GetLastError());
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s_SdkInitFunc();
|
2023-06-25 11:37:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2024-04-03 01:34:25 +02:00
|
|
|
// Purpose: Unloads the SDK module
|
2023-06-25 11:37:52 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
2024-04-03 01:34:25 +02:00
|
|
|
static void ShutdownGameSDK()
|
|
|
|
{
|
|
|
|
if (s_SdkModule)
|
|
|
|
{
|
|
|
|
s_SdkShutdownFunc();
|
|
|
|
|
|
|
|
FreeLibrary(s_SdkModule);
|
|
|
|
s_SdkModule = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: LauncherMain hook; loads the SDK before the game inits
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int WINAPI hLauncherMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
2023-06-25 11:37:52 +02:00
|
|
|
{
|
|
|
|
InitGameSDK(lpCmdLine); // Init GameSDK, internal function calls LauncherMain.
|
2024-04-03 01:34:25 +02:00
|
|
|
const int ret = v_LauncherMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
|
|
|
|
ShutdownGameSDK();
|
|
|
|
|
|
|
|
return ret;
|
2023-06-25 11:37:52 +02:00
|
|
|
}
|
|
|
|
|
2023-07-19 16:00:36 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: hooks the entry point
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void AttachEP()
|
|
|
|
{
|
|
|
|
DetourTransactionBegin();
|
|
|
|
DetourUpdateThread(GetCurrentThread());
|
|
|
|
|
2024-04-03 01:34:25 +02:00
|
|
|
DetourAttach(&v_LauncherMain, &hLauncherMain);
|
2023-07-19 16:00:36 +02:00
|
|
|
|
|
|
|
HRESULT hr = DetourTransactionCommit();
|
|
|
|
if (hr != NO_ERROR) // Failed to hook into the process, terminate...
|
|
|
|
{
|
|
|
|
Assert(0);
|
|
|
|
FatalError("Failed to detour process: error code = %08x\n", hr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: unhooks the entry point
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void DetachEP()
|
|
|
|
{
|
|
|
|
DetourTransactionBegin();
|
|
|
|
DetourUpdateThread(GetCurrentThread());
|
|
|
|
|
2024-04-03 01:34:25 +02:00
|
|
|
DetourDetach(&v_LauncherMain, &hLauncherMain);
|
2023-07-19 16:00:36 +02:00
|
|
|
HRESULT hr = DetourTransactionCommit();
|
2023-07-19 18:56:01 +02:00
|
|
|
|
2023-07-19 16:00:36 +02:00
|
|
|
Assert(hr != NO_ERROR);
|
2023-07-19 18:56:01 +02:00
|
|
|
NOTE_UNUSED(hr);
|
2023-07-19 16:00:36 +02:00
|
|
|
}
|
|
|
|
|
2023-06-25 11:37:52 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: APIENTRY
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
|
|
|
{
|
|
|
|
switch (dwReason)
|
|
|
|
{
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
{
|
2023-07-02 22:02:31 +02:00
|
|
|
s_ProcessEnvironmentBlock = CModule::GetProcessEnvironmentBlock();
|
2023-06-25 11:37:52 +02:00
|
|
|
s_DosHeader = (IMAGE_DOS_HEADER*)s_ProcessEnvironmentBlock->ImageBaseAddress;
|
|
|
|
s_NtHeaders = (IMAGE_NT_HEADERS64*)((uintptr_t)s_DosHeader
|
|
|
|
+ (uintptr_t)s_DosHeader->e_lfanew);
|
|
|
|
|
2024-04-03 01:34:25 +02:00
|
|
|
v_LauncherMain = CModule::GetExportedSymbol((QWORD)s_DosHeader, "LauncherMain")
|
2023-06-25 11:37:52 +02:00
|
|
|
.RCast<int (*)(HINSTANCE, HINSTANCE, LPSTR, int)>();
|
|
|
|
|
2023-07-19 16:00:36 +02:00
|
|
|
AttachEP();
|
2023-06-25 11:37:52 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
{
|
2023-07-19 16:00:36 +02:00
|
|
|
DetachEP();
|
2023-06-25 11:37:52 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|