Fixed crash in GetClientInfo() native on L4D (bug 3569, r=me).
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.
1.1 --- a/extensions/sdktools/vglobals.cpp Sat Jan 17 13:45:51 2009 +1300
1.2 +++ b/extensions/sdktools/vglobals.cpp Thu Jan 22 15:20:45 2009 -0600
1.3 @@ -83,12 +83,49 @@
1.4 }
1.5 #endif
1.6
1.7 -bool vcmp(const void *_addr1, const void *_addr2, size_t len)
1.8 +size_t UTIL_StringToSignature(const char *str, char buffer[], size_t maxlength)
1.9 {
1.10 - unsigned char *addr1 = (unsigned char *)_addr1;
1.11 - unsigned char *addr2 = (unsigned char *)_addr2;
1.12 + size_t real_bytes = 0;
1.13 + size_t length = strlen(str);
1.14
1.15 - for (size_t i=0; i<len; i++)
1.16 + for (size_t i=0; i<length; i++)
1.17 + {
1.18 + if (real_bytes >= maxlength)
1.19 + {
1.20 + break;
1.21 + }
1.22 + buffer[real_bytes++] = (unsigned char)str[i];
1.23 + if (str[i] == '\\'
1.24 + && str[i+1] == 'x')
1.25 + {
1.26 + if (i + 3 >= length)
1.27 + {
1.28 + continue;
1.29 + }
1.30 + /* Get the hex part */
1.31 + char s_byte[3];
1.32 + int r_byte;
1.33 + s_byte[0] = str[i+2];
1.34 + s_byte[1] = str[i+3];
1.35 + s_byte[2] = '\n';
1.36 + /* Read it as an integer */
1.37 + sscanf(s_byte, "%x", &r_byte);
1.38 + /* Save the value */
1.39 + buffer[real_bytes-1] = (unsigned char)r_byte;
1.40 + /* Adjust index */
1.41 + i += 3;
1.42 + }
1.43 + }
1.44 +
1.45 + return real_bytes;
1.46 +}
1.47 +
1.48 +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len)
1.49 +{
1.50 + unsigned char *addr1 = (unsigned char *) addr;
1.51 + unsigned char *addr2 = (unsigned char *) sig;
1.52 +
1.53 + for (size_t i = 0; i < len; i++)
1.54 {
1.55 if (addr2[i] == '*')
1.56 continue;
1.57 @@ -100,34 +137,20 @@
1.58 }
1.59
1.60 #if defined PLATFORM_WINDOWS
1.61 - /* Thanks to DS for the sigs */
1.62 - #define ISERVER_WIN_SIG "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8"
1.63 - #define ISERVER_WIN_SIG_LEN 11
1.64 void GetIServer()
1.65 {
1.66 + const char *sigstr;
1.67 + char sig[32];
1.68 + size_t siglen;
1.69 int offset;
1.70 void *vfunc = NULL;
1.71
1.72 - /* Get the offset into CreateFakeClient */
1.73 - if (!g_pGameConf->GetOffset("sv", &offset))
1.74 - {
1.75 - return;
1.76 - }
1.77 #if defined METAMOD_PLAPI_VERSION
1.78 /* Get the CreateFakeClient function pointer */
1.79 if (!(vfunc=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient)))
1.80 {
1.81 return;
1.82 }
1.83 -
1.84 - /* Check if we're on the expected function */
1.85 - if (!vcmp(vfunc, ISERVER_WIN_SIG, ISERVER_WIN_SIG_LEN))
1.86 - {
1.87 - return;
1.88 - }
1.89 -
1.90 - /* Finally we have the interface we were looking for */
1.91 - iserver = *reinterpret_cast<IServer **>(reinterpret_cast<unsigned char *>(vfunc) + offset);
1.92 #else
1.93 /* Get the interface manually */
1.94 SourceHook::MemFuncInfo info = {true, -1, 0, 0};
1.95 @@ -139,14 +162,33 @@
1.96 void **vtable = *reinterpret_cast<void ***>(enginePatch->GetThisPtr() + info.thisptroffs + info.vtbloffs);
1.97 vfunc = vtable[info.vtblindex];
1.98 }
1.99 - /* Check if we're on the expected function */
1.100 - if (!vcmp(vfunc, ISERVER_WIN_SIG, ISERVER_WIN_SIG_LEN))
1.101 +#endif
1.102 +
1.103 + /* Get signature string for IVEngineServer::CreateFakeClient() */
1.104 + sigstr = g_pGameConf->GetKeyValue("CreateFakeClient_Windows");
1.105 +
1.106 + if (!sigstr)
1.107 {
1.108 return;
1.109 }
1.110
1.111 + /* Convert signature string to signature bytes */
1.112 + siglen = UTIL_StringToSignature(sigstr, sig, sizeof(sig));
1.113 +
1.114 + /* Check if we're on the expected function */
1.115 + if (!UTIL_VerifySignature(vfunc, sig, siglen))
1.116 + {
1.117 + return;
1.118 + }
1.119 +
1.120 + /* Get the offset into CreateFakeClient */
1.121 + if (!g_pGameConf->GetOffset("sv", &offset))
1.122 + {
1.123 + return;
1.124 + }
1.125 +
1.126 + /* Finally we have the interface we were looking for */
1.127 iserver = *reinterpret_cast<IServer **>(reinterpret_cast<unsigned char *>(vfunc) + offset);
1.128 -#endif
1.129 }
1.130 #elif defined PLATFORM_POSIX
1.131 void GetIServer()
2.1 --- a/extensions/sdktools/vnatives.cpp Sat Jan 17 13:45:51 2009 +1300
2.2 +++ b/extensions/sdktools/vnatives.cpp Thu Jan 22 15:20:45 2009 -0600
2.3 @@ -962,8 +962,14 @@
2.4
2.5 static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params)
2.6 {
2.7 + if (iserver == NULL)
2.8 + {
2.9 + return pContext->ThrowNativeError("IServer interface not supported, file a bug report.");
2.10 + }
2.11 +
2.12 IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
2.13 - IClient *pClient = iserver->GetClient(params[1]-1);
2.14 + IClient *pClient = iserver->GetClient(params[1] - 1);
2.15 +
2.16 if (player == NULL || pClient == NULL)
2.17 {
2.18 return pContext->ThrowNativeError("Invalid client index %d", params[1]);
3.1 --- a/gamedata/sdktools.games/engine.ep1.txt Sat Jan 17 13:45:51 2009 +1300
3.2 +++ b/gamedata/sdktools.games/engine.ep1.txt Thu Jan 22 15:20:45 2009 -0600
3.3 @@ -196,6 +196,19 @@
3.4 /* IServer interface pointer */
3.5 "#default"
3.6 {
3.7 + "Keys"
3.8 + {
3.9 + /* Signature for the beginning of IVEngineServer::CreateFakeClient.
3.10 + *
3.11 + * The engine binary is not actually scanned in order to look for
3.12 + * this. SourceHook is used to used to determine the address of the
3.13 + * function and this signature is used to verify that it contains
3.14 + * the expected code. A pointer to sv (IServer interface) is used
3.15 + * here.
3.16 + */
3.17 + "CreateFakeClient_Windows" "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8"
3.18 + }
3.19 +
3.20 "Offsets"
3.21 {
3.22 /* Offset into IVEngineServer::CreateFakeClient */
4.1 --- a/gamedata/sdktools.games/engine.ep2.txt Sat Jan 17 13:45:51 2009 +1300
4.2 +++ b/gamedata/sdktools.games/engine.ep2.txt Thu Jan 22 15:20:45 2009 -0600
4.3 @@ -167,6 +167,19 @@
4.4 /* IServer interface pointer */
4.5 "#default"
4.6 {
4.7 + "Keys"
4.8 + {
4.9 + /* Signature for the beginning of IVEngineServer::CreateFakeClient.
4.10 + *
4.11 + * The engine binary is not actually scanned in order to look for
4.12 + * this. SourceHook is used to used to determine the address of the
4.13 + * function and this signature is used to verify that it contains
4.14 + * the expected code. A pointer to sv (IServer interface) is used
4.15 + * here.
4.16 + */
4.17 + "CreateFakeClient_Windows" "\x8B\x44\x24\x2A\x50\xB9\x2A\x2A\x2A\x2A\xE8"
4.18 + }
4.19 +
4.20 "Offsets"
4.21 {
4.22 /* Offset into IVEngineServer::CreateFakeClient */
5.1 --- a/gamedata/sdktools.games/engine.l4d.txt Sat Jan 17 13:45:51 2009 +1300
5.2 +++ b/gamedata/sdktools.games/engine.l4d.txt Thu Jan 22 15:20:45 2009 -0600
5.3 @@ -166,12 +166,25 @@
5.4 /* IServer interface pointer */
5.5 "#default"
5.6 {
5.7 + "Keys"
5.8 + {
5.9 + /* Signature for the beginning of IVEngineServer::CreateFakeClient.
5.10 + *
5.11 + * The engine binary is not actually scanned in order to look for
5.12 + * this. SourceHook is used to used to determine the address of the
5.13 + * function and this signature is used to verify that it contains
5.14 + * the expected code. A pointer to sv (IServer interface) is used
5.15 + * here.
5.16 + */
5.17 + "CreateFakeClient_Windows" "\x55\x8B\xEC\x83\xEC\x08\x89\x4D\xF8\x8B\x45\x08\x50\xB9\x2A\x2A\x2A\x2A\xE8"
5.18 + }
5.19 +
5.20 "Offsets"
5.21 {
5.22 /* Offset into IVEngineServer::CreateFakeClient */
5.23 "sv"
5.24 {
5.25 - "windows" "6"
5.26 + "windows" "14"
5.27 }
5.28 }
5.29