From 22675ba2e7d7a8f480b68d09b34d046720abd902 Mon Sep 17 00:00:00 2001 From: Scott Ehlert Date: Thu, 22 Jan 2009 15:20:45 -0600 Subject: [PATCH] Fixed crash in GetClientInfo() native on L4D (bug 3569, r=me). The CreateFakeClient signature (used for verification before retrieving the IServer interface pointer) changed in a recent update. The IServer pointer was therefore null and GetClientInfo() did not check for this. The CreateFakeClient signature is also no longer hardcoded. It is modifiable in gamedata/sdktools.games/engine.*.txt. --- extensions/sdktools/vglobals.cpp | 90 +++++++++++++++++++------- extensions/sdktools/vnatives.cpp | 8 ++- gamedata/sdktools.games/engine.ep1.txt | 13 ++++ gamedata/sdktools.games/engine.ep2.txt | 13 ++++ gamedata/sdktools.games/engine.l4d.txt | 15 ++++- 5 files changed, 113 insertions(+), 26 deletions(-) diff --git a/extensions/sdktools/vglobals.cpp b/extensions/sdktools/vglobals.cpp index 9a0ba0f8..1102538b 100644 --- a/extensions/sdktools/vglobals.cpp +++ b/extensions/sdktools/vglobals.cpp @@ -83,12 +83,49 @@ void InitializeValveGlobals() } #endif -bool vcmp(const void *_addr1, const void *_addr2, size_t len) +size_t UTIL_StringToSignature(const char *str, char buffer[], size_t maxlength) { - unsigned char *addr1 = (unsigned char *)_addr1; - unsigned char *addr2 = (unsigned char *)_addr2; + size_t real_bytes = 0; + size_t length = strlen(str); - for (size_t i=0; i= maxlength) + { + break; + } + buffer[real_bytes++] = (unsigned char)str[i]; + if (str[i] == '\\' + && str[i+1] == 'x') + { + if (i + 3 >= length) + { + continue; + } + /* Get the hex part */ + char s_byte[3]; + int r_byte; + s_byte[0] = str[i+2]; + s_byte[1] = str[i+3]; + s_byte[2] = '\n'; + /* Read it as an integer */ + sscanf(s_byte, "%x", &r_byte); + /* Save the value */ + buffer[real_bytes-1] = (unsigned char)r_byte; + /* Adjust index */ + i += 3; + } + } + + return real_bytes; +} + +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len) +{ + unsigned char *addr1 = (unsigned char *) addr; + unsigned char *addr2 = (unsigned char *) sig; + + for (size_t i = 0; i < len; i++) { if (addr2[i] == '*') continue; @@ -100,34 +137,20 @@ bool vcmp(const void *_addr1, const void *_addr2, size_t len) } #if defined PLATFORM_WINDOWS - /* Thanks to DS for the sigs */ - #define ISERVER_WIN_SIG "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8" - #define ISERVER_WIN_SIG_LEN 11 void GetIServer() { + const char *sigstr; + char sig[32]; + size_t siglen; int offset; void *vfunc = NULL; - /* Get the offset into CreateFakeClient */ - if (!g_pGameConf->GetOffset("sv", &offset)) - { - return; - } #if defined METAMOD_PLAPI_VERSION /* Get the CreateFakeClient function pointer */ if (!(vfunc=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient))) { return; } - - /* Check if we're on the expected function */ - if (!vcmp(vfunc, ISERVER_WIN_SIG, ISERVER_WIN_SIG_LEN)) - { - return; - } - - /* Finally we have the interface we were looking for */ - iserver = *reinterpret_cast(reinterpret_cast(vfunc) + offset); #else /* Get the interface manually */ SourceHook::MemFuncInfo info = {true, -1, 0, 0}; @@ -139,14 +162,33 @@ void GetIServer() void **vtable = *reinterpret_cast(enginePatch->GetThisPtr() + info.thisptroffs + info.vtbloffs); vfunc = vtable[info.vtblindex]; } - /* Check if we're on the expected function */ - if (!vcmp(vfunc, ISERVER_WIN_SIG, ISERVER_WIN_SIG_LEN)) +#endif + + /* Get signature string for IVEngineServer::CreateFakeClient() */ + sigstr = g_pGameConf->GetKeyValue("CreateFakeClient_Windows"); + + if (!sigstr) { return; } + /* Convert signature string to signature bytes */ + siglen = UTIL_StringToSignature(sigstr, sig, sizeof(sig)); + + /* Check if we're on the expected function */ + if (!UTIL_VerifySignature(vfunc, sig, siglen)) + { + return; + } + + /* Get the offset into CreateFakeClient */ + if (!g_pGameConf->GetOffset("sv", &offset)) + { + return; + } + + /* Finally we have the interface we were looking for */ iserver = *reinterpret_cast(reinterpret_cast(vfunc) + offset); -#endif } #elif defined PLATFORM_POSIX void GetIServer() diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp index 885a8c32..99271e22 100644 --- a/extensions/sdktools/vnatives.cpp +++ b/extensions/sdktools/vnatives.cpp @@ -962,8 +962,14 @@ static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params) static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params) { + if (iserver == NULL) + { + return pContext->ThrowNativeError("IServer interface not supported, file a bug report."); + } + IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]); - IClient *pClient = iserver->GetClient(params[1]-1); + IClient *pClient = iserver->GetClient(params[1] - 1); + if (player == NULL || pClient == NULL) { return pContext->ThrowNativeError("Invalid client index %d", params[1]); diff --git a/gamedata/sdktools.games/engine.ep1.txt b/gamedata/sdktools.games/engine.ep1.txt index 86784b98..43fd1999 100644 --- a/gamedata/sdktools.games/engine.ep1.txt +++ b/gamedata/sdktools.games/engine.ep1.txt @@ -196,6 +196,19 @@ /* IServer interface pointer */ "#default" { + "Keys" + { + /* Signature for the beginning of IVEngineServer::CreateFakeClient. + * + * The engine binary is not actually scanned in order to look for + * this. SourceHook is used to used to determine the address of the + * function and this signature is used to verify that it contains + * the expected code. A pointer to sv (IServer interface) is used + * here. + */ + "CreateFakeClient_Windows" "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8" + } + "Offsets" { /* Offset into IVEngineServer::CreateFakeClient */ diff --git a/gamedata/sdktools.games/engine.ep2.txt b/gamedata/sdktools.games/engine.ep2.txt index 77fac3d7..9d1f64c4 100644 --- a/gamedata/sdktools.games/engine.ep2.txt +++ b/gamedata/sdktools.games/engine.ep2.txt @@ -167,6 +167,19 @@ /* IServer interface pointer */ "#default" { + "Keys" + { + /* Signature for the beginning of IVEngineServer::CreateFakeClient. + * + * The engine binary is not actually scanned in order to look for + * this. SourceHook is used to used to determine the address of the + * function and this signature is used to verify that it contains + * the expected code. A pointer to sv (IServer interface) is used + * here. + */ + "CreateFakeClient_Windows" "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8" + } + "Offsets" { /* Offset into IVEngineServer::CreateFakeClient */ diff --git a/gamedata/sdktools.games/engine.l4d.txt b/gamedata/sdktools.games/engine.l4d.txt index be6cdbca..1f723871 100644 --- a/gamedata/sdktools.games/engine.l4d.txt +++ b/gamedata/sdktools.games/engine.l4d.txt @@ -166,12 +166,25 @@ /* IServer interface pointer */ "#default" { + "Keys" + { + /* Signature for the beginning of IVEngineServer::CreateFakeClient. + * + * The engine binary is not actually scanned in order to look for + * this. SourceHook is used to used to determine the address of the + * function and this signature is used to verify that it contains + * the expected code. A pointer to sv (IServer interface) is used + * here. + */ + "CreateFakeClient_Windows" "\x55\x8B\xEC\x83\xEC\x08\x89\x4D\xF8\x8B\x45\x08\x50\xB9\x2A\x2A\x2A\x2A\xE8" + } + "Offsets" { /* Offset into IVEngineServer::CreateFakeClient */ "sv" { - "windows" "6" + "windows" "14" } }