Import DHooks + Dynamic Detouring

This is the latest DHooks version from https://github.com/peace-maker/DHooks2/tree/1314f2d1b4d870677a1b9f628c770f2799878f9b
This commit is contained in:
Peace-Maker
2021-11-17 13:17:39 +01:00
committed by peace-maker
parent 18f9d65938
commit 0f5f1a814e
41 changed files with 9999 additions and 0 deletions
+290
View File
@@ -0,0 +1,290 @@
/**
* =============================================================================
* 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 _CONVENTION_H
#define _CONVENTION_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>
#include <utility>
// ============================================================================
// >> DataType_t
// ============================================================================
enum DataType_t
{
DATA_TYPE_VOID,
DATA_TYPE_BOOL,
DATA_TYPE_CHAR,
DATA_TYPE_UCHAR,
DATA_TYPE_SHORT,
DATA_TYPE_USHORT,
DATA_TYPE_INT,
DATA_TYPE_UINT,
DATA_TYPE_LONG,
DATA_TYPE_ULONG,
DATA_TYPE_LONG_LONG,
DATA_TYPE_ULONG_LONG,
DATA_TYPE_FLOAT,
DATA_TYPE_DOUBLE,
DATA_TYPE_POINTER,
DATA_TYPE_STRING,
DATA_TYPE_OBJECT
};
typedef struct DataTypeSized_s {
DataTypeSized_s()
{
type = DATA_TYPE_POINTER;
size = 0;
custom_register = None;
}
DataType_t type;
size_t size;
Register_t custom_register;
} DataTypeSized_t;
// ============================================================================
// >> FUNCTIONS
// ============================================================================
/*
Returns the size after applying alignment.
@param <size>:
The size that should be aligned.
@param <alignment>:
The alignment that should be used.
*/
inline int Align(int size, int alignment)
{
int unaligned = size % alignment;
if (unaligned == 0)
return size;
return size + (alignment - unaligned);
}
/*
Returns the size of a data type after applying alignment.
@param <type>:
The data type you would like to get the size of.
@param <alignment>:
The alignment that should be used.
*/
inline int GetDataTypeSize(DataTypeSized_t type, int iAlignment=4)
{
switch(type.type)
{
case DATA_TYPE_VOID: return 0;
case DATA_TYPE_BOOL: return Align(sizeof(bool), iAlignment);
case DATA_TYPE_CHAR: return Align(sizeof(char), iAlignment);
case DATA_TYPE_UCHAR: return Align(sizeof(unsigned char), iAlignment);
case DATA_TYPE_SHORT: return Align(sizeof(short), iAlignment);
case DATA_TYPE_USHORT: return Align(sizeof(unsigned short), iAlignment);
case DATA_TYPE_INT: return Align(sizeof(int), iAlignment);
case DATA_TYPE_UINT: return Align(sizeof(unsigned int), iAlignment);
case DATA_TYPE_LONG: return Align(sizeof(long), iAlignment);
case DATA_TYPE_ULONG: return Align(sizeof(unsigned long), iAlignment);
case DATA_TYPE_LONG_LONG: return Align(sizeof(long long), iAlignment);
case DATA_TYPE_ULONG_LONG: return Align(sizeof(unsigned long long), iAlignment);
case DATA_TYPE_FLOAT: return Align(sizeof(float), iAlignment);
case DATA_TYPE_DOUBLE: return Align(sizeof(double), iAlignment);
case DATA_TYPE_POINTER: return Align(sizeof(void *), iAlignment);
case DATA_TYPE_STRING: return Align(sizeof(char *), iAlignment);
case DATA_TYPE_OBJECT: return type.size;
default: puts("Unknown data type.");
}
return 0;
}
// ============================================================================
// >> CLASSES
// ============================================================================
/*
This is the base class for every calling convention. Inherit from this class
to create your own calling convention.
*/
class ICallingConvention
{
public:
/*
Initializes the calling convention.
@param <vecArgTypes>:
A list of DataType_t objects, which define the arguments of the function.
@param <returnType>:
The return type of the function.
*/
ICallingConvention(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4)
{
m_vecArgTypes = std::move(vecArgTypes);
for (size_t i=0; i < m_vecArgTypes.size(); i++)
{
DataTypeSized_t &type = m_vecArgTypes[i];
if (!type.size)
type.size = GetDataTypeSize(type, iAlignment);
}
m_returnType = returnType;
if (!m_returnType.size)
m_returnType.size = GetDataTypeSize(m_returnType, iAlignment);
m_iAlignment = iAlignment;
}
virtual ~ICallingConvention()
{
}
/*
This should return a list of Register_t values. These registers will be
saved for later access.
*/
virtual std::vector<Register_t> GetRegisters() = 0;
/*
Returns the number of bytes that should be added to the stack to clean up.
*/
virtual int GetPopSize() = 0;
virtual int GetArgStackSize() = 0;
virtual void** GetStackArgumentPtr(CRegisters* pRegisters) = 0;
/*
Returns the number of bytes for the buffer to store all the arguments that are passed in a register in.
*/
virtual int GetArgRegisterSize() = 0;
/*
Returns a pointer to the argument at the given index.
@param <iIndex>:
The index of the argument.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters) = 0;
/*
*/
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr) = 0;
/*
Returns a pointer to the return value.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetReturnPtr(CRegisters* pRegisters) = 0;
/*
*/
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) = 0;
/*
Save the return value in a seperate buffer, so we can restore it after calling the original function.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveReturnValue(CRegisters* pRegisters)
{
std::unique_ptr<uint8_t[]> pSavedReturnValue = std::make_unique<uint8_t[]>(m_returnType.size);
memcpy(pSavedReturnValue.get(), GetReturnPtr(pRegisters), m_returnType.size);
m_pSavedReturnBuffers.push_back(std::move(pSavedReturnValue));
}
virtual void RestoreReturnValue(CRegisters* pRegisters)
{
uint8_t* pSavedReturnValue = m_pSavedReturnBuffers.back().get();
memcpy(GetReturnPtr(pRegisters), pSavedReturnValue, m_returnType.size);
ReturnPtrChanged(pRegisters, pSavedReturnValue);
m_pSavedReturnBuffers.pop_back();
}
/*
Save the value of arguments in a seperate buffer for the post callback.
Compiler optimizations might cause the registers or stack space to be reused
and overwritten during function execution if the value isn't needed anymore
at some point. This leads to different values in the post hook.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveCallArguments(CRegisters* pRegisters)
{
int size = GetArgStackSize() + GetArgRegisterSize();
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
virtual void RestoreCallArguments(CRegisters* pRegisters)
{
uint8_t *pSavedCallArguments = m_pSavedCallArguments.back().get();
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy(GetArgumentPtr(i, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
offset += type.size;
}
m_pSavedCallArguments.pop_back();
}
public:
std::vector<DataTypeSized_t> m_vecArgTypes;
DataTypeSized_t m_returnType;
int m_iAlignment;
// Save the return in case we call the original function and want to override the return again.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedReturnBuffers;
// Save call arguments in case the function reuses the space and overwrites the values for the post hook.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedCallArguments;
};
#endif // _CONVENTION_H
@@ -0,0 +1,48 @@
/**
* =============================================================================
* 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).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _X86_GCC_CDECL_H
#define _X86_GCC_CDECL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsCdecl.h"
// ============================================================================
// >> CLASSES
// ============================================================================
typedef x86MsCdecl x86GccCdecl;
#endif // _X86_GCC_CDECL_H
@@ -0,0 +1,87 @@
/**
* =============================================================================
* 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 "x86GccThiscall.h"
// ============================================================================
// >> CLASSES
// ============================================================================
x86GccThiscall::x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
x86GccCdecl(vecArgTypes, returnType, iAlignment)
{
// Always add the |this| pointer.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
type.size = GetDataTypeSize(type, iAlignment);
type.custom_register = None;
m_vecArgTypes.insert(m_vecArgTypes.begin(), type);
}
x86GccThiscall::~x86GccThiscall()
{
}
int x86GccThiscall::GetArgStackSize()
{
// Remove the this pointer from the arguments size.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
return x86GccCdecl::GetArgStackSize() - GetDataTypeSize(type, m_iAlignment);
}
void** x86GccThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
{
// Skip return address and this pointer.
DataTypeSized_t type;
type.type = DATA_TYPE_POINTER;
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4 + GetDataTypeSize(type, m_iAlignment));
}
void x86GccThiscall::SaveCallArguments(CRegisters* pRegisters)
{
// Count the this pointer.
int size = x86GccCdecl::GetArgStackSize() + GetArgRegisterSize();
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
@@ -0,0 +1,59 @@
/**
* =============================================================================
* 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_GCC_THISCALL_H
#define _X86_GCC_THISCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86GccCdecl.h"
// ============================================================================
// >> CLASSES
// ============================================================================
// |this| pointer is always passed as implicit first argument on the stack.
class x86GccThiscall: public x86GccCdecl
{
public:
x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment = 4);
virtual ~x86GccThiscall();
virtual int GetArgStackSize();
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
virtual void SaveCallArguments(CRegisters* pRegisters);
};
#endif // _X86_GCC_THISCALL_H
@@ -0,0 +1,187 @@
/**
* =============================================================================
* 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 "x86MsCdecl.h"
#include <string.h>
// ============================================================================
// >> x86MsCdecl
// ============================================================================
x86MsCdecl::x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsCdecl::~x86MsCdecl()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsCdecl::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// 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 x86MsCdecl::GetPopSize()
{
return 0;
}
int x86MsCdecl::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsCdecl::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsCdecl::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsCdecl::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
// Skip return address.
size_t iOffset = 4;
for(unsigned int i=0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsCdecl::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsCdecl::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == 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);
}
}
@@ -0,0 +1,88 @@
/**
* =============================================================================
* 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_MS_CDECL_H
#define _X86_MS_CDECL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- caller cleans up the stack
- all arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsCdecl: public ICallingConvention
{
public:
x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsCdecl();
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_MS_CDECL_H
@@ -0,0 +1,61 @@
/**
* =============================================================================
* 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 "x86MsFastcall.h"
// ============================================================================
// >> x86MsFastcall
// ============================================================================
x86MsFastcall::x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
x86MsStdcall(vecArgTypes, returnType, iAlignment)
{
// First argument is passed in ecx.
if (m_vecArgTypes.size() > 0) {
DataTypeSized_t &type = m_vecArgTypes[0];
// Don't force the register on the user.
// Why choose Fastcall if you set your own argument registers though..
if (type.custom_register == None)
type.custom_register = ECX;
}
// Second argument is passed in edx.
if (m_vecArgTypes.size() > 1) {
DataTypeSized_t &type = m_vecArgTypes[1];
if (type.custom_register == None)
type.custom_register = EDX;
}
}
@@ -0,0 +1,72 @@
/**
* =============================================================================
* 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_MS_FASTCALL_H
#define _X86_MS_FASTCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "x86MsStdcall.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- first parameter in ecx, second parameter in edx, rest on the stack
- stack parameter order: right-to-left
- callee cleans up the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsFastcall: public x86MsStdcall
{
public:
x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
};
#endif // _X86_MS_FASTCALL_H
@@ -0,0 +1,194 @@
/**
* =============================================================================
* 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 "x86MsStdcall.h"
#include <string.h>
// ============================================================================
// >> x86MsStdcall
// ============================================================================
x86MsStdcall::x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsStdcall::~x86MsStdcall()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsStdcall::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// 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 x86MsStdcall::GetPopSize()
{
int iPopSize = 0;
for(size_t i=0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iPopSize += m_vecArgTypes[i].size;
}
return iPopSize;
}
int x86MsStdcall::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsStdcall::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsStdcall::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsStdcall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
size_t iOffset = 4;
for (unsigned int i = 0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *)(pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsStdcall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsStdcall::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == 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);
}
}
@@ -0,0 +1,88 @@
/**
* =============================================================================
* 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_MS_STDCALL_H
#define _X86_MS_STDCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- callee cleans up the stack
- all arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsStdcall: public ICallingConvention
{
public:
x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsStdcall();
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_MS_STDCALL_H
@@ -0,0 +1,239 @@
/**
* =============================================================================
* 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 "x86MsThiscall.h"
#include <string.h>
// ============================================================================
// >> x86MsThiscall
// ============================================================================
x86MsThiscall::x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
ICallingConvention(vecArgTypes, returnType, iAlignment)
{
if (m_returnType.size > 4)
{
m_pReturnBuffer = malloc(m_returnType.size);
}
else
{
m_pReturnBuffer = NULL;
}
}
x86MsThiscall::~x86MsThiscall()
{
if (m_pReturnBuffer)
{
free(m_pReturnBuffer);
}
}
std::vector<Register_t> x86MsThiscall::GetRegisters()
{
std::vector<Register_t> registers;
registers.push_back(ESP);
// TODO: Allow custom this register.
registers.push_back(ECX);
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
{
registers.push_back(ST0);
}
else
{
registers.push_back(EAX);
if (m_pReturnBuffer)
{
registers.push_back(EDX);
}
}
// 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 x86MsThiscall::GetPopSize()
{
// This pointer.
// FIXME LINUX
//int iPopSize = GetDataTypeSize(DATA_TYPE_POINTER, m_iAlignment);
int iPopSize = 0;
for(size_t i=0; i < m_vecArgTypes.size(); i++)
{
// Only pop arguments that are actually on the stack.
if (m_vecArgTypes[i].custom_register == None)
iPopSize += m_vecArgTypes[i].size;
}
return iPopSize;
}
int x86MsThiscall::GetArgStackSize()
{
int iArgStackSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register == None)
iArgStackSize += m_vecArgTypes[i].size;
}
return iArgStackSize;
}
void** x86MsThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
{
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
}
int x86MsThiscall::GetArgRegisterSize()
{
int iArgRegisterSize = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
{
if (m_vecArgTypes[i].custom_register != None)
iArgRegisterSize += m_vecArgTypes[i].size;
}
return iArgRegisterSize;
}
void* x86MsThiscall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
{
if (iIndex == 0)
{
// TODO: Allow custom this register.
return pRegisters->m_ecx->m_pAddress;
}
// The this pointer isn't explicitly defined as an argument.
iIndex--;
if (iIndex >= m_vecArgTypes.size())
return NULL;
// Check if this argument was passed in a register.
if (m_vecArgTypes[iIndex].custom_register != None)
{
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
if (!pRegister)
return NULL;
return pRegister->m_pAddress;
}
size_t iOffset = 4;
for(unsigned int i=0; i < iIndex; i++)
{
if (m_vecArgTypes[i].custom_register == None)
iOffset += m_vecArgTypes[i].size;
}
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
}
void x86MsThiscall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
{
}
void* x86MsThiscall::GetReturnPtr(CRegisters* pRegisters)
{
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == 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);
}
}
void x86MsThiscall::SaveCallArguments(CRegisters* pRegisters)
{
// Account for implicit this-pointer in ecx.
int size = GetArgStackSize() + GetArgRegisterSize() + sizeof(void *);
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
memcpy(pSavedCallArguments.get(), GetArgumentPtr(0, pRegisters), sizeof(void *));
size_t offset = sizeof(void *);
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i + 1, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
void x86MsThiscall::RestoreCallArguments(CRegisters* pRegisters)
{
uint8_t* pSavedCallArguments = m_pSavedCallArguments.back().get();
memcpy(GetArgumentPtr(0, pRegisters), pSavedCallArguments, sizeof(void *));
size_t offset = sizeof(void *);
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy(GetArgumentPtr(i + 1, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
offset += type.size;
}
m_pSavedCallArguments.pop_back();
}
@@ -0,0 +1,93 @@
/**
* =============================================================================
* 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_MS_THISCALL_H
#define _X86_MS_THISCALL_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "../convention.h"
#include <am-vector.h>
// ============================================================================
// >> CLASSES
// ============================================================================
/*
Source: DynCall manual and Windows docs
Registers:
- eax = return value
- ecx = this pointer
- edx = return value
- esp = stack pointer
- st0 = floating point return value
Parameter passing:
- stack parameter order: right-to-left
- callee cleans up the stack
- all other arguments are pushed onto the stack
- alignment: 4 bytes
Return values:
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
- integers > 32 bits are returned via the eax and edx registers
- floating pointer types are returned via the st0 register
*/
class x86MsThiscall: public ICallingConvention
{
public:
x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
virtual ~x86MsThiscall();
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);
virtual void SaveCallArguments(CRegisters* pRegisters);
virtual void RestoreCallArguments(CRegisters* pRegisters);
private:
void* m_pReturnBuffer;
};
#endif // _X86_MS_THISCALL_H
+503
View File
@@ -0,0 +1,503 @@
/**
* =============================================================================
* 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 "hook.h"
#include "utilities.h"
#include <asm/asm.h>
#include <macro-assembler-x86.h>
#include "extension.h"
using namespace sp;
// ============================================================================
// >> DEFINITIONS
// ============================================================================
#define JMP_SIZE 6
// ============================================================================
// >> CHook
// ============================================================================
CHook::CHook(void* pFunc, ICallingConvention* pConvention)
{
m_pFunc = pFunc;
m_pRegisters = new CRegisters(pConvention->GetRegisters());
m_pCallingConvention = pConvention;
if (!m_hookHandler.init())
return;
if (!m_RetAddr.init())
return;
unsigned char* pTarget = (unsigned char *) pFunc;
// Determine the number of bytes we need to copy
int iBytesToCopy = copy_bytes(pTarget, NULL, JMP_SIZE);
// Create a buffer for the bytes to copy + a jump to the rest of the
// function.
unsigned char* pCopiedBytes = (unsigned char *) smutils->GetScriptingEngine()->AllocatePageMemory(iBytesToCopy + JMP_SIZE);
// Fill the array with NOP instructions
memset(pCopiedBytes, 0x90, iBytesToCopy + JMP_SIZE);
// Copy the required bytes to our array
copy_bytes(pTarget, pCopiedBytes, JMP_SIZE);
// Write a jump after the copied bytes to the function/bridge + number of bytes to copy
WriteJMP(pCopiedBytes + iBytesToCopy, pTarget + iBytesToCopy);
// Save the trampoline
m_pTrampoline = (void *) pCopiedBytes;
// Create the bridge function
m_pBridge = CreateBridge();
// Write a jump to the bridge
WriteJMP((unsigned char *) pFunc, m_pBridge);
}
CHook::~CHook()
{
// Copy back the previously copied bytes
copy_bytes((unsigned char *) m_pTrampoline, (unsigned char *) m_pFunc, JMP_SIZE);
// Free the trampoline buffer
smutils->GetScriptingEngine()->FreePageMemory(m_pTrampoline);
// Free the asm bridge and new return address
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge);
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr);
delete m_pRegisters;
delete m_pCallingConvention;
}
void CHook::AddCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
if (!pCallback)
return;
HookTypeMap::Insert i = m_hookHandler.findForAdd(eHookType);
if (!i.found()) {
HookHandlerSet set;
set.init();
m_hookHandler.add(i, eHookType, std::move(set));
}
i->value.add(pCallback);
}
void CHook::RemoveCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return;
r->value.removeIfExists(pCallback);
}
bool CHook::IsCallbackRegistered(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return false;
return r->value.has(pCallback);
}
bool CHook::AreCallbacksRegistered()
{
HookTypeMap::Result r = m_hookHandler.find(HOOKTYPE_PRE);
if (r.found() && r->value.elements() > 0)
return true;
r = m_hookHandler.find(HOOKTYPE_POST);
if (r.found() && r->value.elements() > 0)
return true;
return false;
}
ReturnAction_t CHook::HookHandler(HookType_t eHookType)
{
if (eHookType == HOOKTYPE_POST)
{
ReturnAction_t lastPreReturnAction = m_LastPreReturnAction.back();
m_LastPreReturnAction.pop_back();
if (lastPreReturnAction == ReturnAction_Override)
m_pCallingConvention->RestoreReturnValue(m_pRegisters);
if (lastPreReturnAction < ReturnAction_Supercede)
m_pCallingConvention->RestoreCallArguments(m_pRegisters);
}
ReturnAction_t returnAction = ReturnAction_Ignored;
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
{
// Still save the arguments for the post hook even if there
// is no pre-handler registered.
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
HookHandlerSet &callbacks = r->value;
for(HookHandlerSet::iterator it=callbacks.iter(); !it.empty(); it.next())
{
ReturnAction_t result = ((HookHandlerFn) *it)(eHookType, this);
if (result > returnAction)
returnAction = result;
}
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
if (returnAction == ReturnAction_Override)
m_pCallingConvention->SaveReturnValue(m_pRegisters);
if (returnAction < ReturnAction_Supercede)
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
void* __cdecl CHook::GetReturnAddress(void* pESP)
{
ReturnAddressMap::Result r = m_RetAddr.find(pESP);
assert(r.found());
if (!r.found())
{
smutils->LogError(myself, "FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.");
return NULL;
}
void *pRetAddr = r->value.back();
r->value.pop_back();
// Clear the stack address from the cache now that we ran the last post hook.
if (r->value.empty())
m_RetAddr.remove(r);
return pRetAddr;
}
void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
{
ReturnAddressMap::Insert i = m_RetAddr.findForAdd(pESP);
if (!i.found())
m_RetAddr.add(i, pESP, std::vector<void *>());
i->value.push_back(pRetAddr);
}
void* CHook::CreateBridge()
{
sp::MacroAssembler masm;
Label label_supercede;
// Write a redirect to the post-hook code
Write_ModifyReturnAddress(masm);
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
Write_CallHandler(masm, HOOKTYPE_PRE);
masm.cmpb(r8_al, ReturnAction_Supercede);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_PRE);
masm.j(equal, &label_supercede);
// Jump to the trampoline
masm.jmp(ExternalAddress(m_pTrampoline));
// This code will be executed if a pre-hook returns ReturnAction_Supercede
masm.bind(&label_supercede);
// Finally, return to the caller
// This will still call post hooks, but will skip the original function.
masm.ret(m_pCallingConvention->GetPopSize());
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
{
// Save scratch registers that are used by SetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Store the return address in eax
masm.movl(eax, Operand(esp, 0));
// Save the original return address by using the current esp as the key.
// This should be unique until we have returned to the original caller.
void (__cdecl CHook::*SetReturnAddress)(void*, void*) = &CHook::SetReturnAddress;
masm.push(esp);
masm.push(eax);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)SetReturnAddress));
masm.addl(esp, 12);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Override the return address. This is a redirect to our post-hook code
m_pNewRetAddr = CreatePostCallback();
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
}
void* CHook::CreatePostCallback()
{
sp::MacroAssembler masm;
int iPopSize = m_pCallingConvention->GetPopSize();
// Subtract the previously added bytes (stack size + return address), so
// that we can access the arguments again
masm.subl(esp, iPopSize+4);
// Call the post-hook handler
Write_CallHandler(masm, HOOKTYPE_POST);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_POST);
// Save scratch registers that are used by GetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Get the original return address
void* (__cdecl CHook::*GetReturnAddress)(void*) = &CHook::GetReturnAddress;
masm.push(esp);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)GetReturnAddress));
masm.addl(esp, 8);
// Save the original return address
static void* pRetAddr = NULL;
masm.movl(Operand(ExternalAddress(&pRetAddr)), eax);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Add the bytes again to the stack (stack size + return address), so we
// don't corrupt the stack.
masm.addl(esp, iPopSize+4);
// Jump to the original return address
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
// Generate the code
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
{
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t) = &CHook::HookHandler;
// Save the registers so that we can access them in our handlers
Write_SaveRegisters(masm, type);
// Align the stack to 16 bytes.
masm.subl(esp, 8);
// Call the global hook handler
masm.push(type);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)HookHandler));
masm.addl(esp, 16);
}
void CHook::Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch(vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress)), r8_al); break;
case CL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress)), r8_cl); break;
case DL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress)), r8_dl); break;
case BL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress)), r8_bl); break;
case AH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress)), r8_ah); break;
case CH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress)), r8_ch); break;
case DH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress)), r8_dh); break;
case BH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress)), r8_bh); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress)), eax); break;
case ECX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress)), ecx); break;
case EDX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress)), edx); break;
case EBX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress)), ebx); break;
case ESP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress)), esp); break;
case EBP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress)), ebp); break;
case ESI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress)), esi); break;
case EDI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress)), edi); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress)), xmm0); break;
case XMM1: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress)), xmm1); break;
case XMM2: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress)), xmm2); break;
case XMM3: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress)), xmm3); break;
case XMM4: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress)), xmm4); break;
case XMM5: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress)), xmm5); break;
case XMM6: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress)), xmm6); break;
case XMM7: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress)), xmm7); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
// Don't mess with the FPU stack in a pre-hook. The float return is returned in st0,
// so only load it in a post hook to avoid writing back NaN.
if (type == HOOKTYPE_POST)
masm.fst32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
break;
//case ST1: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress)), st1); break;
//case ST2: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress)), st2); break;
//case ST3: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress)), st3); break;
//case ST4: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress)), st4); break;
//case ST5: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress)), st5); break;
//case ST6: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress)), st6); break;
//case ST7: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress)), st7); break;
default: puts("Unsupported register.");
}
}
}
void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for (size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch (vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(r8_al, Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress))); break;
case CL: masm.movl(r8_cl, Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress))); break;
case DL: masm.movl(r8_dl, Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress))); break;
case BL: masm.movl(r8_bl, Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress))); break;
case AH: masm.movl(r8_ah, Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress))); break;
case CH: masm.movl(r8_ch, Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress))); break;
case DH: masm.movl(r8_dh, Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress))); break;
case BH: masm.movl(r8_bh, Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress))); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(eax, Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress))); break;
case ECX: masm.movl(ecx, Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress))); break;
case EDX: masm.movl(edx, Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress))); break;
case EBX: masm.movl(ebx, Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress))); break;
case ESP: masm.movl(esp, Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress))); break;
case EBP: masm.movl(ebp, Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress))); break;
case ESI: masm.movl(esi, Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress))); break;
case EDI: masm.movl(edi, Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress))); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(xmm0, Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress))); break;
case XMM1: masm.movaps(xmm1, Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress))); break;
case XMM2: masm.movaps(xmm2, Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress))); break;
case XMM3: masm.movaps(xmm3, Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress))); break;
case XMM4: masm.movaps(xmm4, Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress))); break;
case XMM5: masm.movaps(xmm5, Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress))); break;
case XMM6: masm.movaps(xmm6, Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress))); break;
case XMM7: masm.movaps(xmm7, Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress))); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
if (type == HOOKTYPE_POST) {
// Replace the top of the FPU stack.
// Copy st0 to st0 and pop -> just pop the FPU stack.
masm.fstp(st0);
// Push a value to the FPU stack.
// TODO: Only write back when changed? Save full 80bits for that case.
// Avoid truncation of the data if it's unchanged.
masm.fld32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
}
break;
//case ST1: masm.movl(st1, tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress))); break;
//case ST2: masm.movl(st2, tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress))); break;
//case ST3: masm.movl(st3, tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress))); break;
//case ST4: masm.movl(st4, tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress))); break;
//case ST5: masm.movl(st5, tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress))); break;
//case ST6: masm.movl(st6, tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress))); break;
//case ST7: masm.movl(st7, tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress))); break;
default: puts("Unsupported register.");
}
}
}
+217
View File
@@ -0,0 +1,217 @@
/**
* =============================================================================
* 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 _HOOK_H
#define _HOOK_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include "convention.h"
#include <am-hashmap.h>
#include <am-hashset.h>
// ============================================================================
// >> HookType_t
// ============================================================================
enum HookType_t
{
// Callback will be executed before the original function.
HOOKTYPE_PRE,
// Callback will be executed after the original function.
HOOKTYPE_POST
};
enum ReturnAction_t
{
ReturnAction_Ignored, // handler didn't take any action
ReturnAction_Handled, // plugin did something, but real function should still be called
ReturnAction_Override, // call real function, but use my return value
ReturnAction_Supercede // skip real function; use my return value
};
// ============================================================================
// >> TYPEDEFS
// ============================================================================
class CHook;
typedef ReturnAction_t (*HookHandlerFn)(HookType_t, CHook*);
#ifdef __linux__
#define __cdecl
#endif
struct IntegerPolicy
{
static inline uint32_t hash(size_t i) {
return ke::HashInteger<sizeof(size_t)>(i);
}
static inline bool matches(size_t i1, size_t i2) {
return i1 == i2;
}
};
typedef ke::HashSet<HookHandlerFn*, ke::PointerPolicy<HookHandlerFn>> HookHandlerSet;
typedef ke::HashMap<HookType_t, HookHandlerSet, IntegerPolicy> HookTypeMap;
typedef ke::HashMap<void*, std::vector<void*>, ke::PointerPolicy<void>> ReturnAddressMap;
namespace sp
{
class MacroAssembler;
}
// ============================================================================
// >> CLASSES
// ============================================================================
class CHook
{
private:
friend class CHookManager;
/*
Creates a new function hook.
@param <pFunc>:
The address of the function to hook
@param <pConvention>:
The calling convention of <pFunc>.
*/
CHook(void* pFunc, ICallingConvention* pConvention);
~CHook();
public:
/*
Adds a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be added.
*/
void AddCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Removes a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be removed.
*/
void RemoveCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if a hook handler is already added.
@param type The hook type.
@param pFunc The hook handler that should be checked.
*/
bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if there are any hook handlers added to this hook.
*/
bool AreCallbacksRegistered();
template<class T>
T GetArgument(int iIndex)
{
return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
}
template<class T>
void SetArgument(int iIndex, T value)
{
void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr);
}
template<class T>
T GetReturnValue()
{
return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters);
}
template<class T>
void SetReturnValue(T value)
{
void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr);
}
private:
void* CreateBridge();
void Write_ModifyReturnAddress(sp::MacroAssembler& masm);
void Write_CallHandler(sp::MacroAssembler& masm, HookType_t type);
void Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type);
void Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type);
void* CreatePostCallback();
ReturnAction_t __cdecl HookHandler(HookType_t type);
void* __cdecl GetReturnAddress(void* pESP);
void __cdecl SetReturnAddress(void* pRetAddr, void* pESP);
public:
HookTypeMap m_hookHandler;
// Address of the original function
void* m_pFunc;
ICallingConvention* m_pCallingConvention;
// Address of the bridge
void* m_pBridge;
// Address of the trampoline
void* m_pTrampoline;
// Register storage
CRegisters* m_pRegisters;
// New return address
void* m_pNewRetAddr;
ReturnAddressMap m_RetAddr;
// Save the last return action of the pre HookHandler for use in the post handler.
std::vector<ReturnAction_t> m_LastPreReturnAction;
};
#endif // _HOOK_H
+107
View File
@@ -0,0 +1,107 @@
/**
* =============================================================================
* 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 "manager.h"
// ============================================================================
// >> CHookManager
// ============================================================================
CHook* CHookManager::HookFunction(void* pFunc, ICallingConvention* pConvention)
{
if (!pFunc)
return NULL;
CHook* pHook = FindHook(pFunc);
if (pHook)
{
delete pConvention;
return pHook;
}
pHook = new CHook(pFunc, pConvention);
m_Hooks.push_back(pHook);
return pHook;
}
void CHookManager::UnhookFunction(void* pFunc)
{
if (!pFunc)
return;
for (size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
{
m_Hooks.erase(m_Hooks.begin() + i);
delete pHook;
return;
}
}
}
CHook* CHookManager::FindHook(void* pFunc)
{
if (!pFunc)
return NULL;
for(size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
return pHook;
}
return NULL;
}
void CHookManager::UnhookAllFunctions()
{
for(size_t i = 0; i < m_Hooks.size(); i++)
delete m_Hooks[i];
m_Hooks.clear();
}
// ============================================================================
// >> GetHookManager
// ============================================================================
CHookManager* GetHookManager()
{
static CHookManager* s_pManager = new CHookManager;
return s_pManager;
}
+86
View File
@@ -0,0 +1,86 @@
/**
* =============================================================================
* 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 _MANAGER_H
#define _MANAGER_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "hook.h"
#include "convention.h"
#include <vector>
// ============================================================================
// >> CHookManager
// ============================================================================
class CHookManager
{
public:
/*
Hooks the given function and returns a new CHook instance. If the
function was already hooked, the existing CHook instance will be
returned.
*/
CHook* HookFunction(void* pFunc, ICallingConvention* pConvention);
/*
Removes all callbacks and restores the original function.
*/
void UnhookFunction(void* pFunc);
/*
Returns either NULL or the found CHook instance.
*/
CHook* FindHook(void* pFunc);
/*
Removes all callbacks and restores all functions.
*/
void UnhookAllFunctions();
public:
std::vector<CHook *> m_Hooks;
};
// ============================================================================
// >> GetHookManager
// ============================================================================
/*
Returns a pointer to a static CHookManager object.
*/
CHookManager* GetHookManager();
#endif // _MANAGER_H
@@ -0,0 +1,509 @@
/**
* =============================================================================
* 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.
*/
#include "registers.h"
CRegisters::CRegisters(std::vector<Register_t> registers)
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
m_al = CreateRegister(registers, AL, 1);
m_cl = CreateRegister(registers, CL, 1);
m_dl = CreateRegister(registers, DL, 1);
m_bl = CreateRegister(registers, BL, 1);
// 64-bit mode only
/*
m_spl = CreateRegister(registers, SPL, 1);
m_bpl = CreateRegister(registers, BPL, 1);
m_sil = CreateRegister(registers, SIL, 1);
m_dil = CreateRegister(registers, DIL, 1);
m_r8b = CreateRegister(registers, R8B, 1);
m_r9b = CreateRegister(registers, R9B, 1);
m_r10b = CreateRegister(registers, R10B, 1);
m_r11b = CreateRegister(registers, R11B, 1);
m_r12b = CreateRegister(registers, R12B, 1);
m_r13b = CreateRegister(registers, R13B, 1);
m_r14b = CreateRegister(registers, R14B, 1);
m_r15b = CreateRegister(registers, R15B, 1);
*/
m_ah = CreateRegister(registers, AH, 1);
m_ch = CreateRegister(registers, CH, 1);
m_dh = CreateRegister(registers, DH, 1);
m_bh = CreateRegister(registers, BH, 1);
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
m_ax = CreateRegister(registers, AX, 2);
m_cx = CreateRegister(registers, CX, 2);
m_dx = CreateRegister(registers, DX, 2);
m_bx = CreateRegister(registers, BX, 2);
m_sp = CreateRegister(registers, SP, 2);
m_bp = CreateRegister(registers, BP, 2);
m_si = CreateRegister(registers, SI, 2);
m_di = CreateRegister(registers, DI, 2);
// 64-bit mode only
/*
m_r8w = CreateRegister(registers, R8W, 2);
m_r9w = CreateRegister(registers, R9W, 2);
m_r10w = CreateRegister(registers, R10W, 2);
m_r11w = CreateRegister(registers, R11W, 2);
m_r12w = CreateRegister(registers, R12W, 2);
m_r13w = CreateRegister(registers, R13W, 2);
m_r14w = CreateRegister(registers, R14W, 2);
m_r15w = CreateRegister(registers, R14W, 2);
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
m_eax = CreateRegister(registers, EAX, 4);
m_ecx = CreateRegister(registers, ECX, 4);
m_edx = CreateRegister(registers, EDX, 4);
m_ebx = CreateRegister(registers, EBX, 4);
m_esp = CreateRegister(registers, ESP, 4);
m_ebp = CreateRegister(registers, EBP, 4);
m_esi = CreateRegister(registers, ESI, 4);
m_edi = CreateRegister(registers, EDI, 4);
// 64-bit mode only
/*
m_r8d = CreateRegister(registers, R8D, 4);
m_r9d = CreateRegister(registers, R9D, 4);
m_r10d = CreateRegister(registers, R10D, 4);
m_r11d = CreateRegister(registers, R11D, 4);
m_r12d = CreateRegister(registers, R12D, 4);
m_r13d = CreateRegister(registers, R13D, 4);
m_r14d = CreateRegister(registers, R14D, 4);
m_r15d = CreateRegister(registers, R15D, 4);
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
m_rax = CreateRegister(registers, RAX, 8);
m_rcx = CreateRegister(registers, RCX, 8);
m_rdx = CreateRegister(registers, RDX, 8);
m_rbx = CreateRegister(registers, RBX, 8);
m_rsp = CreateRegister(registers, RSP, 8);
m_rbp = CreateRegister(registers, RBP, 8);
m_rsi = CreateRegister(registers, RSI, 8);
m_rdi = CreateRegister(registers, RDI, 8);
*/
// 64-bit mode only
/*
m_r8 = CreateRegister(registers, R8, 8);
m_r9 = CreateRegister(registers, R9, 8);
m_r10 = CreateRegister(registers, R10, 8);
m_r11 = CreateRegister(registers, R11, 8);
m_r12 = CreateRegister(registers, R12, 8);
m_r13 = CreateRegister(registers, R13, 8);
m_r14 = CreateRegister(registers, R14, 8);
m_r15 = CreateRegister(registers, R15, 8);
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
m_mm0 = CreateRegister(registers, MM0, 8);
m_mm1 = CreateRegister(registers, MM1, 8);
m_mm2 = CreateRegister(registers, MM2, 8);
m_mm3 = CreateRegister(registers, MM3, 8);
m_mm4 = CreateRegister(registers, MM4, 8);
m_mm5 = CreateRegister(registers, MM5, 8);
m_mm6 = CreateRegister(registers, MM6, 8);
m_mm7 = CreateRegister(registers, MM7, 8);
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// Copying data from xmm0-7 requires the memory address to be 16-byte aligned.
m_xmm0 = CreateRegister(registers, XMM0, 16, 16);
m_xmm1 = CreateRegister(registers, XMM1, 16, 16);
m_xmm2 = CreateRegister(registers, XMM2, 16, 16);
m_xmm3 = CreateRegister(registers, XMM3, 16, 16);
m_xmm4 = CreateRegister(registers, XMM4, 16, 16);
m_xmm5 = CreateRegister(registers, XMM5, 16, 16);
m_xmm6 = CreateRegister(registers, XMM6, 16, 16);
m_xmm7 = CreateRegister(registers, XMM7, 16, 16);
// 64-bit mode only
/*
m_xmm8 = CreateRegister(registers, XMM8, 16);
m_xmm9 = CreateRegister(registers, XMM9, 16);
m_xmm10 = CreateRegister(registers, XMM10, 16);
m_xmm11 = CreateRegister(registers, XMM11, 16);
m_xmm12 = CreateRegister(registers, XMM12, 16);
m_xmm13 = CreateRegister(registers, XMM13, 16);
m_xmm14 = CreateRegister(registers, XMM14, 16);
m_xmm15 = CreateRegister(registers, XMM15, 16);
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
m_cs = CreateRegister(registers, CS, 2);
m_ss = CreateRegister(registers, SS, 2);
m_ds = CreateRegister(registers, DS, 2);
m_es = CreateRegister(registers, ES, 2);
m_fs = CreateRegister(registers, FS, 2);
m_gs = CreateRegister(registers, GS, 2);
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
m_st0 = CreateRegister(registers, ST0, 10);
m_st1 = CreateRegister(registers, ST1, 10);
m_st2 = CreateRegister(registers, ST2, 10);
m_st3 = CreateRegister(registers, ST3, 10);
m_st4 = CreateRegister(registers, ST4, 10);
m_st5 = CreateRegister(registers, ST5, 10);
m_st6 = CreateRegister(registers, ST6, 10);
m_st7 = CreateRegister(registers, ST7, 10);
}
CRegisters::~CRegisters()
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
DeleteRegister(m_al);
DeleteRegister(m_cl);
DeleteRegister(m_dl);
DeleteRegister(m_bl);
// 64-bit mode only
/*
DeleteRegister(m_spl);
DeleteRegister(m_bpl);
DeleteRegister(m_sil);
DeleteRegister(m_dil);
DeleteRegister(m_r8b);
DeleteRegister(m_r9b);
DeleteRegister(m_r10b);
DeleteRegister(m_r11b);
DeleteRegister(m_r12b);
DeleteRegister(m_r13b);
DeleteRegister(m_r14b);
DeleteRegister(m_r15b);
*/
DeleteRegister(m_ah);
DeleteRegister(m_ch);
DeleteRegister(m_dh);
DeleteRegister(m_bh);
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
DeleteRegister(m_ax);
DeleteRegister(m_cx);
DeleteRegister(m_dx);
DeleteRegister(m_bx);
DeleteRegister(m_sp);
DeleteRegister(m_bp);
DeleteRegister(m_si);
DeleteRegister(m_di);
// 64-bit mode only
/*
DeleteRegister(m_r8w);
DeleteRegister(m_r9w);
DeleteRegister(m_r10w);
DeleteRegister(m_r11w);
DeleteRegister(m_r12w);
DeleteRegister(m_r13w);
DeleteRegister(m_r14w);
DeleteRegister(m_r15w);
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
DeleteRegister(m_eax);
DeleteRegister(m_ecx);
DeleteRegister(m_edx);
DeleteRegister(m_ebx);
DeleteRegister(m_esp);
DeleteRegister(m_ebp);
DeleteRegister(m_esi);
DeleteRegister(m_edi);
// 64-bit mode only
/*
DeleteRegister(m_r8d);
DeleteRegister(m_r9d);
DeleteRegister(m_r10d);
DeleteRegister(m_r11d);
DeleteRegister(m_r12d);
DeleteRegister(m_r13d);
DeleteRegister(m_r14d);
DeleteRegister(m_r15d);
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
DeleteRegister(m_rax);
DeleteRegister(m_rcx);
DeleteRegister(m_rdx);
DeleteRegister(m_rbx);
DeleteRegister(m_rsp);
DeleteRegister(m_rbp);
DeleteRegister(m_rsi);
DeleteRegister(m_rdi);
*/
// 64-bit mode only
/*
DeleteRegister(m_r8);
DeleteRegister(m_r9);
DeleteRegister(m_r10);
DeleteRegister(m_r11);
DeleteRegister(m_r12);
DeleteRegister(m_r13);
DeleteRegister(m_r14);
DeleteRegister(m_r15);
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
DeleteRegister(m_mm0);
DeleteRegister(m_mm1);
DeleteRegister(m_mm2);
DeleteRegister(m_mm3);
DeleteRegister(m_mm4);
DeleteRegister(m_mm5);
DeleteRegister(m_mm6);
DeleteRegister(m_mm7);
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
DeleteRegister(m_xmm0);
DeleteRegister(m_xmm1);
DeleteRegister(m_xmm2);
DeleteRegister(m_xmm3);
DeleteRegister(m_xmm4);
DeleteRegister(m_xmm5);
DeleteRegister(m_xmm6);
DeleteRegister(m_xmm7);
// 64-bit mode only
/*
DeleteRegister(m_xmm8);
DeleteRegister(m_xmm9);
DeleteRegister(m_xmm10);
DeleteRegister(m_xmm11);
DeleteRegister(m_xmm12);
DeleteRegister(m_xmm13);
DeleteRegister(m_xmm14);
DeleteRegister(m_xmm15);
*/
// ========================================================================
// >> 2-bit Segment registers
// ========================================================================
DeleteRegister(m_cs);
DeleteRegister(m_ss);
DeleteRegister(m_ds);
DeleteRegister(m_es);
DeleteRegister(m_fs);
DeleteRegister(m_gs);
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
DeleteRegister(m_st0);
DeleteRegister(m_st1);
DeleteRegister(m_st2);
DeleteRegister(m_st3);
DeleteRegister(m_st4);
DeleteRegister(m_st5);
DeleteRegister(m_st6);
DeleteRegister(m_st7);
}
CRegister* CRegisters::CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment)
{
for(size_t i = 0; i < registers.size(); i++)
{
if (registers[i] == reg)
{
return new CRegister(iSize, iAlignment);
}
}
return NULL;
}
void CRegisters::DeleteRegister(CRegister* pRegister)
{
if (pRegister)
{
delete pRegister;
}
}
CRegister* CRegisters::GetRegister(Register_t reg)
{
switch (reg)
{
case AL:
return m_al;
case CL:
return m_cl;
case DL:
return m_dl;
case BL:
return m_bl;
case AH:
return m_ah;
case CH:
return m_ch;
case DH:
return m_dh;
case BH:
return m_bh;
case AX:
return m_ax;
case CX:
return m_cx;
case DX:
return m_dx;
case BX:
return m_bx;
case SP:
return m_sp;
case BP:
return m_bp;
case SI:
return m_si;
case DI:
return m_di;
case EAX:
return m_eax;
case ECX:
return m_ecx;
case EDX:
return m_edx;
case EBX:
return m_ebx;
case ESP:
return m_esp;
case EBP:
return m_ebp;
case ESI:
return m_esi;
case EDI:
return m_edi;
case MM0:
return m_mm0;
case MM1:
return m_mm1;
case MM2:
return m_mm2;
case MM3:
return m_mm3;
case MM4:
return m_mm4;
case MM5:
return m_mm5;
case MM6:
return m_mm6;
case MM7:
return m_mm7;
case XMM0:
return m_xmm0;
case XMM1:
return m_xmm1;
case XMM2:
return m_xmm2;
case XMM3:
return m_xmm3;
case XMM4:
return m_xmm4;
case XMM5:
return m_xmm5;
case XMM6:
return m_xmm6;
case XMM7:
return m_xmm7;
case CS:
return m_cs;
case SS:
return m_ss;
case DS:
return m_ds;
case ES:
return m_es;
case FS:
return m_fs;
case GS:
return m_gs;
case ST0:
return m_st0;
case ST1:
return m_st1;
case ST2:
return m_st2;
case ST3:
return m_st3;
case ST4:
return m_st4;
case ST5:
return m_st5;
case ST6:
return m_st6;
case ST7:
return m_st7;
default:
return NULL;
}
}
+462
View File
@@ -0,0 +1,462 @@
/**
* =============================================================================
* 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 _REGISTERS_H
#define _REGISTERS_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include <vector>
#include <am-platform.h>
#include <cinttypes>
#include <cstdlib>
// ============================================================================
// >> Register_t
// ============================================================================
enum Register_t
{
// No register at all.
None,
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
AL,
CL,
DL,
BL,
// 64-bit mode only
/*
SPL,
BPL,
SIL,
DIL,
R8B,
R9B,
R10B,
R11B,
R12B,
R13B,
R14B,
R15B,
*/
AH,
CH,
DH,
BH,
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
AX,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
// 64-bit mode only
/*
R8W,
R9W,
R10W,
R11W,
R12W,
R13W,
R14W,
R15W,
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
EAX,
ECX,
EDX,
EBX,
ESP,
EBP,
ESI,
EDI,
// 64-bit mode only
/*
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
*/
// 64-bit mode only
/*
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
MM0,
MM1,
MM2,
MM3,
MM4,
MM5,
MM6,
MM7,
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
// 64-bit mode only
/*
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CS,
SS,
DS,
ES,
FS,
GS,
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
ST0,
ST1,
ST2,
ST3,
ST4,
ST5,
ST6,
ST7,
};
// ============================================================================
// >> CRegister
// ============================================================================
class CRegister
{
public:
CRegister(uint16_t iSize, uint16_t iAlignment = 0)
{
m_iSize = iSize;
m_iAlignment = iAlignment;
if (iAlignment > 0)
#ifdef KE_WINDOWS
m_pAddress = _aligned_malloc(iSize, iAlignment);
#else
m_pAddress = aligned_alloc(iAlignment, iSize);
#endif
else
m_pAddress = malloc(iSize);
}
~CRegister()
{
#ifdef KE_WINDOWS
if (m_iAlignment > 0)
_aligned_free(m_pAddress);
else
free(m_pAddress);
#else
free(m_pAddress);
#endif
}
template<class T>
T GetValue()
{
return *(T *) m_pAddress;
}
template<class T>
T GetPointerValue(int iOffset=0)
{
return *(T *) (GetValue<unsigned long>() + iOffset);
}
template<class T>
void SetValue(T value)
{
*(T *) m_pAddress = value;
}
template<class T>
void SetPointerValue(T value, int iOffset=0)
{
*(T *) (GetValue<unsigned long>() + iOffset) = value;
}
public:
uint16_t m_iSize;
uint16_t m_iAlignment;
void* m_pAddress;
};
// ============================================================================
// >> CRegisters
// ============================================================================
class CRegisters
{
public:
CRegisters(std::vector<Register_t> registers);
~CRegisters();
CRegister* GetRegister(Register_t reg);
private:
CRegister* CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment = 0);
void DeleteRegister(CRegister* pRegister);
public:
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
CRegister* m_al;
CRegister* m_cl;
CRegister* m_dl;
CRegister* m_bl;
// 64-bit mode only
/*
CRegister* m_spl;
CRegister* m_bpl;
CRegister* m_sil;
CRegister* m_dil;
CRegister* m_r8b;
CRegister* m_r9b;
CRegister* m_r10b;
CRegister* m_r11b;
CRegister* m_r12b;
CRegister* m_r13b;
CRegister* m_r14b;
CRegister* m_r15b;
*/
CRegister* m_ah;
CRegister* m_ch;
CRegister* m_dh;
CRegister* m_bh;
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
CRegister* m_ax;
CRegister* m_cx;
CRegister* m_dx;
CRegister* m_bx;
CRegister* m_sp;
CRegister* m_bp;
CRegister* m_si;
CRegister* m_di;
// 64-bit mode only
/*
CRegister* m_r8w;
CRegister* m_r9w;
CRegister* m_r10w;
CRegister* m_r11w;
CRegister* m_r12w;
CRegister* m_r13w;
CRegister* m_r14w;
CRegister* m_r15w;
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
CRegister* m_eax;
CRegister* m_ecx;
CRegister* m_edx;
CRegister* m_ebx;
CRegister* m_esp;
CRegister* m_ebp;
CRegister* m_esi;
CRegister* m_edi;
// 64-bit mode only
/*
CRegister* m_r8d;
CRegister* m_r9d;
CRegister* m_r10d;
CRegister* m_r11d;
CRegister* m_r12d;
CRegister* m_r13d;
CRegister* m_r14d;
CRegister* m_r15d;
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
CRegister* m_rax;
CRegister* m_rcx;
CRegister* m_rdx;
CRegister* m_rbx;
CRegister* m_rsp;
CRegister* m_rbp;
CRegister* m_rsi;
CRegister* m_rdi;
*/
// 64-bit mode only
/*
CRegister* m_r8;
CRegister* m_r9;
CRegister* m_r10;
CRegister* m_r11;
CRegister* m_r12;
CRegister* m_r13;
CRegister* m_r14;
CRegister* m_r15;
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
CRegister* m_mm0;
CRegister* m_mm1;
CRegister* m_mm2;
CRegister* m_mm3;
CRegister* m_mm4;
CRegister* m_mm5;
CRegister* m_mm6;
CRegister* m_mm7;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
CRegister* m_xmm0;
CRegister* m_xmm1;
CRegister* m_xmm2;
CRegister* m_xmm3;
CRegister* m_xmm4;
CRegister* m_xmm5;
CRegister* m_xmm6;
CRegister* m_xmm7;
// 64-bit mode only
/*
CRegister* m_xmm8;
CRegister* m_xmm9;
CRegister* m_xmm10;
CRegister* m_xmm11;
CRegister* m_xmm12;
CRegister* m_xmm13;
CRegister* m_xmm14;
CRegister* m_xmm15;
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CRegister* m_cs;
CRegister* m_ss;
CRegister* m_ds;
CRegister* m_es;
CRegister* m_fs;
CRegister* m_gs;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
CRegister* m_st0;
CRegister* m_st1;
CRegister* m_st2;
CRegister* m_st3;
CRegister* m_st4;
CRegister* m_st5;
CRegister* m_st6;
CRegister* m_st7;
};
#endif // _REGISTERS_H
@@ -0,0 +1,73 @@
/**
* =============================================================================
* 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).
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __linux__
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#define PAGE_SIZE 4096
#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
#endif
#include <asm/asm.h>
// ============================================================================
// >> ParseParams
// ============================================================================
void SetMemPatchable(void* pAddr, size_t size)
{
#if defined __linux__
if (mprotect((void *) ALIGN(pAddr), sysconf(_SC_PAGESIZE), PAGE_EXECUTE_READWRITE) == -1)
perror("mprotect");
#elif defined _WIN32
DWORD old_prot;
VirtualProtect(pAddr, size, PAGE_EXECUTE_READWRITE, &old_prot);
#endif
}
// ============================================================================
// >> WriteJMP
// ============================================================================
void WriteJMP(unsigned char* src, void* dest)
{
SetMemPatchable(src, 20);
inject_jmp((void *)src, dest);
}
@@ -0,0 +1,41 @@
/**
* =============================================================================
* 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).
*/
#ifndef _UTILITIES_H
#define _UTILITIES_H
// ============================================================================
// >> FUNCTIONS
// ============================================================================
void SetMemPatchable(void* pAddr, size_t size);
void WriteJMP(unsigned char* src, void* dest);
#endif // _UTILITIES_H