diff --git a/addons/source-python/packages/source-python/memory/hooks.py b/addons/source-python/packages/source-python/memory/hooks.py index 1c45bc113..f4ac953f2 100644 --- a/addons/source-python/packages/source-python/memory/hooks.py +++ b/addons/source-python/packages/source-python/memory/hooks.py @@ -26,7 +26,8 @@ 'PreHook', 'set_hooks_disabled', 'get_hooks_disabled', - 'hooks_disabled' + 'hooks_disabled', + 'use_pre_registers', ) @@ -86,6 +87,56 @@ class PostHook(_Hook): # ============================================================================= # >> FUNCTIONS # ============================================================================= +@contextmanager +def use_pre_registers(stack_data, value=True): + """Temporarily set ``StackData.use_pre_registers`` to the given value. + When the context ends, the previous value is restored. + + Some functions overwrite CPU registers during their execution with values + from their internal calculations. In a post-hook, you have access to the + modified CPU registers, but in some cases you might want to access the + registers that were saved before the pre-hook was called. In that case you + can use this context manager to get access to the previous state of the + registers. On Windows this is often required when hooking THISCALL + functions, because the this-pointer is saved in the CPU register ``ECX``, + but gets overwritten during the execution of the hooked function. So, in a + post-hook you won't have access to the this-pointer anymore. + + Example (CS:S/Windows): + + .. code:: python + + from entities.hooks import EntityCondition + from entities.hooks import EntityPostHook + from entities.hooks import EntityPreHook + + from memory.hooks import use_pre_registers + + @EntityPreHook(EntityCondition.is_player, 'drop_weapon') + def pre_on_drop_weapon(stack_data): + print(f'PRE: this = {stack_data[0].address}') + + @EntityPostHook(EntityCondition.is_player, 'drop_weapon') + def post_on_drop_weapon(stack_data, ret_val): + print(f'POST FALSE: this = {stack_data[0].address}') + with use_pre_registers(stack_data): + print(f'POST CORRECT: this = {stack_data[0].address}') + + Output: + + .. code:: + + PRE: this = 546778280 + POST FALSE: this = 16439007 + POST CORRECT: this = 546778280 + """ + old = stack_data.use_pre_registers + stack_data.use_pre_registers = value + try: + yield + finally: + stack_data.use_pre_registers = old + @contextmanager def hooks_disabled(disabled=True): """Temporarily disable or enable all hook callbacks. By default hooks are diff --git a/src/core/modules/memory/memory_hooks.cpp b/src/core/modules/memory/memory_hooks.cpp index f23800a0c..01c39660e 100755 --- a/src/core/modules/memory/memory_hooks.cpp +++ b/src/core/modules/memory/memory_hooks.cpp @@ -173,9 +173,10 @@ object CStackData::GetItem(unsigned int iIndex) BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range.") // Argument already cached? - object retval = m_mapCache[iIndex]; - if (retval) - return retval; + object retval; + //object retval = m_mapCache[iIndex]; + //if (retval) + // return retval; switch(m_pHook->m_pCallingConvention->m_vecArgTypes[iIndex]) { @@ -196,7 +197,7 @@ object CStackData::GetItem(unsigned int iIndex) case DATA_TYPE_STRING: retval = GetArgument(m_pHook, iIndex); break; default: BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Unknown type.") break; } - m_mapCache[iIndex] = retval; + //m_mapCache[iIndex] = retval; return retval; } @@ -206,7 +207,7 @@ void CStackData::SetItem(unsigned int iIndex, object value) BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range.") // Update cache - m_mapCache[iIndex] = value; + //m_mapCache[iIndex] = value; switch(m_pHook->m_pCallingConvention->m_vecArgTypes[iIndex]) { case DATA_TYPE_BOOL: SetArgument(m_pHook, iIndex, value); break; diff --git a/src/core/modules/memory/memory_hooks.h b/src/core/modules/memory/memory_hooks.h index c01efd4e9..7cc9ecd50 100644 --- a/src/core/modules/memory/memory_hooks.h +++ b/src/core/modules/memory/memory_hooks.h @@ -51,20 +51,30 @@ class CStackData void SetItem(unsigned int iIndex, object value); CRegisters* GetRegisters() - { return m_pHook->m_pRegisters; } + { return m_pHook->GetRegisters(); } str __repr__() { return str(tuple(ptr(this))); } void* GetReturnAddress() { - void* pESP = m_pHook->m_pRegisters->m_esp->GetValue(); + void* pESP = m_pHook->GetRegisters()->m_esp->GetValue(); if (m_pHook->m_RetAddr.count(pESP) == 0) { return NULL; } return m_pHook->m_RetAddr[pESP].back(); } + bool GetUsePreRegister() + { + return m_pHook->m_bUsePreRegisters; + } + + void SetUsePreRegisters(bool value) + { + m_pHook->m_bUsePreRegisters = value; + } + protected: CHook* m_pHook; std::map m_mapCache; diff --git a/src/core/modules/memory/memory_wrap.cpp b/src/core/modules/memory/memory_wrap.cpp index 593e27f94..828d9942a 100755 --- a/src/core/modules/memory/memory_wrap.cpp +++ b/src/core/modules/memory/memory_wrap.cpp @@ -635,6 +635,11 @@ void export_stack_data(scope _memory) .add_property("registers", make_function(&CStackData::GetRegisters, reference_existing_object_policy()) ) + + .add_property( + "use_pre_registers", + &CStackData::GetUsePreRegister, + &CStackData::SetUsePreRegisters) ; } diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.cpp b/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.cpp deleted file mode 100644 index 29dd3aa87..000000000 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/** -* ============================================================================= -* DynamicHooks -* Copyright (C) 2015 Robin Gohmert. All rights reserved. -* ============================================================================= -* -* This software is provided 'as-is', without any express or implied warranty. -* In no event will the authors be held liable for any damages arising from -* the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software in a -* product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -* -fPIC thunks correctly -* -* Idea and trampoline code taken from DynDetours (thanks your-name-here). -*/ - -// ============================================================================ -// >> INCLUDES -// ============================================================================ -#include "x86MsCdecl.h" -#include - - -// ============================================================================ -// >> x86MsCdecl -// ============================================================================ -x86MsCdecl::x86MsCdecl(std::vector vecArgTypes, DataType_t returnType, int iAlignment) : - ICallingConvention(vecArgTypes, returnType, iAlignment) -{ - int iSize = GetDataTypeSize(m_returnType); - if (iSize > 4) - { - m_pReturnBuffer = malloc(iSize); - } - else - { - m_pReturnBuffer = NULL; - } -} - -x86MsCdecl::~x86MsCdecl() -{ - if (m_pReturnBuffer) - { - free(m_pReturnBuffer); - } -} - -std::list x86MsCdecl::GetRegisters() -{ - std::list registers; - - registers.push_back(ESP); - - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - { - registers.push_back(ST0); - } - else - { - registers.push_back(EAX); - if (m_pReturnBuffer) - { - registers.push_back(EDX); - } - } - - return registers; -} - -int x86MsCdecl::GetPopSize() -{ - return 0; -} - -void* x86MsCdecl::GetArgumentPtr(int iIndex, CRegisters* pRegisters) -{ - int iOffset = 4; - for(int i=0; i < iIndex; i++) - { - iOffset += GetDataTypeSize(m_vecArgTypes[i], m_iAlignment); - } - - return (void *) (pRegisters->m_esp->GetValue() + iOffset); -} - -void x86MsCdecl::ArgumentPtrChanged(int iIndex, CRegisters* pRegisters, void* pArgumentPtr) -{ -} - -void* x86MsCdecl::GetReturnPtr(CRegisters* pRegisters) -{ - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - return pRegisters->m_st0->m_pAddress; - - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(m_pReturnBuffer, pRegisters->m_eax, 4); - memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4); - return m_pReturnBuffer; - } - - return pRegisters->m_eax->m_pAddress; -} - -void x86MsCdecl::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) -{ - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(pRegisters->m_eax, m_pReturnBuffer, 4); - memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4); - } -} \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.h b/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.h index 612b70662..846ec4579 100644 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.h +++ b/src/thirdparty/DynamicHooks/include/conventions/x86MsCdecl.h @@ -77,6 +77,7 @@ class x86MsCdecl: public ICallingConvention private: void* m_pReturnBuffer; + int* m_pOffsets; }; #endif // _X86_MS_CDECL_H \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsFastcall.h b/src/thirdparty/DynamicHooks/include/conventions/x86MsFastcall.h index f6e7e04c0..34340f687 100755 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsFastcall.h +++ b/src/thirdparty/DynamicHooks/include/conventions/x86MsFastcall.h @@ -78,6 +78,7 @@ class x86MsFastcall: public ICallingConvention private: void* m_pReturnBuffer; + int* m_pOffsets; }; #endif // _X86_MS_FASTCALL_H \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.cpp b/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.cpp deleted file mode 100644 index 7441c52e7..000000000 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/** -* ============================================================================= -* DynamicHooks -* Copyright (C) 2015 Robin Gohmert. All rights reserved. -* ============================================================================= -* -* This software is provided 'as-is', without any express or implied warranty. -* In no event will the authors be held liable for any damages arising from -* the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software in a -* product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -* -fPIC thunks correctly -* -* Idea and trampoline code taken from DynDetours (thanks your-name-here). -*/ - -// ============================================================================ -// >> INCLUDES -// ============================================================================ -#include "x86MsStdcall.h" -#include - - -// ============================================================================ -// >> x86MsStdcall -// ============================================================================ -x86MsStdcall::x86MsStdcall(std::vector vecArgTypes, DataType_t returnType, int iAlignment) : - ICallingConvention(vecArgTypes, returnType, iAlignment) -{ - int iSize = GetDataTypeSize(m_returnType); - if (iSize > 4) - { - m_pReturnBuffer = malloc(iSize); - } - else - { - m_pReturnBuffer = NULL; - } -} - -x86MsStdcall::~x86MsStdcall() -{ - if (m_pReturnBuffer) - { - free(m_pReturnBuffer); - } -} - -std::list x86MsStdcall::GetRegisters() -{ - std::list registers; - - registers.push_back(ESP); - - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - { - registers.push_back(ST0); - } - else - { - registers.push_back(EAX); - if (m_pReturnBuffer) - { - registers.push_back(EDX); - } - } - - return registers; -} - -int x86MsStdcall::GetPopSize() -{ - int iPopSize = 0; - - for(unsigned int i=0; i < m_vecArgTypes.size(); i++) - { - iPopSize += GetDataTypeSize(m_vecArgTypes[i], m_iAlignment); - } - - return iPopSize; -} - -void* x86MsStdcall::GetArgumentPtr(int iIndex, CRegisters* pRegisters) -{ - int iOffset = 4; - for(int i=0; i < iIndex; i++) - { - iOffset += GetDataTypeSize(m_vecArgTypes[i], m_iAlignment); - } - - return (void *) (pRegisters->m_esp->GetValue() + iOffset); -} - -void x86MsStdcall::ArgumentPtrChanged(int iIndex, CRegisters* pRegisters, void* pArgumentPtr) -{ -} - -void* x86MsStdcall::GetReturnPtr(CRegisters* pRegisters) -{ - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - return pRegisters->m_st0->m_pAddress; - - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(m_pReturnBuffer, pRegisters->m_eax, 4); - memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4); - return m_pReturnBuffer; - } - - return pRegisters->m_eax->m_pAddress; -} - -void x86MsStdcall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) -{ - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(pRegisters->m_eax, m_pReturnBuffer, 4); - memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4); - } -} \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.h b/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.h index 1f8653439..2cd997ac6 100644 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.h +++ b/src/thirdparty/DynamicHooks/include/conventions/x86MsStdcall.h @@ -77,6 +77,7 @@ class x86MsStdcall: public ICallingConvention private: void* m_pReturnBuffer; + int* m_pOffsets; }; #endif // _X86_MS_STDCALL_H \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.cpp b/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.cpp deleted file mode 100644 index 8dcc27880..000000000 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/** -* ============================================================================= -* DynamicHooks -* Copyright (C) 2015 Robin Gohmert. All rights reserved. -* ============================================================================= -* -* This software is provided 'as-is', without any express or implied warranty. -* In no event will the authors be held liable for any damages arising from -* the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software in a -* product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -* -fPIC thunks correctly -* -* Idea and trampoline code taken from DynDetours (thanks your-name-here). -*/ - -// ============================================================================ -// >> INCLUDES -// ============================================================================ -#include "x86MsThiscall.h" -#include - - -// ============================================================================ -// >> x86MsThiscall -// ============================================================================ -x86MsThiscall::x86MsThiscall(std::vector vecArgTypes, DataType_t returnType, int iAlignment) : - ICallingConvention(vecArgTypes, returnType, iAlignment) -{ - int iSize = GetDataTypeSize(m_returnType); - if (iSize > 4) - { - m_pReturnBuffer = malloc(iSize); - } - else - { - m_pReturnBuffer = NULL; - } -} - -x86MsThiscall::~x86MsThiscall() -{ - if (m_pReturnBuffer) - { - free(m_pReturnBuffer); - } -} - -std::list x86MsThiscall::GetRegisters() -{ - std::list registers; - - registers.push_back(ESP); - registers.push_back(ECX); - - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - { - registers.push_back(ST0); - } - else - { - registers.push_back(EAX); - if (m_pReturnBuffer) - { - registers.push_back(EDX); - } - } - - return registers; -} - -int x86MsThiscall::GetPopSize() -{ - int iPopSize = 0; - - for(unsigned int i=1; i <= m_vecArgTypes.size() - 1; i++) - { - iPopSize += GetDataTypeSize(m_vecArgTypes[i], m_iAlignment); - } - - return iPopSize; -} - -void* x86MsThiscall::GetArgumentPtr(int iIndex, CRegisters* pRegisters) -{ - if (iIndex == 0) - { - return pRegisters->m_ecx->m_pAddress; - } - - int iOffset = 4; - for(int i=1; i < iIndex; i++) - { - iOffset += GetDataTypeSize(m_vecArgTypes[i], m_iAlignment); - } - - return (void *) (pRegisters->m_esp->GetValue() + iOffset); -} - -void x86MsThiscall::ArgumentPtrChanged(int iIndex, CRegisters* pRegisters, void* pArgumentPtr) -{ -} - -void* x86MsThiscall::GetReturnPtr(CRegisters* pRegisters) -{ - if (m_returnType == DATA_TYPE_FLOAT || m_returnType == DATA_TYPE_DOUBLE) - return pRegisters->m_st0->m_pAddress; - - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(m_pReturnBuffer, pRegisters->m_eax, 4); - memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4); - return m_pReturnBuffer; - } - - return pRegisters->m_eax->m_pAddress; -} - -void x86MsThiscall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) -{ - if (m_pReturnBuffer) - { - // First half in eax, second half in edx - memcpy(pRegisters->m_eax, m_pReturnBuffer, 4); - memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4); - } -} \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.h b/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.h index 90cbf6033..0308fd5de 100644 --- a/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.h +++ b/src/thirdparty/DynamicHooks/include/conventions/x86MsThiscall.h @@ -78,6 +78,7 @@ class x86MsThiscall: public ICallingConvention private: void* m_pReturnBuffer; + int* m_pOffsets; }; #endif // _X86_MS_THISCALL_H \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/include/hook.h b/src/thirdparty/DynamicHooks/include/hook.h index 36589c149..2a5452c91 100644 --- a/src/thirdparty/DynamicHooks/include/hook.h +++ b/src/thirdparty/DynamicHooks/include/hook.h @@ -114,42 +114,51 @@ class CHook @param pFunc The hook handler that should be checked. */ bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc); + + + /* + Return the CRegisters pointer based on m_bUsePreRegister. It returns + m_pRegisterPre in a pre-hook and m_pRegisterPost in a post-hook. + */ + CRegisters* GetRegisters(); template T GetArgument(int iIndex) { - return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters); + return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, GetRegisters()); } template void SetArgument(int iIndex, T value) { - void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters); + CRegisters* pRegisters = GetRegisters(); + void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, pRegisters); *(T *) pPtr = value; - m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr); + m_pCallingConvention->ArgumentPtrChanged(iIndex, pRegisters, pPtr); } template T GetReturnValue() { - return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters); + return *(T *) m_pCallingConvention->GetReturnPtr(GetRegisters()); } template void SetReturnValue(T value) { - void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters); + CRegisters* pRegisters = GetRegisters(); + void* pPtr = m_pCallingConvention->GetReturnPtr(pRegisters); *(T *) pPtr = value; - m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr); + m_pCallingConvention->ReturnPtrChanged(pRegisters, pPtr); } private: void* CreateBridge(); void Write_ModifyReturnAddress(Assembler& a); - void Write_CallHandler(Assembler& a, HookType_t type); - void Write_SaveRegisters(Assembler& a); - void Write_RestoreRegisters(Assembler& a); + void Write_CallHandler(Assembler& a, HookType_t type, CRegisters* pRegisters); + void Write_SaveRegisters(Assembler& a, CRegisters* pRegisters); + void Write_RestoreRegisters(Assembler& a, CRegisters* pRegisters); void* CreatePostCallback(); @@ -173,12 +182,15 @@ class CHook void* m_pTrampoline; // Register storage - CRegisters* m_pRegisters; + CRegisters* m_pRegistersPre; + CRegisters* m_pRegistersPost; // New return address void* m_pNewRetAddr; std::map > m_RetAddr; + + bool m_bUsePreRegisters; }; #endif // _HOOK_H \ No newline at end of file diff --git a/src/thirdparty/DynamicHooks/lib/DynamicHooks.lib b/src/thirdparty/DynamicHooks/lib/DynamicHooks.lib index cce7b33be..62a0b0eec 100644 Binary files a/src/thirdparty/DynamicHooks/lib/DynamicHooks.lib and b/src/thirdparty/DynamicHooks/lib/DynamicHooks.lib differ diff --git a/src/thirdparty/DynamicHooks/lib/libDynamicHooks.a b/src/thirdparty/DynamicHooks/lib/libDynamicHooks.a index 3d2cd55f1..ed615be3e 100644 Binary files a/src/thirdparty/DynamicHooks/lib/libDynamicHooks.a and b/src/thirdparty/DynamicHooks/lib/libDynamicHooks.a differ