Skip to content

Commit

Permalink
Fix a deadlock when game crashes inside RunListenServer.
Browse files Browse the repository at this point in the history
  • Loading branch information
hzqst committed Mar 14, 2024
1 parent 0ca7869 commit 005bc32
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 31 deletions.
1 change: 1 addition & 0 deletions Plugins/ThreadGuard/ThreadManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ HANDLE WINAPI NewCreateThread(

DWORD WINAPI NewWaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
{
//Fuck valve
if (dwMilliseconds == 0)
{
auto ThreadManager = FindThreadManagerByVirtualAddress(_ReturnAddress());
Expand Down
74 changes: 71 additions & 3 deletions Plugins/ThreadGuard/privatehook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "privatehook.h"
#include "ThreadManager.h"

#include <IEngine.h>

private_funcs_t gPrivateFuncs = { 0 };

hook_t* g_pHook_FreeLibrary_Engine = NULL;
Expand All @@ -14,9 +16,26 @@ IThreadManager* g_ThreadManager_GameUI = NULL;
IThreadManager* g_ThreadManager_ServerBrowser = NULL;
IThreadManager* g_ThreadManager_ServerDLL = NULL;

IEngine** engine = NULL;

void ServerDLL_WaitForShutdown(HMODULE hModule);
void ServerBrowser_WaitForShutdown(HMODULE hModule);

#define DLL_INACTIVE 0 // no dll
#define DLL_ACTIVE 1 // dll is running
#define DLL_PAUSED 2 // dll is paused
#define DLL_CLOSE 3 // closing down dll
#define DLL_TRANS 4 // Level Transition
#define DLL_RESTART 5 // engine is shutting down but will restart right away

int GetEngineDLLState()
{
if (engine)
return (*engine)->GetState();

return DLL_INACTIVE;
}

BOOL WINAPI NewFreeLibrary_Engine(HMODULE hModule)
{
if (g_ThreadManager_ServerDLL && g_ThreadManager_ServerDLL->GetModule() == hModule)
Expand All @@ -39,7 +58,7 @@ BOOL WINAPI NewFreeLibrary_GameUI(HMODULE hModule)

void Engine_WaitForShutdown(HMODULE hModule, BlobHandle_t hBlobModule)
{
if (g_ThreadManager_Engine)
if (g_ThreadManager_Engine && GetEngineDLLState() == DLL_CLOSE)
{
g_ThreadManager_Engine->StartTermination();
g_ThreadManager_Engine->WaitForAliveThreadsToShutdown();
Expand All @@ -63,6 +82,55 @@ void Engine_InstallHook(HMODULE hModule, BlobHandle_t hBlobModule)
g_ThreadManager_Engine = CreateThreadManagerForBlob(hBlobModule);
g_ThreadManager_Engine->InstallHook(hookflag_CreateThread | hookflag_WaitForSingleObject | hookflag_Sleep);
}

if (1)
{
const char sigs1[] = "Sys_InitArgv( OrigCmd )";
auto Sys_InitArgv_String = Search_Pattern_Data(sigs1);
if (!Sys_InitArgv_String)
Sys_InitArgv_String = Search_Pattern_Rdata(sigs1);
if (Sys_InitArgv_String)
{
char pattern[] = "\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A";
*(DWORD*)(pattern + 1) = (DWORD)Sys_InitArgv_String;
auto Sys_InitArgv_PushString = (PUCHAR)Search_Pattern(pattern);
if (Sys_InitArgv_PushString)
{
g_pMetaHookAPI->DisasmRanges(Sys_InitArgv_PushString, 0x50, [](void* inst, PUCHAR address, size_t instLen, int instCount, int depth, PVOID context)
{
auto pinst = (cs_insn*)inst;

if (pinst->id == X86_INS_MOV &&
pinst->detail->x86.op_count == 2 &&
pinst->detail->x86.operands[0].type == X86_OP_REG &&
pinst->detail->x86.operands[1].type == X86_OP_MEM &&
pinst->detail->x86.operands[1].mem.base == 0)
{//A1 40 77 7B 02 mov eax, gl_backbuffer_fbo
ULONG_PTR imm = pinst->detail->x86.operands[1].mem.disp;

if (imm >= (ULONG_PTR)g_dwEngineDataBase && imm < (ULONG_PTR)g_dwEngineDataBase + g_dwEngineDataSize)
{
engine = (decltype(engine))imm;
return TRUE;
}
}

if (address[0] == 0xCC)
return TRUE;

if (pinst->id == X86_INS_RET)
return TRUE;

return FALSE;
}, 0, NULL);
}
}
}

if (!engine)
{
Sys_Error("CEngine not found");
}
}

void Engine_UninstallHook(HMODULE hModule, BlobHandle_t hBlobModule)
Expand Down Expand Up @@ -113,7 +181,7 @@ void GameUI_UnistallHook(HMODULE hModule)

void ServerDLL_WaitForShutdown(HMODULE hModule)
{
if (g_ThreadManager_ServerDLL)
if (g_ThreadManager_ServerDLL && GetEngineDLLState() == DLL_CLOSE)
{
g_ThreadManager_ServerDLL->StartTermination();
g_ThreadManager_ServerDLL->WaitForAliveThreadsToShutdown();
Expand Down Expand Up @@ -144,7 +212,7 @@ void ServerDLL_UninstallHook(HMODULE hModule)

void ServerBrowser_WaitForShutdown(HMODULE hModule)
{
if (g_ThreadManager_ServerBrowser)
if (g_ThreadManager_ServerBrowser && GetEngineDLLState() == DLL_CLOSE)
{
g_ThreadManager_ServerBrowser->StartTermination();
g_ThreadManager_ServerBrowser->WaitForAliveThreadsToShutdown();
Expand Down
36 changes: 26 additions & 10 deletions include/Interface/IEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,35 @@
#pragma once
#endif

enum
{
ENGINE_RESULT_NONE,
ENGINE_RESULT_RESTART,
ENGINE_RESULT_UNSUPPORTEDVIDEO
};
#include "interface.h"

class IEngine : public IBaseInterface
{
public:
virtual int Run(HINSTANCE instance, char *basedir, const char *cmdline, char *szCommand, CreateInterfaceFn launcherFactory, CreateInterfaceFn filesystemFactory);
};
enum
{
QUIT_NOTQUITTING = 0,
QUIT_TODESKTOP,
QUIT_RESTART
};

#define VENGINE_LAUNCHER_API_VERSION "VENGINE_LAUNCHER_API_VERSION002"
virtual bool Load( bool dedicated, char *basedir, char *cmdline ) = 0;
virtual void Unload( void ) = 0;
virtual void SetState( int iState ) = 0;
virtual int GetState( void ) = 0;
virtual void SetSubState( int iSubState ) = 0;
virtual int GetSubState( void ) = 0;
virtual int Frame( void ) = 0;
virtual double GetFrameTime( void ) = 0;
virtual double GetCurTime( void ) = 0;
virtual void TrapKey_Event( int key, bool down ) = 0;
virtual void TrapMouse_Event( int buttons, bool down ) = 0;
virtual void StartTrapMode( void ) = 0;
virtual bool IsTrapping( void ) = 0;
virtual bool CheckDoneTrapping( int& buttons, int& key ) = 0;
virtual int GetQuitting( void ) = 0;
virtual void SetQuitting( int quittype ) = 0;

};

#endif
#endif //IENGINE_H
25 changes: 25 additions & 0 deletions include/Interface/IEngineAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef IENGINEAPI_H
#define IENGINEAPI_H

#ifdef _WIN32
#pragma once
#endif

#include "interface.h"

enum
{
ENGINE_RESULT_NONE,
ENGINE_RESULT_RESTART,
ENGINE_RESULT_UNSUPPORTEDVIDEO
};

class IEngineAPI : public IBaseInterface
{
public:
virtual int Run(HINSTANCE instance, char *basedir, const char *cmdline, char *szCommand, CreateInterfaceFn launcherFactory, CreateInterfaceFn filesystemFactory);
};

#define VENGINE_LAUNCHER_API_VERSION "VENGINE_LAUNCHER_API_VERSION002"

#endif //IENGINEAPI_H
20 changes: 16 additions & 4 deletions src/LoadDllNotification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,13 @@ void MH_DispatchLoadLdrDllNotificationCallback(PCUNICODE_STRING FullDllName, PCU
if (FullDllName)
{
UnicodeToWString(FullDllName, wFullDllName);
UnicodeToWString(BaseDllName, wBaseDllName);

ctx.FullDllName = wFullDllName.c_str();
}

if (BaseDllName)
{
UnicodeToWString(BaseDllName, wBaseDllName);
ctx.BaseDllName = wBaseDllName.c_str();
}

Expand Down Expand Up @@ -217,7 +221,9 @@ static NTSTATUS NTAPI NewLdrLoadDll(PWSTR a1, PULONG a2, PUNICODE_STRING a3, PVO
{
auto pLdrEntryInfo = GetLdrEntryInfoByDllBase(*a4);

if (pLdrEntryInfo)
if (pLdrEntryInfo &&
//Skip here, dispatch engine notification manually
pLdrEntryInfo->DllBase != MH_GetEngineBase())
{
MH_DispatchLoadLdrDllNotificationCallback(&pLdrEntryInfo->FullDllName, &pLdrEntryInfo->BaseDllName, pLdrEntryInfo->DllBase, pLdrEntryInfo->SizeOfImage, LOAD_DLL_NOTIFICATION_IS_LOAD);
}
Expand All @@ -236,13 +242,19 @@ VOID CALLBACK LdrDllNotificationCallback(
{
auto& args = NotificationData->Loaded;

MH_DispatchLoadLdrDllNotificationCallback(args.FullDllName, args.BaseDllName, args.DllBase, args.SizeOfImage, LOAD_DLL_NOTIFICATION_IS_LOAD | LOAD_DLL_NOTIFICATION_IS_IN_CRIT_REGION);
if (args.DllBase != MH_GetEngineBase())
{
MH_DispatchLoadLdrDllNotificationCallback(args.FullDllName, args.BaseDllName, args.DllBase, args.SizeOfImage, LOAD_DLL_NOTIFICATION_IS_LOAD | LOAD_DLL_NOTIFICATION_IS_IN_CRIT_REGION);
}
}
else if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
{
auto& args = NotificationData->Unloaded;

MH_DispatchLoadLdrDllNotificationCallback(args.FullDllName, args.BaseDllName, args.DllBase, args.SizeOfImage, LOAD_DLL_NOTIFICATION_IS_UNLOAD | LOAD_DLL_NOTIFICATION_IS_IN_CRIT_REGION);
if (args.DllBase != MH_GetEngineBase())
{
MH_DispatchLoadLdrDllNotificationCallback(args.FullDllName, args.BaseDllName, args.DllBase, args.SizeOfImage, LOAD_DLL_NOTIFICATION_IS_UNLOAD | LOAD_DLL_NOTIFICATION_IS_IN_CRIT_REGION);
}
}
g_IsInLdrCriticalRegion = false;
}
Expand Down
28 changes: 16 additions & 12 deletions src/launcher.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "metahook.h"
#include <IEngine.h>
#include <IEngineAPI.h>
#include "LoadBlob.h"
#include "LoadDllNotification.h"
#include "sys.h"
Expand All @@ -14,6 +14,9 @@ IFileSystem* g_pFileSystem = nullptr;
PVOID g_BlobLoaderSectionBase = NULL;
ULONG g_BlobLoaderSectionSize = 0;

PVOID MH_GetEngineBase(void);
DWORD MH_GetEngineSize(void);

#if 0
#include <dbghelp.h>
#include <shlobj.h>
Expand Down Expand Up @@ -330,7 +333,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi

memset(szNewCommandParams, 0, sizeof(szNewCommandParams));

IEngine *engineAPI = NULL;
IEngineAPI *EngineAPI = NULL;
HINTERFACEMODULE hEngine = NULL;
BlobHandle_t hBlobEngine = NULL;

Expand Down Expand Up @@ -383,12 +386,12 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
{
BlobLoaderAddBlob(hBlobEngine);
RunDllMainForBlob(hBlobEngine, DLL_PROCESS_ATTACH);
RunExportEntryForBlob(hBlobEngine, (void**)&engineAPI);
RunExportEntryForBlob(hBlobEngine, (void**)&EngineAPI);

if (!engineAPI)
if (!EngineAPI)
{
char msg[512];
wsprintf(msg, "Could not get engineAPI from engine : %s.", pszEngineDLL);
wsprintf(msg, "Could not get EngineAPI from engine : %s.", pszEngineDLL);
MessageBoxA(NULL, msg, "Fatal Error", MB_ICONERROR);
ExitProcess(0);
}
Expand All @@ -406,32 +409,32 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
ExitProcess(0);
}

CreateInterfaceFn engineCreateInterface = (CreateInterfaceFn)Sys_GetFactory(hEngine);
CreateInterfaceFn EngineFactory = (CreateInterfaceFn)Sys_GetFactory(hEngine);

if (!engineCreateInterface)
if (!EngineFactory)
{
char msg[512];
wsprintf(msg, "Could not get factory from engine : %s.", pszEngineDLL);
MessageBoxA(NULL, msg, "Fatal Error", MB_ICONERROR);
ExitProcess(0);
}

engineAPI = (IEngine *)engineCreateInterface(VENGINE_LAUNCHER_API_VERSION, NULL);
EngineAPI = (IEngineAPI *)EngineFactory(VENGINE_LAUNCHER_API_VERSION, NULL);

if (!engineAPI)
if (!EngineAPI)
{
char msg[512];
wsprintf(msg, "Could not get engineAPI from engine : %s.", pszEngineDLL);
wsprintf(msg, "Could not get EngineAPI from engine : %s.", pszEngineDLL);
MessageBoxA(NULL, msg, "Fatal Error", MB_ICONERROR);
ExitProcess(0);
}
}

if (engineAPI)
if (EngineAPI)
{
MH_LoadEngine((HMODULE)hEngine, hBlobEngine, szGameName, szFullPath);

iResult = engineAPI->Run(hInstance, Sys_GetLongPathName(), CommandLine()->GetCmdLine(), szNewCommandParams, Sys_GetFactoryThis(), Sys_GetFactory(hFileSystem));
iResult = EngineAPI->Run(hInstance, Sys_GetLongPathName(), CommandLine()->GetCmdLine(), szNewCommandParams, Sys_GetFactoryThis(), Sys_GetFactory(hFileSystem));

MH_ExitGame(iResult);

Expand All @@ -443,6 +446,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
}
else
{
MH_DispatchLoadLdrDllNotificationCallback(NULL, NULL, MH_GetEngineBase(), MH_GetEngineSize(), LOAD_DLL_NOTIFICATION_IS_UNLOAD);
Sys_FreeModule(hEngine);
}

Expand Down
4 changes: 2 additions & 2 deletions src/metahook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ DWORD MH_LoadBlobFile(BYTE* pBuffer, void** pBlobFootPrint, void** pv, DWORD dwB
return 0;
}

void MH_FreeBlob(void** pBlobFootPrint)
void MH_FreeBlobProxy(void** pBlobFootPrint)
{
BlobHandle_t hBlob = (BlobHandle_t)(*pBlobFootPrint);

Expand Down Expand Up @@ -2043,7 +2043,7 @@ void MH_LoadEngine(HMODULE hEngineModule, BlobHandle_t hBlobEngine, const char*
*/
if (g_pfnFreeBlob)
{
MH_InlineHook(g_pfnFreeBlob, MH_FreeBlob, NULL);
MH_InlineHook(g_pfnFreeBlob, MH_FreeBlobProxy, NULL);
}

MH_LoadDllPaths(szGameName, szFullGamePath);
Expand Down

0 comments on commit 005bc32

Please sign in to comment.