Skip to content

Commit

Permalink
Fixed a crash on Windows in certain cases.
Browse files Browse the repository at this point in the history
For the Blue Shift and Gunman Chronicles autostop, I decided to
hook the CMultiManager::ManagerUse function (it's exported so I
don't need to find any patterns and it servers its purpose well
allowing me to check the stuff that I need). The way that the
Detours hooking works is storing the first couple of instructions
from the target function and replacing them with a jump to the
hook, after which the hook can call a trampoline function that
contains those stored instructions as well as a jump to the rest of
the game code. Here is a screenshot of the beginning of ManagerUse:
![Disassembly](https://a.pomf.se/ksxxok.png)

It just turns out that the first 3 instructions aren't quite enough
for a jump, so Detours grabbed the fourth one as well, which
happens to be a first instruction of a loop (actually that loop is
an optimized tail call). What happens then is on certain condition
the jump back to that instruction happens in the function, which
effectively jumps into the middle of the Detours' jump instruction
thus causing the crash.

The conditions for the crash are:
- Either the game is in multiplayer,
- Or the multi_manager has the SF_MULTIMAN_CLONE flag set.
  • Loading branch information
YaLTeR committed Apr 4, 2015
1 parent bd67e82 commit 4579f96
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 30 deletions.
36 changes: 15 additions & 21 deletions BunnymodXT/modules/ServerDLL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void ServerDLL::Hook(const std::wstring& moduleName, void* moduleHandle, void* m
{ reinterpret_cast<void**>(&ORIG_CmdStart), reinterpret_cast<void*>(HOOKED_CmdStart) },
{ reinterpret_cast<void**>(&ORIG_CNihilanth__DyingThink), reinterpret_cast<void*>(HOOKED_CNihilanth__DyingThink) },
{ reinterpret_cast<void**>(&ORIG_COFGeneWorm__DyingThink), reinterpret_cast<void*>(HOOKED_COFGeneWorm__DyingThink) },
{ reinterpret_cast<void**>(&ORIG_CMultiManager__ManagerUse), reinterpret_cast<void*>(HOOKED_CMultiManager__ManagerUse) }
{ reinterpret_cast<void**>(&ORIG_CMultiManager__ManagerThink), reinterpret_cast<void*>(HOOKED_CMultiManager__ManagerThink) }
});
}

Expand All @@ -68,7 +68,7 @@ void ServerDLL::Unhook()
{ reinterpret_cast<void**>(&ORIG_CmdStart), reinterpret_cast<void*>(HOOKED_CmdStart) },
{ reinterpret_cast<void**>(&ORIG_CNihilanth__DyingThink), reinterpret_cast<void*>(HOOKED_CNihilanth__DyingThink) },
{ reinterpret_cast<void**>(&ORIG_COFGeneWorm__DyingThink), reinterpret_cast<void*>(HOOKED_COFGeneWorm__DyingThink) },
{ reinterpret_cast<void**>(&ORIG_CMultiManager__ManagerUse), reinterpret_cast<void*>(HOOKED_CMultiManager__ManagerUse) }
{ reinterpret_cast<void**>(&ORIG_CMultiManager__ManagerThink), reinterpret_cast<void*>(HOOKED_CMultiManager__ManagerThink) }
});

Clear();
Expand All @@ -86,7 +86,7 @@ void ServerDLL::Clear()
ORIG_CNihilanth__DyingThink_Linux = nullptr;
ORIG_COFGeneWorm__DyingThink = nullptr;
ORIG_COFGeneWorm__DyingThink_Linux = nullptr;
ORIG_CMultiManager__ManagerUse = nullptr;
ORIG_CMultiManager__ManagerThink = nullptr;
ORIG_CMultiManager__ManagerUse_Linux = nullptr;
ORIG_GetEntityAPI = nullptr;
ppmove = nullptr;
Expand Down Expand Up @@ -266,16 +266,16 @@ void ServerDLL::FindStuff()
}
}

