Fixed crash in GetClientInfo() native on L4D (bug 3569, r=me).
authorScott Ehlert <ds@alliedmods.net>
Thu Jan 22 15:20:45 2009 -0600 (2009-01-22)
changeset 2535b3bfd3c1ad9f
parent 2534 7b3dc262df32
child 2539 6a02043f203b
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
extensions/sdktools/vnatives.cpp
gamedata/sdktools.games/engine.ep1.txt
gamedata/sdktools.games/engine.ep2.txt
gamedata/sdktools.games/engine.l4d.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