-
-
Notifications
You must be signed in to change notification settings - Fork 451
Add better support for detouring at instruction addresses with DHooks #1969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
c1a5bf9
154ceb2
45020bd
7ec67d9
657b3dd
42b1e5e
e906917
19f15c0
ddb9066
e0e3cfb
abba523
eb780f0
5adeef8
1799c7e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/** | ||
* ============================================================================= | ||
* DynamicHooks | ||
* Copyright (C) 2015 Robin Gohmert. All rights reserved. | ||
* Copyright (C) 2018-2021 AlliedModders LLC. 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). | ||
* | ||
* Adopted to provide similar features to SourceHook by AlliedModders LLC. | ||
*/ | ||
|
||
// ============================================================================ | ||
// >> INCLUDES | ||
// ============================================================================ | ||
#include "x86Instruction.h" | ||
#include <string.h> | ||
|
||
|
||
// ============================================================================ | ||
// >> x86Instruction | ||
// ============================================================================ | ||
x86Instruction::x86Instruction(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) : | ||
ICallingConvention(vecArgTypes, returnType, iAlignment) | ||
{ | ||
} | ||
|
||
x86Instruction::~x86Instruction() | ||
{ | ||
} | ||
|
||
std::vector<Register_t> x86Instruction::GetRegisters() | ||
{ | ||
std::vector<Register_t> registers; | ||
|
||
// Save all the custom calling convention registers as well. | ||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) | ||
{ | ||
if (m_vecArgTypes[i].custom_register == None) | ||
continue; | ||
|
||
// TODO: Make sure the list is unique? Set? | ||
registers.push_back(m_vecArgTypes[i].custom_register); | ||
} | ||
|
||
return registers; | ||
} | ||
|
||
int x86Instruction::GetPopSize() | ||
{ | ||
return 0; | ||
} | ||
|
||
int x86Instruction::GetArgStackSize() | ||
{ | ||
return 0; | ||
} | ||
|
||
void** x86Instruction::GetStackArgumentPtr(CRegisters* pRegisters) | ||
{ | ||
return NULL; | ||
} | ||
|
||
int x86Instruction::GetArgRegisterSize() | ||
{ | ||
return 0; | ||
} | ||
|
||
void* x86Instruction::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters) | ||
{ | ||
if (iIndex >= m_vecArgTypes.size()) | ||
return NULL; | ||
|
||
// Check if this argument wasn't passed in a register. | ||
if (m_vecArgTypes[iIndex].custom_register == None) | ||
return NULL; | ||
|
||
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register); | ||
if (!pRegister) | ||
return NULL; | ||
|
||
return pRegister->m_pAddress; | ||
} | ||
|
||
void x86Instruction::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr) | ||
{ | ||
} | ||
|
||
void* x86Instruction::GetReturnPtr(CRegisters* pRegisters) | ||
{ | ||
return NULL; | ||
} | ||
|
||
void x86Instruction::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** | ||
* ============================================================================= | ||
* DynamicHooks | ||
* Copyright (C) 2015 Robin Gohmert. All rights reserved. | ||
* Copyright (C) 2018-2021 AlliedModders LLC. 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). | ||
* | ||
* Adopted to provide similar features to SourceHook by AlliedModders LLC. | ||
*/ | ||
|
||
#ifndef _X86_INSTRUCTION_H | ||
#define _X86_INSTRUCTION_H | ||
|
||
// ============================================================================ | ||
// >> INCLUDES | ||
// ============================================================================ | ||
#include "../convention.h" | ||
|
||
|
||
// ============================================================================ | ||
// >> CLASSES | ||
// ============================================================================ | ||
class x86Instruction: public ICallingConvention | ||
{ | ||
public: | ||
x86Instruction(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4); | ||
virtual ~x86Instruction(); | ||
|
||
virtual std::vector<Register_t> GetRegisters(); | ||
virtual int GetPopSize(); | ||
virtual int GetArgStackSize(); | ||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters); | ||
virtual int GetArgRegisterSize(); | ||
|
||
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters); | ||
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr); | ||
|
||
virtual void* GetReturnPtr(CRegisters* pRegisters); | ||
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr); | ||
|
||
private: | ||
void* m_pReturnBuffer; | ||
}; | ||
|
||
#endif // _X86_INSTRUCTION_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,6 +57,7 @@ CHook::CHook(void* pFunc, ICallingConvention* pConvention) | |
m_pFunc = pFunc; | ||
m_pRegisters = new CRegisters(pConvention->GetRegisters()); | ||
m_pCallingConvention = pConvention; | ||
m_pNewRetAddr = NULL; | ||
|
||
if (!m_hookHandler.init()) | ||
return; | ||
|
@@ -85,8 +86,8 @@ CHook::CHook(void* pFunc, ICallingConvention* pConvention) | |
// Save the trampoline | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful if you could skip instructions similar to the "supercede" mode for function calls. Imagine a mid-function detour on something like I guess you can work around this by detouring after that instruction for now, but that could force you into jump target territory for no reason. I guess we can improve that in an additional PR. This feature is pretty advanced and you should be knowing what you're doing when going this way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't even consider jump targets. Having a post routine would improve the situation, but it won't solve all cases as you mentioned yourself:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, don't really need a whole post routine for a single instruction, just a supercede implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that would just be conditionally replacing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay that does seem to work. One problem is that you can't change params and supercede at the same time currently. Should I add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thought about changing enum MRESReturn
{
MRES_ChangedHandled = -2, /**< Deprecated, use `MRES_Handled | MRES_Changed` instead */
MRES_ChangedOverride = -1, /**< Deprecated, use `MRES_Override | MRES_Changed` instead */
MRES_Ignored = 0, /**< plugin didn't take any action */
MRES_Handled, /**< plugin did something, but real function should still be called */
MRES_Override, /**< call real function, but use my return value */
MRES_Supercede = 1 << 30, /**< Skip hooked function/instruction. For functions you need to supply a return value.
For instructions you can optionally add a number to set the amount of instructions to skip (hooked instruction included in the count),
For example return `MRES_Supercede + 2` to skip 2 instructions. */
MRES_Changed = 1 << 31, /**< Add to return action to replace the params with your own values,
For example return `MRES_Handled | MRES_Changed` to change the function arguments via a pre-hook */
}; Does returning |
||
m_pTrampoline = (void *) pCopiedBytes; | ||
|
||
// Create the bridge function | ||
m_pBridge = CreateBridge(); | ||
// Create the bridge function. A post hook can't be created if it isn't a function call and won't have a stack argument pointer. | ||
m_pBridge = CreateBridge(pConvention->GetStackArgumentPtr(m_pRegisters) != NULL); | ||
|
||
// Write a jump to the bridge | ||
DoGatePatch((unsigned char *) pFunc, m_pBridge); | ||
|
@@ -102,7 +103,8 @@ CHook::~CHook() | |
|
||
// Free the asm bridge and new return address | ||
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge); | ||
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr); | ||
if (m_pNewRetAddr) | ||
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr); | ||
|
||
delete m_pRegisters; | ||
delete m_pCallingConvention; | ||
|
@@ -229,13 +231,14 @@ void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP) | |
i->value.push_back(pRetAddr); | ||
} | ||
|
||
void* CHook::CreateBridge() | ||
void* CHook::CreateBridge(bool createPostHook) | ||
{ | ||
sp::MacroAssembler masm; | ||
Label label_supercede; | ||
|
||
// Write a redirect to the post-hook code | ||
Write_ModifyReturnAddress(masm); | ||
if (createPostHook) | ||
Write_ModifyReturnAddress(masm); | ||
|
||
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned | ||
Write_CallHandler(masm, HOOKTYPE_PRE); | ||
|
@@ -244,7 +247,8 @@ void* CHook::CreateBridge() | |
// Restore the previously saved registers, so any changes will be applied | ||
Write_RestoreRegisters(masm, HOOKTYPE_PRE); | ||
|
||
masm.j(equal, &label_supercede); | ||
if (createPostHook) | ||
masm.j(equal, &label_supercede); | ||
|
||
// Jump to the trampoline | ||
masm.jmp(ExternalAddress(m_pTrampoline)); | ||
|
Uh oh!
There was an error while loading. Please reload this page.