Import DHooks + Dynamic Detouring
This is the latest DHooks version from https://github.com/peace-maker/DHooks2/tree/1314f2d1b4d870677a1b9f628c770f2799878f9b
This commit is contained in:
@@ -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
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user