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.
This commit is contained in:
parent
80602b011d
commit
22675ba2e7
@ -83,12 +83,49 @@ void InitializeValveGlobals()
|
|||||||
}
|
}
|
||||||
#endif
|
#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;
|
size_t real_bytes = 0;
|
||||||
unsigned char *addr2 = (unsigned char *)_addr2;
|
size_t length = strlen(str);
|
||||||
|
|
||||||
for (size_t i=0; i<len; i++)
|
for (size_t i=0; i<length; i++)
|
||||||
|
{
|
||||||
|
if (real_bytes >= 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] == '*')
|
if (addr2[i] == '*')
|
||||||
continue;
|
continue;
|
||||||
@ -100,34 +137,20 @@ bool vcmp(const void *_addr1, const void *_addr2, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined PLATFORM_WINDOWS
|
#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()
|
void GetIServer()
|
||||||
{
|
{
|
||||||
|
const char *sigstr;
|
||||||
|
char sig[32];
|
||||||
|
size_t siglen;
|
||||||
int offset;
|
int offset;
|
||||||
void *vfunc = NULL;
|
void *vfunc = NULL;
|
||||||
|
|
||||||
/* Get the offset into CreateFakeClient */
|
|
||||||
if (!g_pGameConf->GetOffset("sv", &offset))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if defined METAMOD_PLAPI_VERSION
|
#if defined METAMOD_PLAPI_VERSION
|
||||||
/* Get the CreateFakeClient function pointer */
|
/* Get the CreateFakeClient function pointer */
|
||||||
if (!(vfunc=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient)))
|
if (!(vfunc=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient)))
|
||||||
{
|
{
|
||||||
return;
|
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<IServer **>(reinterpret_cast<unsigned char *>(vfunc) + offset);
|
|
||||||
#else
|
#else
|
||||||
/* Get the interface manually */
|
/* Get the interface manually */
|
||||||
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
|
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
|
||||||
@ -139,14 +162,33 @@ void GetIServer()
|
|||||||
void **vtable = *reinterpret_cast<void ***>(enginePatch->GetThisPtr() + info.thisptroffs + info.vtbloffs);
|
void **vtable = *reinterpret_cast<void ***>(enginePatch->GetThisPtr() + info.thisptroffs + info.vtbloffs);
|
||||||
vfunc = vtable[info.vtblindex];
|
vfunc = vtable[info.vtblindex];
|
||||||
}
|
}
|
||||||
/* Check if we're on the expected function */
|
#endif
|
||||||
if (!vcmp(vfunc, ISERVER_WIN_SIG, ISERVER_WIN_SIG_LEN))
|
|
||||||
|
/* Get signature string for IVEngineServer::CreateFakeClient() */
|
||||||
|
sigstr = g_pGameConf->GetKeyValue("CreateFakeClient_Windows");
|
||||||
|
|
||||||
|
if (!sigstr)
|
||||||
{
|
{
|
||||||
return;
|
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<IServer **>(reinterpret_cast<unsigned char *>(vfunc) + offset);
|
iserver = *reinterpret_cast<IServer **>(reinterpret_cast<unsigned char *>(vfunc) + offset);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#elif defined PLATFORM_POSIX
|
#elif defined PLATFORM_POSIX
|
||||||
void GetIServer()
|
void GetIServer()
|
||||||
|
@ -962,8 +962,14 @@ static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params)
|
|||||||
|
|
||||||
static cell_t SetClientInfo(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]);
|
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)
|
if (player == NULL || pClient == NULL)
|
||||||
{
|
{
|
||||||
return pContext->ThrowNativeError("Invalid client index %d", params[1]);
|
return pContext->ThrowNativeError("Invalid client index %d", params[1]);
|
||||||
|
@ -196,6 +196,19 @@
|
|||||||
/* IServer interface pointer */
|
/* IServer interface pointer */
|
||||||
"#default"
|
"#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"
|
"Offsets"
|
||||||
{
|
{
|
||||||
/* Offset into IVEngineServer::CreateFakeClient */
|
/* Offset into IVEngineServer::CreateFakeClient */
|
||||||
|
@ -167,6 +167,19 @@
|
|||||||
/* IServer interface pointer */
|
/* IServer interface pointer */
|
||||||
"#default"
|
"#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"
|
"Offsets"
|
||||||
{
|
{
|
||||||
/* Offset into IVEngineServer::CreateFakeClient */
|
/* Offset into IVEngineServer::CreateFakeClient */
|
||||||
|
@ -166,12 +166,25 @@
|
|||||||
/* IServer interface pointer */
|
/* IServer interface pointer */
|
||||||
"#default"
|
"#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"
|
"Offsets"
|
||||||
{
|
{
|
||||||
/* Offset into IVEngineServer::CreateFakeClient */
|
/* Offset into IVEngineServer::CreateFakeClient */
|
||||||
"sv"
|
"sv"
|
||||||
{
|
{
|
||||||
"windows" "6"
|
"windows" "14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user