ORIG_CMultiManager__ManagerUse = reinterpret_cast<_CMultiManager__ManagerUse>(MemUtils::GetSymbolAddress(m_Handle, "?ManagerUse@CMultiManager@@QAEXPAVCBaseEntity@@0W4USE_TYPE@@M@Z"));
if (ORIG_CMultiManager__ManagerUse)
EngineDevMsg("[server dll] Found CMultiManager::ManagerUse at %p.\n", ORIG_CMultiManager__ManagerUse);
ORIG_CMultiManager__ManagerThink = reinterpret_cast<_CMultiManager__ManagerThink>(MemUtils::GetSymbolAddress(m_Handle, "?ManagerThink@CMultiManager@@QAEXXZ"));
if (ORIG_CMultiManager__ManagerThink)
EngineDevMsg("[server dll] Found CMultiManager::ManagerThink at %p.\n", ORIG_CMultiManager__ManagerThink);
else {
ORIG_CMultiManager__ManagerUse_Linux = reinterpret_cast<_CMultiManager__ManagerUse_Linux>(MemUtils::GetSymbolAddress(m_Handle, "_ZN13CMultiManager10ManagerUseEP11CBaseEntityS1_8USE_TYPEf"));
if (ORIG_CMultiManager__ManagerUse_Linux)
EngineDevMsg("[server dll] Found CMultiManager::ManagerUse [Linux] at %p.\n", ORIG_CMultiManager__ManagerUse_Linux);
else {
EngineDevWarning("[server dll] Could not find CMultiManager::ManagerUse.\n");
EngineWarning("Blue Shift automatic timer stopping is not available.\n");
EngineDevWarning("[server dll] Could not find CMultiManager::ManagerThink or CMultiManager::ManagerUse.\n");
EngineWarning("Blue Shift and Gunman Chronicles automatic timer stopping is not available.\n");
}
}

Expand Down Expand Up @@ -313,14 +313,14 @@ void ServerDLL::FindStuff()
{
EngineDevWarning("[server dll] Couldn't find the pattern in GiveFnptrsToDll.\n");
EngineWarning("Serverside logging is not available.\n");
EngineWarning("Blue Shift automatic timer stopping is not available.\n");
EngineWarning("Blue Shift and Gunman Chronicles automatic timer stopping is not available.\n");
}
}
else
{
EngineDevWarning("[server dll] Couldn't get the address of GiveFnptrsToDll.\n");
EngineWarning("Serverside logging is not avaliable.\n");
EngineWarning("Blue Shift automatic timer stopping is not available.\n");
EngineWarning("Blue Shift and Gunman Chronicles automatic timer stopping is not available.\n");
}
}
}
Expand Down Expand Up @@ -497,24 +497,18 @@ HOOK_DEF_1(ServerDLL, void, __cdecl, COFGeneWorm__DyingThink_Linux, void*, thisp
return ORIG_COFGeneWorm__DyingThink_Linux(thisptr);
}

HOOK_DEF_6(ServerDLL, void, __fastcall, CMultiManager__ManagerUse, void*, thisptr, int, edx, void*, pActivator, void*, pCaller, int, useType, float, value)
HOOK_DEF_2(ServerDLL, void, __fastcall, CMultiManager__ManagerThink, void*, thisptr, int, edx)
{
if (CVars::bxt_timer_autostop.GetBool() && ppGlobals && pCaller) {
if (CVars::bxt_timer_autostop.GetBool() && ppGlobals) {
entvars_t *pev = *reinterpret_cast<entvars_t**>(reinterpret_cast<uintptr_t>(thisptr) + 4);
if (pev && pev->targetname) {
const char *targetname = (*ppGlobals)->pStringBase + pev->targetname;
if (!std::strcmp(targetname, "roll_the_credits") || !std::strcmp(targetname, "youwinmulti")) {
entvars_t *callerPev = *reinterpret_cast<entvars_t**>(reinterpret_cast<uintptr_t>(pCaller) + 4);
if (callerPev && callerPev->targetname) {
const char *callerTargetname = (*ppGlobals)->pStringBase + callerPev->targetname;
if (!std::strcmp(callerTargetname, "mgr_take_over") || !std::strcmp(callerTargetname, "endbot"))
CustomHud::SetCountingTime(false);
}
}
if (!std::strcmp(targetname, "roll_the_credits") || !std::strcmp(targetname, "youwinmulti"))
CustomHud::SetCountingTime(false);
}
}

return ORIG_CMultiManager__ManagerUse(thisptr, edx, pActivator, pCaller, useType, value);
return ORIG_CMultiManager__ManagerThink(thisptr, edx);
}

HOOK_DEF_5(ServerDLL, void, __cdecl, CMultiManager__ManagerUse_Linux, void*, thisptr, void*, pActivator, void*, pCaller, int, useType, float, value)
Expand Down
2 changes: 1 addition & 1 deletion BunnymodXT/modules/ServerDLL.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ServerDLL : public IHookableDirFilter
HOOK_DECL(void, __cdecl, CNihilanth__DyingThink_Linux, void* thisptr)
HOOK_DECL(void, __fastcall, COFGeneWorm__DyingThink, void* thisptr, int edx)
HOOK_DECL(void, __cdecl, COFGeneWorm__DyingThink_Linux, void* thisptr)
HOOK_DECL(void, __fastcall, CMultiManager__ManagerUse, void* thisptr, int edx, void* pActivator, void* pCaller, int useType, float value)
HOOK_DECL(void, __fastcall, CMultiManager__ManagerThink, void* thisptr, int edx)
HOOK_DECL(void, __cdecl, CMultiManager__ManagerUse_Linux, void* thisptr, void* pActivator, void* pCaller, int useType, float value)

public:
Expand Down
16 changes: 8 additions & 8 deletions BunnymodXT/stdafx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const double M_PI = 3.14159265358979323846;
#define HOOK_DECL(ret, call, name, ...) \
public: \
static ret call HOOKED_##name(__VA_ARGS__); \
ret call HOOKED_##name##_Func(__VA_ARGS__); \
ret HOOKED_##name##_Func(__VA_ARGS__); \
protected: \
typedef ret(call *_##name) (__VA_ARGS__); \
_##name ORIG_##name;
Expand Down Expand Up @@ -103,40 +103,40 @@ const double M_PI = 3.14159265358979323846;
ret call class::HOOKED_##name() { \
return class::GetInstance().HOOKED_##name##_Func(); \
} \
ret call class::HOOKED_##name##_Func()
ret class::HOOKED_##name##_Func()

#define HOOK_DEF_1(class, ret, call, name, t1, n1) \
ret call class::HOOKED_##name(t1 n1) { \
return class::GetInstance().HOOKED_##name##_Func(n1); \
} \
ret call class::HOOKED_##name##_Func(t1 n1)
ret class::HOOKED_##name##_Func(t1 n1)

#define HOOK_DEF_2(class, ret, call, name, t1, n1, t2, n2) \
ret call class::HOOKED_##name(t1 n1, t2 n2) { \
return class::GetInstance().HOOKED_##name##_Func(n1, n2); \
} \
ret call class::HOOKED_##name##_Func(t1 n1, t2 n2)
ret class::HOOKED_##name##_Func(t1 n1, t2 n2)

#define HOOK_DEF_3(class, ret, call, name, t1, n1, t2, n2, t3, n3) \
ret call class::HOOKED_##name(t1 n1, t2 n2, t3 n3) { \
return class::GetInstance().HOOKED_##name##_Func(n1, n2, n3); \
} \
ret call class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3)
ret class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3)

#define HOOK_DEF_4(class, ret, call, name, t1, n1, t2, n2, t3, n3, t4, n4) \
ret call class::HOOKED_##name(t1 n1, t2 n2, t3 n3, t4 n4) { \
return class::GetInstance().HOOKED_##name##_Func(n1, n2, n3, n4); \
} \
ret call class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4)
ret class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4)

#define HOOK_DEF_5(class, ret, call, name, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5) \
ret call class::HOOKED_##name(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5) { \
return class::GetInstance().HOOKED_##name##_Func(n1, n2, n3, n4, n5); \
} \
ret call class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5)
ret class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5)

#define HOOK_DEF_6(class, ret, call, name, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \
ret call class::HOOKED_##name(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6) { \
return class::GetInstance().HOOKED_##name##_Func(n1, n2, n3, n4, n5, n6); \
} \
ret call class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6)
ret class::HOOKED_##name##_Func(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6)

1 comment on commit 4579f96

@YaLTeR
Copy link
Owner Author

@YaLTeR YaLTeR commented on 4579f96 Aug 6, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since pomf is dead, here's a screenshot reupload: http://i.imgur.com/CR6idYo.png

Please # to comment.