Import DHooks + Dynamic Detouring

This is the latest DHooks version from 1314f2d1b4
This commit is contained in:
Peace-Maker 2021-11-02 14:28:24 +01:00 committed by Your Name
parent 45e9da90fc
commit 3c0f700a6b
41 changed files with 9999 additions and 0 deletions

View File

@ -804,6 +804,7 @@ else:
'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/dhooks/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/pgsql/AMBuilder',

View File

@ -0,0 +1,63 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os
for cxx in builder.targets:
binary = SM.ExtLibrary(builder, cxx, 'dhooks.ext')
# Only x86 on Linux and Windows is supported.
if binary.compiler.target.platform == 'mac' or binary.compiler.target.arch != 'x86':
continue
binary.compiler.defines += [
'META_NO_HL2SDK',
'HAVE_STRING_H',
]
if binary.compiler.like('gcc'):
binary.compiler.cflags += ['-Wno-invalid-offsetof']
binary.compiler.cxxincludes += [
os.path.join(SM.mms_root, 'core'),
os.path.join(SM.mms_root, 'core', 'sourcehook'),
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
os.path.join(builder.sourcePath, 'sourcepawn', 'vm'),
os.path.join(builder.sourcePath, 'sourcepawn', 'vm', 'x86'),
os.path.join(builder.sourcePath, 'extensions', 'dhooks', 'DynamicHooks'),
]
binary.sources += [
'extension.cpp',
'listeners.cpp',
'natives.cpp',
'signatures.cpp',
'vhook.cpp',
'util.cpp',
'dynhooks_sourcepawn.cpp',
'../../public/smsdk_ext.cpp',
'../../public/asm/asm.c',
'../../public/libudis86/decode.c',
'../../public/libudis86/itab.c',
'../../public/libudis86/syn-att.c',
'../../public/libudis86/syn-intel.c',
'../../public/libudis86/syn.c',
'../../public/libudis86/udis86.c',
'../../sourcepawn/vm/x86/assembler-x86.cpp',
]
# DynamicHooks
binary.sources += [
os.path.join('DynamicHooks', 'hook.cpp'),
os.path.join('DynamicHooks', 'manager.cpp'),
os.path.join('DynamicHooks', 'registers.cpp'),
os.path.join('DynamicHooks', 'utilities.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsCdecl.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsStdcall.cpp'),
os.path.join('DynamicHooks', 'conventions', 'x86MsFastcall.cpp'),
]
if binary.compiler.target.platform == 'windows':
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86MsThiscall.cpp')]
else:
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86GccThiscall.cpp')]
SM.extensions += [builder.Add(binary)]

View File

@ -0,0 +1,290 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _CONVENTION_H
#define _CONVENTION_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>
#include <utility>
// ============================================================================
// >> DataType_t
// ============================================================================
enum DataType_t
{
DATA_TYPE_VOID,
DATA_TYPE_BOOL,
DATA_TYPE_CHAR,
DATA_TYPE_UCHAR,
DATA_TYPE_SHORT,
DATA_TYPE_USHORT,
DATA_TYPE_INT,
DATA_TYPE_UINT,
DATA_TYPE_LONG,
DATA_TYPE_ULONG,
DATA_TYPE_LONG_LONG,
DATA_TYPE_ULONG_LONG,
DATA_TYPE_FLOAT,
DATA_TYPE_DOUBLE,
DATA_TYPE_POINTER,
DATA_TYPE_STRING,
DATA_TYPE_OBJECT
};
typedef struct DataTypeSized_s {
DataTypeSized_s()
{
type = DATA_TYPE_POINTER;
size = 0;
custom_register = None;
}
DataType_t type;
size_t size;
Register_t custom_register;
} DataTypeSized_t;
// ============================================================================
// >> FUNCTIONS
// ============================================================================
/*
Returns the size after applying alignment.
@param <size>:
The size that should be aligned.
@param <alignment>:
The alignment that should be used.
*/
inline int Align(int size, int alignment)
{
int unaligned = size % alignment;
if (unaligned == 0)
return size;
return size + (alignment - unaligned);
}
/*
Returns the size of a data type after applying alignment.
@param <type>:
The data type you would like to get the size of.
@param <alignment>:
The alignment that should be used.
*/
inline int GetDataTypeSize(DataTypeSized_t type, int iAlignment=4)
{
switch(type.type)
{
case DATA_TYPE_VOID: return 0;
case DATA_TYPE_BOOL: return Align(sizeof(bool), iAlignment);
case DATA_TYPE_CHAR: return Align(sizeof(char), iAlignment);
case DATA_TYPE_UCHAR: return Align(sizeof(unsigned char), iAlignment);
case DATA_TYPE_SHORT: return Align(sizeof(short), iAlignment);
case DATA_TYPE_USHORT: return Align(sizeof(unsigned short), iAlignment);
case DATA_TYPE_INT: return Align(sizeof(int), iAlignment);
case DATA_TYPE_UINT: return Align(sizeof(unsigned int), iAlignment);
case DATA_TYPE_LONG: return Align(sizeof(long), iAlignment);
case DATA_TYPE_ULONG: return Align(sizeof(unsigned long), iAlignment);
case DATA_TYPE_LONG_LONG: return Align(sizeof(long long), iAlignment);
case DATA_TYPE_ULONG_LONG: return Align(sizeof(unsigned long long), iAlignment);
case DATA_TYPE_FLOAT: return Align(sizeof(float), iAlignment);
case DATA_TYPE_DOUBLE: return Align(sizeof(double), iAlignment);
case DATA_TYPE_POINTER: return Align(sizeof(void *), iAlignment);
case DATA_TYPE_STRING: return Align(sizeof(char *), iAlignment);
case DATA_TYPE_OBJECT: return type.size;
default: puts("Unknown data type.");
}
return 0;
}
// ============================================================================
// >> CLASSES
// ============================================================================
/*
This is the base class for every calling convention. Inherit from this class
to create your own calling convention.
*/
class ICallingConvention
{
public:
/*
Initializes the calling convention.
@param <vecArgTypes>:
A list of DataType_t objects, which define the arguments of the function.
@param <returnType>:
The return type of the function.
*/
ICallingConvention(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4)
{
m_vecArgTypes = std::move(vecArgTypes);
for (size_t i=0; i < m_vecArgTypes.size(); i++)
{
DataTypeSized_t &type = m_vecArgTypes[i];
if (!type.size)
type.size = GetDataTypeSize(type, iAlignment);
}
m_returnType = returnType;
if (!m_returnType.size)
m_returnType.size = GetDataTypeSize(m_returnType, iAlignment);
m_iAlignment = iAlignment;
}
virtual ~ICallingConvention()
{
}
/*
This should return a list of Register_t values. These registers will be
saved for later access.
*/
virtual std::vector<Register_t> GetRegisters() = 0;
/*
Returns the number of bytes that should be added to the stack to clean up.
*/
virtual int GetPopSize() = 0;
virtual int GetArgStackSize() = 0;
virtual void** GetStackArgumentPtr(CRegisters* pRegisters) = 0;
/*
Returns the number of bytes for the buffer to store all the arguments that are passed in a register in.
*/
virtual int GetArgRegisterSize() = 0;
/*
Returns a pointer to the argument at the given index.
@param <iIndex>:
The index of the argument.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters) = 0;
/*
*/
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr) = 0;
/*
Returns a pointer to the return value.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void* GetReturnPtr(CRegisters* pRegisters) = 0;
/*
*/
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) = 0;
/*
Save the return value in a seperate buffer, so we can restore it after calling the original function.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveReturnValue(CRegisters* pRegisters)
{
std::unique_ptr<uint8_t[]> pSavedReturnValue = std::make_unique<uint8_t[]>(m_returnType.size);
memcpy(pSavedReturnValue.get(), GetReturnPtr(pRegisters), m_returnType.size);
m_pSavedReturnBuffers.push_back(std::move(pSavedReturnValue));
}
virtual void RestoreReturnValue(CRegisters* pRegisters)
{
uint8_t* pSavedReturnValue = m_pSavedReturnBuffers.back().get();
memcpy(GetReturnPtr(pRegisters), pSavedReturnValue, m_returnType.size);
ReturnPtrChanged(pRegisters, pSavedReturnValue);
m_pSavedReturnBuffers.pop_back();
}
/*
Save the value of arguments in a seperate buffer for the post callback.
Compiler optimizations might cause the registers or stack space to be reused
and overwritten during function execution if the value isn't needed anymore
at some point. This leads to different values in the post hook.
@param <pRegisters>:
A snapshot of all saved registers.
*/
virtual void SaveCallArguments(CRegisters* pRegisters)
{
int size = GetArgStackSize() + GetArgRegisterSize();
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
offset += type.size;
}
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
}
virtual void RestoreCallArguments(CRegisters* pRegisters)
{
uint8_t *pSavedCallArguments = m_pSavedCallArguments.back().get();
size_t offset = 0;
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
DataTypeSized_t &type = m_vecArgTypes[i];
memcpy(GetArgumentPtr(i, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
offset += type.size;
}
m_pSavedCallArguments.pop_back();
}
public:
std::vector<DataTypeSized_t> m_vecArgTypes;
DataTypeSized_t m_returnType;
int m_iAlignment;
// Save the return in case we call the original function and want to override the return again.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedReturnBuffers;
// Save call arguments in case the function reuses the space and overwrites the values for the post hook.
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedCallArguments;
};
#endif // _CONVENTION_H

View File

@ -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

View File

@ -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));
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -0,0 +1,503 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "hook.h"
#include "utilities.h"
#include <asm/asm.h>
#include <macro-assembler-x86.h>
#include "extension.h"
using namespace sp;
// ============================================================================
// >> DEFINITIONS
// ============================================================================
#define JMP_SIZE 6
// ============================================================================
// >> CHook
// ============================================================================
CHook::CHook(void* pFunc, ICallingConvention* pConvention)
{
m_pFunc = pFunc;
m_pRegisters = new CRegisters(pConvention->GetRegisters());
m_pCallingConvention = pConvention;
if (!m_hookHandler.init())
return;
if (!m_RetAddr.init())
return;
unsigned char* pTarget = (unsigned char *) pFunc;
// Determine the number of bytes we need to copy
int iBytesToCopy = copy_bytes(pTarget, NULL, JMP_SIZE);
// Create a buffer for the bytes to copy + a jump to the rest of the
// function.
unsigned char* pCopiedBytes = (unsigned char *) smutils->GetScriptingEngine()->AllocatePageMemory(iBytesToCopy + JMP_SIZE);
// Fill the array with NOP instructions
memset(pCopiedBytes, 0x90, iBytesToCopy + JMP_SIZE);
// Copy the required bytes to our array
copy_bytes(pTarget, pCopiedBytes, JMP_SIZE);
// Write a jump after the copied bytes to the function/bridge + number of bytes to copy
WriteJMP(pCopiedBytes + iBytesToCopy, pTarget + iBytesToCopy);
// Save the trampoline
m_pTrampoline = (void *) pCopiedBytes;
// Create the bridge function
m_pBridge = CreateBridge();
// Write a jump to the bridge
WriteJMP((unsigned char *) pFunc, m_pBridge);
}
CHook::~CHook()
{
// Copy back the previously copied bytes
copy_bytes((unsigned char *) m_pTrampoline, (unsigned char *) m_pFunc, JMP_SIZE);
// Free the trampoline buffer
smutils->GetScriptingEngine()->FreePageMemory(m_pTrampoline);
// Free the asm bridge and new return address
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge);
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr);
delete m_pRegisters;
delete m_pCallingConvention;
}
void CHook::AddCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
if (!pCallback)
return;
HookTypeMap::Insert i = m_hookHandler.findForAdd(eHookType);
if (!i.found()) {
HookHandlerSet set;
set.init();
m_hookHandler.add(i, eHookType, std::move(set));
}
i->value.add(pCallback);
}
void CHook::RemoveCallback(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return;
r->value.removeIfExists(pCallback);
}
bool CHook::IsCallbackRegistered(HookType_t eHookType, HookHandlerFn* pCallback)
{
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
return false;
return r->value.has(pCallback);
}
bool CHook::AreCallbacksRegistered()
{
HookTypeMap::Result r = m_hookHandler.find(HOOKTYPE_PRE);
if (r.found() && r->value.elements() > 0)
return true;
r = m_hookHandler.find(HOOKTYPE_POST);
if (r.found() && r->value.elements() > 0)
return true;
return false;
}
ReturnAction_t CHook::HookHandler(HookType_t eHookType)
{
if (eHookType == HOOKTYPE_POST)
{
ReturnAction_t lastPreReturnAction = m_LastPreReturnAction.back();
m_LastPreReturnAction.pop_back();
if (lastPreReturnAction == ReturnAction_Override)
m_pCallingConvention->RestoreReturnValue(m_pRegisters);
if (lastPreReturnAction < ReturnAction_Supercede)
m_pCallingConvention->RestoreCallArguments(m_pRegisters);
}
ReturnAction_t returnAction = ReturnAction_Ignored;
HookTypeMap::Result r = m_hookHandler.find(eHookType);
if (!r.found())
{
// Still save the arguments for the post hook even if there
// is no pre-handler registered.
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
HookHandlerSet &callbacks = r->value;
for(HookHandlerSet::iterator it=callbacks.iter(); !it.empty(); it.next())
{
ReturnAction_t result = ((HookHandlerFn) *it)(eHookType, this);
if (result > returnAction)
returnAction = result;
}
if (eHookType == HOOKTYPE_PRE)
{
m_LastPreReturnAction.push_back(returnAction);
if (returnAction == ReturnAction_Override)
m_pCallingConvention->SaveReturnValue(m_pRegisters);
if (returnAction < ReturnAction_Supercede)
m_pCallingConvention->SaveCallArguments(m_pRegisters);
}
return returnAction;
}
void* __cdecl CHook::GetReturnAddress(void* pESP)
{
ReturnAddressMap::Result r = m_RetAddr.find(pESP);
assert(r.found());
if (!r.found())
{
smutils->LogError(myself, "FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.");
return NULL;
}
void *pRetAddr = r->value.back();
r->value.pop_back();
// Clear the stack address from the cache now that we ran the last post hook.
if (r->value.empty())
m_RetAddr.remove(r);
return pRetAddr;
}
void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
{
ReturnAddressMap::Insert i = m_RetAddr.findForAdd(pESP);
if (!i.found())
m_RetAddr.add(i, pESP, std::vector<void *>());
i->value.push_back(pRetAddr);
}
void* CHook::CreateBridge()
{
sp::MacroAssembler masm;
Label label_supercede;
// Write a redirect to the post-hook code
Write_ModifyReturnAddress(masm);
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
Write_CallHandler(masm, HOOKTYPE_PRE);
masm.cmpb(r8_al, ReturnAction_Supercede);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_PRE);
masm.j(equal, &label_supercede);
// Jump to the trampoline
masm.jmp(ExternalAddress(m_pTrampoline));
// This code will be executed if a pre-hook returns ReturnAction_Supercede
masm.bind(&label_supercede);
// Finally, return to the caller
// This will still call post hooks, but will skip the original function.
masm.ret(m_pCallingConvention->GetPopSize());
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
{
// Save scratch registers that are used by SetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Store the return address in eax
masm.movl(eax, Operand(esp, 0));
// Save the original return address by using the current esp as the key.
// This should be unique until we have returned to the original caller.
void (__cdecl CHook::*SetReturnAddress)(void*, void*) = &CHook::SetReturnAddress;
masm.push(esp);
masm.push(eax);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)SetReturnAddress));
masm.addl(esp, 12);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Override the return address. This is a redirect to our post-hook code
m_pNewRetAddr = CreatePostCallback();
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
}
void* CHook::CreatePostCallback()
{
sp::MacroAssembler masm;
int iPopSize = m_pCallingConvention->GetPopSize();
// Subtract the previously added bytes (stack size + return address), so
// that we can access the arguments again
masm.subl(esp, iPopSize+4);
// Call the post-hook handler
Write_CallHandler(masm, HOOKTYPE_POST);
// Restore the previously saved registers, so any changes will be applied
Write_RestoreRegisters(masm, HOOKTYPE_POST);
// Save scratch registers that are used by GetReturnAddress
static void* pEAX = NULL;
static void* pECX = NULL;
static void* pEDX = NULL;
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
// Get the original return address
void* (__cdecl CHook::*GetReturnAddress)(void*) = &CHook::GetReturnAddress;
masm.push(esp);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)GetReturnAddress));
masm.addl(esp, 8);
// Save the original return address
static void* pRetAddr = NULL;
masm.movl(Operand(ExternalAddress(&pRetAddr)), eax);
// Restore scratch registers
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
// Add the bytes again to the stack (stack size + return address), so we
// don't corrupt the stack.
masm.addl(esp, iPopSize+4);
// Jump to the original return address
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
// Generate the code
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
masm.emitToExecutableMemory(base);
return base;
}
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
{
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t) = &CHook::HookHandler;
// Save the registers so that we can access them in our handlers
Write_SaveRegisters(masm, type);
// Align the stack to 16 bytes.
masm.subl(esp, 8);
// Call the global hook handler
masm.push(type);
masm.push(intptr_t(this));
masm.call(ExternalAddress((void *&)HookHandler));
masm.addl(esp, 16);
}
void CHook::Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch(vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress)), r8_al); break;
case CL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress)), r8_cl); break;
case DL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress)), r8_dl); break;
case BL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress)), r8_bl); break;
case AH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress)), r8_ah); break;
case CH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress)), r8_ch); break;
case DH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress)), r8_dh); break;
case BH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress)), r8_bh); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress)), eax); break;
case ECX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress)), ecx); break;
case EDX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress)), edx); break;
case EBX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress)), ebx); break;
case ESP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress)), esp); break;
case EBP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress)), ebp); break;
case ESI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress)), esi); break;
case EDI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress)), edi); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress)), xmm0); break;
case XMM1: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress)), xmm1); break;
case XMM2: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress)), xmm2); break;
case XMM3: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress)), xmm3); break;
case XMM4: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress)), xmm4); break;
case XMM5: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress)), xmm5); break;
case XMM6: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress)), xmm6); break;
case XMM7: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress)), xmm7); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
// Don't mess with the FPU stack in a pre-hook. The float return is returned in st0,
// so only load it in a post hook to avoid writing back NaN.
if (type == HOOKTYPE_POST)
masm.fst32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
break;
//case ST1: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress)), st1); break;
//case ST2: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress)), st2); break;
//case ST3: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress)), st3); break;
//case ST4: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress)), st4); break;
//case ST5: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress)), st5); break;
//case ST6: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress)), st6); break;
//case ST7: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress)), st7); break;
default: puts("Unsupported register.");
}
}
}
void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
{
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
for (size_t i = 0; i < vecRegistersToSave.size(); i++)
{
switch (vecRegistersToSave[i])
{
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
case AL: masm.movl(r8_al, Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress))); break;
case CL: masm.movl(r8_cl, Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress))); break;
case DL: masm.movl(r8_dl, Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress))); break;
case BL: masm.movl(r8_bl, Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress))); break;
case AH: masm.movl(r8_ah, Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress))); break;
case CH: masm.movl(r8_ch, Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress))); break;
case DH: masm.movl(r8_dh, Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress))); break;
case BH: masm.movl(r8_bh, Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress))); break;
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
case EAX: masm.movl(eax, Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress))); break;
case ECX: masm.movl(ecx, Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress))); break;
case EDX: masm.movl(edx, Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress))); break;
case EBX: masm.movl(ebx, Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress))); break;
case ESP: masm.movl(esp, Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress))); break;
case EBP: masm.movl(ebp, Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress))); break;
case ESI: masm.movl(esi, Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress))); break;
case EDI: masm.movl(edi, Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress))); break;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
// TODO: Also provide movups?
case XMM0: masm.movaps(xmm0, Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress))); break;
case XMM1: masm.movaps(xmm1, Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress))); break;
case XMM2: masm.movaps(xmm2, Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress))); break;
case XMM3: masm.movaps(xmm3, Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress))); break;
case XMM4: masm.movaps(xmm4, Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress))); break;
case XMM5: masm.movaps(xmm5, Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress))); break;
case XMM6: masm.movaps(xmm6, Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress))); break;
case XMM7: masm.movaps(xmm7, Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress))); break;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
case ST0:
if (type == HOOKTYPE_POST) {
// Replace the top of the FPU stack.
// Copy st0 to st0 and pop -> just pop the FPU stack.
masm.fstp(st0);
// Push a value to the FPU stack.
// TODO: Only write back when changed? Save full 80bits for that case.
// Avoid truncation of the data if it's unchanged.
masm.fld32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
}
break;
//case ST1: masm.movl(st1, tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress))); break;
//case ST2: masm.movl(st2, tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress))); break;
//case ST3: masm.movl(st3, tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress))); break;
//case ST4: masm.movl(st4, tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress))); break;
//case ST5: masm.movl(st5, tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress))); break;
//case ST6: masm.movl(st6, tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress))); break;
//case ST7: masm.movl(st7, tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress))); break;
default: puts("Unsupported register.");
}
}
}

View File

@ -0,0 +1,217 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _HOOK_H
#define _HOOK_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "registers.h"
#include "convention.h"
#include <am-hashmap.h>
#include <am-hashset.h>
// ============================================================================
// >> HookType_t
// ============================================================================
enum HookType_t
{
// Callback will be executed before the original function.
HOOKTYPE_PRE,
// Callback will be executed after the original function.
HOOKTYPE_POST
};
enum ReturnAction_t
{
ReturnAction_Ignored, // handler didn't take any action
ReturnAction_Handled, // plugin did something, but real function should still be called
ReturnAction_Override, // call real function, but use my return value
ReturnAction_Supercede // skip real function; use my return value
};
// ============================================================================
// >> TYPEDEFS
// ============================================================================
class CHook;
typedef ReturnAction_t (*HookHandlerFn)(HookType_t, CHook*);
#ifdef __linux__
#define __cdecl
#endif
struct IntegerPolicy
{
static inline uint32_t hash(size_t i) {
return ke::HashInteger<sizeof(size_t)>(i);
}
static inline bool matches(size_t i1, size_t i2) {
return i1 == i2;
}
};
typedef ke::HashSet<HookHandlerFn*, ke::PointerPolicy<HookHandlerFn>> HookHandlerSet;
typedef ke::HashMap<HookType_t, HookHandlerSet, IntegerPolicy> HookTypeMap;
typedef ke::HashMap<void*, std::vector<void*>, ke::PointerPolicy<void>> ReturnAddressMap;
namespace sp
{
class MacroAssembler;
}
// ============================================================================
// >> CLASSES
// ============================================================================
class CHook
{
private:
friend class CHookManager;
/*
Creates a new function hook.
@param <pFunc>:
The address of the function to hook
@param <pConvention>:
The calling convention of <pFunc>.
*/
CHook(void* pFunc, ICallingConvention* pConvention);
~CHook();
public:
/*
Adds a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be added.
*/
void AddCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Removes a hook handler to the hook.
@param type The hook type.
@param pFunc The hook handler that should be removed.
*/
void RemoveCallback(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if a hook handler is already added.
@param type The hook type.
@param pFunc The hook handler that should be checked.
*/
bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc);
/*
Checks if there are any hook handlers added to this hook.
*/
bool AreCallbacksRegistered();
template<class T>
T GetArgument(int iIndex)
{
return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
}
template<class T>
void SetArgument(int iIndex, T value)
{
void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr);
}
template<class T>
T GetReturnValue()
{
return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters);
}
template<class T>
void SetReturnValue(T value)
{
void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters);
*(T *) pPtr = value;
m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr);
}
private:
void* CreateBridge();
void Write_ModifyReturnAddress(sp::MacroAssembler& masm);
void Write_CallHandler(sp::MacroAssembler& masm, HookType_t type);
void Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type);
void Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type);
void* CreatePostCallback();
ReturnAction_t __cdecl HookHandler(HookType_t type);
void* __cdecl GetReturnAddress(void* pESP);
void __cdecl SetReturnAddress(void* pRetAddr, void* pESP);
public:
HookTypeMap m_hookHandler;
// Address of the original function
void* m_pFunc;
ICallingConvention* m_pCallingConvention;
// Address of the bridge
void* m_pBridge;
// Address of the trampoline
void* m_pTrampoline;
// Register storage
CRegisters* m_pRegisters;
// New return address
void* m_pNewRetAddr;
ReturnAddressMap m_RetAddr;
// Save the last return action of the pre HookHandler for use in the post handler.
std::vector<ReturnAction_t> m_LastPreReturnAction;
};
#endif // _HOOK_H

View File

@ -0,0 +1,107 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "manager.h"
// ============================================================================
// >> CHookManager
// ============================================================================
CHook* CHookManager::HookFunction(void* pFunc, ICallingConvention* pConvention)
{
if (!pFunc)
return NULL;
CHook* pHook = FindHook(pFunc);
if (pHook)
{
delete pConvention;
return pHook;
}
pHook = new CHook(pFunc, pConvention);
m_Hooks.push_back(pHook);
return pHook;
}
void CHookManager::UnhookFunction(void* pFunc)
{
if (!pFunc)
return;
for (size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
{
m_Hooks.erase(m_Hooks.begin() + i);
delete pHook;
return;
}
}
}
CHook* CHookManager::FindHook(void* pFunc)
{
if (!pFunc)
return NULL;
for(size_t i = 0; i < m_Hooks.size(); i++)
{
CHook* pHook = m_Hooks[i];
if (pHook->m_pFunc == pFunc)
return pHook;
}
return NULL;
}
void CHookManager::UnhookAllFunctions()
{
for(size_t i = 0; i < m_Hooks.size(); i++)
delete m_Hooks[i];
m_Hooks.clear();
}
// ============================================================================
// >> GetHookManager
// ============================================================================
CHookManager* GetHookManager()
{
static CHookManager* s_pManager = new CHookManager;
return s_pManager;
}

View File

@ -0,0 +1,86 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _MANAGER_H
#define _MANAGER_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include "hook.h"
#include "convention.h"
#include <vector>
// ============================================================================
// >> CHookManager
// ============================================================================
class CHookManager
{
public:
/*
Hooks the given function and returns a new CHook instance. If the
function was already hooked, the existing CHook instance will be
returned.
*/
CHook* HookFunction(void* pFunc, ICallingConvention* pConvention);
/*
Removes all callbacks and restores the original function.
*/
void UnhookFunction(void* pFunc);
/*
Returns either NULL or the found CHook instance.
*/
CHook* FindHook(void* pFunc);
/*
Removes all callbacks and restores all functions.
*/
void UnhookAllFunctions();
public:
std::vector<CHook *> m_Hooks;
};
// ============================================================================
// >> GetHookManager
// ============================================================================
/*
Returns a pointer to a static CHookManager object.
*/
CHookManager* GetHookManager();
#endif // _MANAGER_H

View File

@ -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;
}
}

View File

@ -0,0 +1,462 @@
/**
* =============================================================================
* DynamicHooks
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
* -fPIC thunks correctly
*
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
*
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
*/
#ifndef _REGISTERS_H
#define _REGISTERS_H
// ============================================================================
// >> INCLUDES
// ============================================================================
#include <vector>
#include <am-platform.h>
#include <cinttypes>
#include <cstdlib>
// ============================================================================
// >> Register_t
// ============================================================================
enum Register_t
{
// No register at all.
None,
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
AL,
CL,
DL,
BL,
// 64-bit mode only
/*
SPL,
BPL,
SIL,
DIL,
R8B,
R9B,
R10B,
R11B,
R12B,
R13B,
R14B,
R15B,
*/
AH,
CH,
DH,
BH,
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
AX,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
// 64-bit mode only
/*
R8W,
R9W,
R10W,
R11W,
R12W,
R13W,
R14W,
R15W,
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
EAX,
ECX,
EDX,
EBX,
ESP,
EBP,
ESI,
EDI,
// 64-bit mode only
/*
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
*/
// 64-bit mode only
/*
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
MM0,
MM1,
MM2,
MM3,
MM4,
MM5,
MM6,
MM7,
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
// 64-bit mode only
/*
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CS,
SS,
DS,
ES,
FS,
GS,
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
ST0,
ST1,
ST2,
ST3,
ST4,
ST5,
ST6,
ST7,
};
// ============================================================================
// >> CRegister
// ============================================================================
class CRegister
{
public:
CRegister(uint16_t iSize, uint16_t iAlignment = 0)
{
m_iSize = iSize;
m_iAlignment = iAlignment;
if (iAlignment > 0)
#ifdef KE_WINDOWS
m_pAddress = _aligned_malloc(iSize, iAlignment);
#else
m_pAddress = aligned_alloc(iAlignment, iSize);
#endif
else
m_pAddress = malloc(iSize);
}
~CRegister()
{
#ifdef KE_WINDOWS
if (m_iAlignment > 0)
_aligned_free(m_pAddress);
else
free(m_pAddress);
#else
free(m_pAddress);
#endif
}
template<class T>
T GetValue()
{
return *(T *) m_pAddress;
}
template<class T>
T GetPointerValue(int iOffset=0)
{
return *(T *) (GetValue<unsigned long>() + iOffset);
}
template<class T>
void SetValue(T value)
{
*(T *) m_pAddress = value;
}
template<class T>
void SetPointerValue(T value, int iOffset=0)
{
*(T *) (GetValue<unsigned long>() + iOffset) = value;
}
public:
uint16_t m_iSize;
uint16_t m_iAlignment;
void* m_pAddress;
};
// ============================================================================
// >> CRegisters
// ============================================================================
class CRegisters
{
public:
CRegisters(std::vector<Register_t> registers);
~CRegisters();
CRegister* GetRegister(Register_t reg);
private:
CRegister* CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment = 0);
void DeleteRegister(CRegister* pRegister);
public:
// ========================================================================
// >> 8-bit General purpose registers
// ========================================================================
CRegister* m_al;
CRegister* m_cl;
CRegister* m_dl;
CRegister* m_bl;
// 64-bit mode only
/*
CRegister* m_spl;
CRegister* m_bpl;
CRegister* m_sil;
CRegister* m_dil;
CRegister* m_r8b;
CRegister* m_r9b;
CRegister* m_r10b;
CRegister* m_r11b;
CRegister* m_r12b;
CRegister* m_r13b;
CRegister* m_r14b;
CRegister* m_r15b;
*/
CRegister* m_ah;
CRegister* m_ch;
CRegister* m_dh;
CRegister* m_bh;
// ========================================================================
// >> 16-bit General purpose registers
// ========================================================================
CRegister* m_ax;
CRegister* m_cx;
CRegister* m_dx;
CRegister* m_bx;
CRegister* m_sp;
CRegister* m_bp;
CRegister* m_si;
CRegister* m_di;
// 64-bit mode only
/*
CRegister* m_r8w;
CRegister* m_r9w;
CRegister* m_r10w;
CRegister* m_r11w;
CRegister* m_r12w;
CRegister* m_r13w;
CRegister* m_r14w;
CRegister* m_r15w;
*/
// ========================================================================
// >> 32-bit General purpose registers
// ========================================================================
CRegister* m_eax;
CRegister* m_ecx;
CRegister* m_edx;
CRegister* m_ebx;
CRegister* m_esp;
CRegister* m_ebp;
CRegister* m_esi;
CRegister* m_edi;
// 64-bit mode only
/*
CRegister* m_r8d;
CRegister* m_r9d;
CRegister* m_r10d;
CRegister* m_r11d;
CRegister* m_r12d;
CRegister* m_r13d;
CRegister* m_r14d;
CRegister* m_r15d;
*/
// ========================================================================
// >> 64-bit General purpose registers
// ========================================================================
// 64-bit mode only
/*
CRegister* m_rax;
CRegister* m_rcx;
CRegister* m_rdx;
CRegister* m_rbx;
CRegister* m_rsp;
CRegister* m_rbp;
CRegister* m_rsi;
CRegister* m_rdi;
*/
// 64-bit mode only
/*
CRegister* m_r8;
CRegister* m_r9;
CRegister* m_r10;
CRegister* m_r11;
CRegister* m_r12;
CRegister* m_r13;
CRegister* m_r14;
CRegister* m_r15;
*/
// ========================================================================
// >> 64-bit MM (MMX) registers
// ========================================================================
CRegister* m_mm0;
CRegister* m_mm1;
CRegister* m_mm2;
CRegister* m_mm3;
CRegister* m_mm4;
CRegister* m_mm5;
CRegister* m_mm6;
CRegister* m_mm7;
// ========================================================================
// >> 128-bit XMM registers
// ========================================================================
CRegister* m_xmm0;
CRegister* m_xmm1;
CRegister* m_xmm2;
CRegister* m_xmm3;
CRegister* m_xmm4;
CRegister* m_xmm5;
CRegister* m_xmm6;
CRegister* m_xmm7;
// 64-bit mode only
/*
CRegister* m_xmm8;
CRegister* m_xmm9;
CRegister* m_xmm10;
CRegister* m_xmm11;
CRegister* m_xmm12;
CRegister* m_xmm13;
CRegister* m_xmm14;
CRegister* m_xmm15;
*/
// ========================================================================
// >> 16-bit Segment registers
// ========================================================================
CRegister* m_cs;
CRegister* m_ss;
CRegister* m_ds;
CRegister* m_es;
CRegister* m_fs;
CRegister* m_gs;
// ========================================================================
// >> 80-bit FPU registers
// ========================================================================
CRegister* m_st0;
CRegister* m_st1;
CRegister* m_st2;
CRegister* m_st3;
CRegister* m_st4;
CRegister* m_st5;
CRegister* m_st6;
CRegister* m_st7;
};
#endif // _REGISTERS_H

View File

@ -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);
}

View File

@ -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

View File

@ -0,0 +1,633 @@
#include "dynhooks_sourcepawn.h"
#include "util.h"
#include <memory>
#ifdef KE_WINDOWS
#include "conventions/x86MsCdecl.h"
#include "conventions/x86MsThiscall.h"
#include "conventions/x86MsStdcall.h"
#include "conventions/x86MsFastcall.h"
typedef x86MsCdecl x86DetourCdecl;
typedef x86MsThiscall x86DetourThisCall;
typedef x86MsStdcall x86DetourStdCall;
typedef x86MsFastcall x86DetourFastCall;
#elif defined KE_LINUX
#include "conventions/x86GccCdecl.h"
#include "conventions/x86GccThiscall.h"
#include "conventions/x86MsStdcall.h"
#include "conventions/x86MsFastcall.h"
typedef x86GccCdecl x86DetourCdecl;
typedef x86GccThiscall x86DetourThisCall;
// Uhm, stdcall on linux?
typedef x86MsStdcall x86DetourStdCall;
// Uhumm, fastcall on linux?
typedef x86MsFastcall x86DetourFastCall;
#else
#error "Unsupported platform."
#endif
// Keep a map of detours and their registered plugin callbacks.
DetourMap g_pPreDetours;
DetourMap g_pPostDetours;
void UnhookFunction(HookType_t hookType, CHook *pDetour)
{
CHookManager *pDetourManager = GetHookManager();
pDetour->RemoveCallback(hookType, (HookHandlerFn *)(void *)&HandleDetour);
// Only disable the detour if there are no more listeners.
if (!pDetour->AreCallbacksRegistered())
pDetourManager->UnhookFunction(pDetour->m_pFunc);
}
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback)
{
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
// See if we already have this detour in our list.
PluginCallbackList *wrappers;
DetourMap::Insert f = map->findForAdd(pDetour);
if (f.found())
{
wrappers = f->value;
}
else
{
// Create a vector to store all the plugin callbacks in.
wrappers = new PluginCallbackList;
if (!map->add(f, pDetour, wrappers))
{
delete wrappers;
UnhookFunction(hookType, pDetour);
return false;
}
}
// Add the plugin callback to the detour list.
CDynamicHooksSourcePawn *pWrapper = new CDynamicHooksSourcePawn(setup, pDetour, pCallback, hookType == HOOKTYPE_POST);
wrappers->push_back(pWrapper);
return true;
}
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback)
{
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
DetourMap::Result res = map->find(pDetour);
if (!res.found())
return false;
// Remove the plugin's callback
bool bRemoved = false;
PluginCallbackList *wrappers = res->value;
for (int i = wrappers->size()-1; i >= 0 ; i--)
{
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
if (pWrapper->plugin_callback == pCallback)
{
bRemoved = true;
delete pWrapper;
wrappers->erase(wrappers->begin() + i);
}
}
// No more plugin hooks on this callback. Free our structures.
if (wrappers->empty())
{
delete wrappers;
UnhookFunction(hookType, pDetour);
map->remove(res);
}
return bRemoved;
}
void RemoveAllCallbacksForContext(HookType_t hookType, DetourMap *map, IPluginContext *pContext)
{
PluginCallbackList *wrappers;
CDynamicHooksSourcePawn *pWrapper;
DetourMap::iterator it = map->iter();
// Run through all active detours we added.
for (; !it.empty(); it.next())
{
wrappers = it->value;
// See if there are callbacks of this plugin context registered
// and remove them.
for (int i = wrappers->size() - 1; i >= 0; i--)
{
pWrapper = wrappers->at(i);
if (pWrapper->plugin_callback->GetParentRuntime()->GetDefaultContext() != pContext)
continue;
delete pWrapper;
wrappers->erase(wrappers->begin() + i);
}
// No plugin interested in this hook anymore. unhook.
if (wrappers->empty())
{
delete wrappers;
UnhookFunction(hookType, it->key);
it.erase();
}
}
}
void RemoveAllCallbacksForContext(IPluginContext *pContext)
{
RemoveAllCallbacksForContext(HOOKTYPE_PRE, &g_pPreDetours, pContext);
RemoveAllCallbacksForContext(HOOKTYPE_POST, &g_pPostDetours, pContext);
}
void CleanupDetours(HookType_t hookType, DetourMap *map)
{
PluginCallbackList *wrappers;
CDynamicHooksSourcePawn *pWrapper;
DetourMap::iterator it = map->iter();
// Run through all active detours we added.
for (; !it.empty(); it.next())
{
wrappers = it->value;
// Remove all callbacks
for (int i = wrappers->size() - 1; i >= 0; i--)
{
pWrapper = wrappers->at(i);
delete pWrapper;
}
// Unhook the function
delete wrappers;
UnhookFunction(hookType, it->key);
}
map->clear();
}
void CleanupDetours()
{
CleanupDetours(HOOKTYPE_PRE, &g_pPreDetours);
CleanupDetours(HOOKTYPE_POST, &g_pPostDetours);
}
ICallingConvention *ConstructCallingConvention(HookSetup *setup)
{
// Convert function parameter types into DynamicHooks structures.
std::vector<DataTypeSized_t> vecArgTypes;
for (size_t i = 0; i < setup->params.size(); i++)
{
ParamInfo &info = setup->params[i];
DataTypeSized_t type;
type.type = DynamicHooks_ConvertParamTypeFrom(info.type);
type.size = info.size;
type.custom_register = info.custom_register;
vecArgTypes.push_back(type);
}
DataTypeSized_t returnType;
returnType.type = DynamicHooks_ConvertReturnTypeFrom(setup->returnType);
returnType.size = 0;
// TODO: Add support for a custom return register.
returnType.custom_register = None;
ICallingConvention *pCallConv = nullptr;
switch (setup->callConv)
{
case CallConv_CDECL:
pCallConv = new x86DetourCdecl(vecArgTypes, returnType);
break;
case CallConv_THISCALL:
pCallConv = new x86DetourThisCall(vecArgTypes, returnType);
break;
case CallConv_STDCALL:
pCallConv = new x86DetourStdCall(vecArgTypes, returnType);
break;
case CallConv_FASTCALL:
pCallConv = new x86DetourFastCall(vecArgTypes, returnType);
break;
default:
smutils->LogError(myself, "Unknown calling convention %d.", setup->callConv);
break;
}
return pCallConv;
}
// Some arguments might be optimized to be passed in registers instead of the stack.
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup)
{
// The registers the arguments are passed in might not be the same size as the actual parameter type.
// Update the type info to the size of the register that's now holding that argument,
// so we can copy the whole value.
ICallingConvention* callingConvention = pDetour->m_pCallingConvention;
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
int numArgs = argTypes.size();
for (int i = 0; i < numArgs; i++)
{
// Ignore regular arguments on the stack.
if (argTypes[i].custom_register == None)
continue;
CRegister *reg = pDetour->m_pRegisters->GetRegister(argTypes[i].custom_register);
// That register can't be handled yet.
if (!reg)
return false;
argTypes[i].size = reg->m_iSize;
setup->params[i].size = reg->m_iSize;
}
return true;
}
// Central handler for all detours. Heart of the detour support.
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour)
{
// Can't call into SourcePawn offthread.
if (g_MainThreadId != std::this_thread::get_id())
return ReturnAction_Ignored;
DetourMap *map;
if (hookType == HOOKTYPE_PRE)
map = &g_pPreDetours;
else
map = &g_pPostDetours;
// Find the callback list for this detour.
DetourMap::Result r = map->find(pDetour);
if (!r.found())
return ReturnAction_Ignored;
// List of all callbacks.
PluginCallbackList *wrappers = r->value;
HookReturnStruct *returnStruct = NULL;
Handle_t rHndl = BAD_HANDLE;
HookParamsStruct *paramStruct = NULL;
Handle_t pHndl = BAD_HANDLE;
int argNum = pDetour->m_pCallingConvention->m_vecArgTypes.size();
// Keep a copy of the last return value if some plugin wants to override or supercede the function.
ReturnAction_t finalRet = ReturnAction_Ignored;
std::unique_ptr<uint8_t[]> finalRetBuf = std::make_unique<uint8_t[]>(pDetour->m_pCallingConvention->m_returnType.size);
// Call all the plugin functions..
for (size_t i = 0; i < wrappers->size(); i++)
{
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
IPluginFunction *pCallback = pWrapper->plugin_callback;
// Create a seperate buffer for changed return values for this plugin.
// We update the finalRet above if the tempRet is higher than the previous ones in the callback list.
ReturnAction_t tempRet = ReturnAction_Ignored;
uint8_t *tempRetBuf = nullptr;
// Find the this pointer for thiscalls.
// Don't even try to load it if the plugin doesn't care and set it to be ignored.
if (pWrapper->callConv == CallConv_THISCALL && pWrapper->thisType != ThisPointer_Ignore)
{
// The this pointer is implicitly always the first argument.
void *thisPtr = pDetour->GetArgument<void *>(0);
cell_t thisAddr = GetThisPtr(thisPtr, pWrapper->thisType);
pCallback->PushCell(thisAddr);
}
// Create the structure for plugins to change/get the return value if the function returns something.
if (pWrapper->returnType != ReturnType_Void)
{
// Create a handle for the return value to pass to the plugin callback.
returnStruct = pWrapper->GetReturnStruct();
HandleError err;
rHndl = handlesys->CreateHandle(g_HookReturnHandle, returnStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
if (!rHndl)
{
pCallback->Cancel();
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ReturnHandle in preparation to call hook callback. (error %d)", err);
if (returnStruct)
delete returnStruct;
// Don't call more callbacks. They will probably fail too.
break;
}
pCallback->PushCell(rHndl);
}
// Create the structure for plugins to access the function arguments if it has some.
if (argNum > 0)
{
paramStruct = pWrapper->GetParamStruct();
HandleError err;
pHndl = handlesys->CreateHandle(g_HookParamsHandle, paramStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
if (!pHndl)
{
pCallback->Cancel();
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ThisHandle in preparation to call hook callback. (error %d)", err);
// Don't leak our own handles here! Free the return struct if we fail during the argument marshalling.
if (rHndl)
{
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
handlesys->FreeHandle(rHndl, &sec);
rHndl = BAD_HANDLE;
}
if (paramStruct)
delete paramStruct;
// Don't call more callbacks. They will probably fail too.
break;
}
pCallback->PushCell(pHndl);
}
// Run the plugin callback.
cell_t result = (cell_t)MRES_Ignored;
pCallback->Execute(&result);
switch ((MRESReturn)result)
{
case MRES_Handled:
tempRet = ReturnAction_Handled;
break;
case MRES_ChangedHandled:
tempRet = ReturnAction_Handled;
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
pWrapper->UpdateParamsFromStruct(paramStruct);
break;
case MRES_ChangedOverride:
case MRES_Override:
case MRES_Supercede:
// See if this function returns something we should override.
if (pWrapper->returnType != ReturnType_Void)
{
// Make sure the plugin provided a new return value. Could be an oversight if MRES_ChangedOverride
// is called without the return value actually being changed.
if (!returnStruct->isChanged)
{
//Throw an error if no override was set
tempRet = ReturnAction_Ignored;
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Tried to override return value without return value being set");
break;
}
if (pWrapper->returnType == ReturnType_String || pWrapper->returnType == ReturnType_Int || pWrapper->returnType == ReturnType_Bool)
{
tempRetBuf = *(uint8_t **)returnStruct->newResult;
}
else if (pWrapper->returnType == ReturnType_Float)
{
*(float *)&tempRetBuf = *(float *)returnStruct->newResult;
}
else
{
tempRetBuf = (uint8_t *)returnStruct->newResult;
}
}
// Store if the plugin wants the original function to be called.
if (result == MRES_Supercede)
tempRet = ReturnAction_Supercede;
else
tempRet = ReturnAction_Override;
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
if (result == MRES_ChangedOverride)
pWrapper->UpdateParamsFromStruct(paramStruct);
break;
default:
tempRet = ReturnAction_Ignored;
break;
}
// Prioritize the actions.
if (finalRet <= tempRet)
{
// Copy the action and return value.
finalRet = tempRet;
memcpy(finalRetBuf.get(), &tempRetBuf, pDetour->m_pCallingConvention->m_returnType.size);
}
// Free the handles again.
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
if (returnStruct)
{
handlesys->FreeHandle(rHndl, &sec);
}
if (paramStruct)
{
handlesys->FreeHandle(pHndl, &sec);
}
}
// If we want to use our own return value, write it back.
if (finalRet >= ReturnAction_Override)
{
void* pPtr = pDetour->m_pCallingConvention->GetReturnPtr(pDetour->m_pRegisters);
memcpy(pPtr, finalRetBuf.get(), pDetour->m_pCallingConvention->m_returnType.size);
pDetour->m_pCallingConvention->ReturnPtrChanged(pDetour->m_pRegisters, pPtr);
}
return finalRet;
}
CDynamicHooksSourcePawn::CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post)
{
this->params = setup->params;
this->offset = -1;
this->returnFlag = setup->returnFlag;
this->returnType = setup->returnType;
this->post = post;
this->plugin_callback = pCallback;
this->entity = -1;
this->thisType = setup->thisType;
this->hookType = setup->hookType;
this->m_pDetour = pDetour;
this->callConv = setup->callConv;
}
HookReturnStruct *CDynamicHooksSourcePawn::GetReturnStruct()
{
// Create buffers to store the return value of the function.
HookReturnStruct *res = new HookReturnStruct();
res->isChanged = false;
res->type = this->returnType;
res->orgResult = NULL;
res->newResult = NULL;
// Copy the actual function's return value too for post hooks.
if (this->post)
{
switch (this->returnType)
{
case ReturnType_String:
res->orgResult = malloc(sizeof(string_t));
res->newResult = malloc(sizeof(string_t));
*(string_t *)res->orgResult = m_pDetour->GetReturnValue<string_t>();
break;
case ReturnType_Int:
res->orgResult = malloc(sizeof(int));
res->newResult = malloc(sizeof(int));
*(int *)res->orgResult = m_pDetour->GetReturnValue<int>();
break;
case ReturnType_Bool:
res->orgResult = malloc(sizeof(bool));
res->newResult = malloc(sizeof(bool));
*(bool *)res->orgResult = m_pDetour->GetReturnValue<bool>();
break;
case ReturnType_Float:
res->orgResult = malloc(sizeof(float));
res->newResult = malloc(sizeof(float));
*(float *)res->orgResult = m_pDetour->GetReturnValue<float>();
break;
case ReturnType_Vector:
{
res->orgResult = malloc(sizeof(SDKVector));
res->newResult = malloc(sizeof(SDKVector));
SDKVector vec = m_pDetour->GetReturnValue<SDKVector>();
*(SDKVector *)res->orgResult = vec;
break;
}
default:
res->orgResult = m_pDetour->GetReturnValue<void *>();
break;
}
}
// Pre hooks don't have access to the return value yet - duh.
// Just create the buffers for overridden values.
// TODO: Strip orgResult malloc.
else
{
switch (this->returnType)
{
case ReturnType_String:
res->orgResult = malloc(sizeof(string_t));
res->newResult = malloc(sizeof(string_t));
*(string_t *)res->orgResult = NULL_STRING;
break;
case ReturnType_Vector:
res->orgResult = malloc(sizeof(SDKVector));
res->newResult = malloc(sizeof(SDKVector));
*(SDKVector *)res->orgResult = SDKVector();
break;
case ReturnType_Int:
res->orgResult = malloc(sizeof(int));
res->newResult = malloc(sizeof(int));
*(int *)res->orgResult = 0;
break;
case ReturnType_Bool:
res->orgResult = malloc(sizeof(bool));
res->newResult = malloc(sizeof(bool));
*(bool *)res->orgResult = false;
break;
case ReturnType_Float:
res->orgResult = malloc(sizeof(float));
res->newResult = malloc(sizeof(float));
*(float *)res->orgResult = 0.0;
break;
}
}
return res;
}
HookParamsStruct *CDynamicHooksSourcePawn::GetParamStruct()
{
// Save argument values of detoured function.
HookParamsStruct *params = new HookParamsStruct();
params->dg = this;
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
size_t stackSize = callingConvention->GetArgStackSize();
size_t paramsSize = stackSize + callingConvention->GetArgRegisterSize();
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
size_t numArgs = argTypes.size();
// Create space for original parameters and changes plugins might do.
params->orgParams = (void **)malloc(paramsSize);
params->newParams = (void **)malloc(paramsSize);
params->isChanged = (bool *)malloc(numArgs * sizeof(bool));
// Save old stack parameters.
if (stackSize > 0)
{
void *pArgPtr = m_pDetour->m_pCallingConvention->GetStackArgumentPtr(m_pDetour->m_pRegisters);
memcpy(params->orgParams, pArgPtr, stackSize);
}
memset(params->newParams, 0, paramsSize);
memset(params->isChanged, false, numArgs * sizeof(bool));
size_t firstArg = 0;
// TODO: Support custom register for this ptr.
if (callConv == CallConv_THISCALL)
firstArg = 1;
// Save the old parameters passed in a register.
size_t offset = stackSize;
for (size_t i = 0; i < numArgs; i++)
{
// We already saved the stack arguments.
if (argTypes[i].custom_register == None)
continue;
size_t size = argTypes[i].size;
// Register argument values are saved after all stack arguments in this buffer.
void *paramAddr = (void *)((intptr_t)params->orgParams + offset);
void *regAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
memcpy(paramAddr, regAddr, size);
offset += size;
}
return params;
}
void CDynamicHooksSourcePawn::UpdateParamsFromStruct(HookParamsStruct *params)
{
// Function had no params to update now.
if (!params)
return;
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
size_t stackSize = callingConvention->GetArgStackSize();
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
size_t numArgs = argTypes.size();
size_t firstArg = 0;
// TODO: Support custom register for this ptr.
if (callConv == CallConv_THISCALL)
firstArg = 1;
size_t stackOffset = 0;
// Values of arguments stored in registers are saved after the stack arguments.
size_t registerOffset = stackSize;
size_t offset;
for (size_t i = 0; i < numArgs; i++)
{
size_t size = argTypes[i].size;
// Only have to copy something if the plugin changed this parameter.
if (params->isChanged[i])
{
// Get the offset of this argument in the linear buffer. Register argument values are placed after all stack arguments.
offset = argTypes[i].custom_register == None ? stackOffset : registerOffset;
void *paramAddr = (void *)((intptr_t)params->newParams + offset);
void *stackAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
memcpy(stackAddr, paramAddr, size);
}
// Keep track of the seperate stack and register arguments.
if (argTypes[i].custom_register == None)
stackOffset += size;
else
registerOffset += size;
}
}

View File

@ -0,0 +1,42 @@
#ifndef _INCLUDE_DYNHOOKS_SP_H_
#define _INCLUDE_DYNHOOKS_SP_H_
#include "manager.h"
#include "vhook.h"
#include <am-hashmap.h>
class CDynamicHooksSourcePawn;
typedef ke::HashMap<IPluginFunction *, CDynamicHooksSourcePawn *, ke::PointerPolicy<IPluginFunction>> CallbackMap;
typedef std::vector<CDynamicHooksSourcePawn *> PluginCallbackList;
typedef ke::HashMap<CHook *, PluginCallbackList *, ke::PointerPolicy<CHook>> DetourMap;
//extern ke::Vector<CHook *> g_pDetours;
// Keep a list of plugin callback -> Hook wrapper for easily removing plugin hooks
//extern CallbackMap g_pPluginPreDetours;
//extern CallbackMap g_pPluginPostDetours;
// Keep a list of hook -> callbacks for calling in the detour handler
extern DetourMap g_pPreDetours;
extern DetourMap g_pPostDetours;
class CDynamicHooksSourcePawn : public DHooksInfo {
public:
CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post);
HookReturnStruct *GetReturnStruct();
HookParamsStruct *GetParamStruct();
void UpdateParamsFromStruct(HookParamsStruct *params);
public:
CHook *m_pDetour;
CallingConvention callConv;
};
ICallingConvention *ConstructCallingConvention(HookSetup *setup);
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup);
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour);
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback);
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback);
void RemoveAllCallbacksForContext(IPluginContext *pContext);
void CleanupDetours();
#endif

View File

@ -0,0 +1,165 @@
#include "extension.h"
#include "listeners.h"
#include "dynhooks_sourcepawn.h"
#include "signatures.h"
DHooks g_DHooksIface; /**< Global singleton for extension's main interface */
SMEXT_LINK(&g_DHooksIface);
IBinTools *g_pBinTools;
ISDKHooks *g_pSDKHooks;
ISDKTools *g_pSDKTools;
DHooksEntityListener *g_pEntityListener = NULL;
HandleType_t g_HookSetupHandle = 0;
HandleType_t g_HookParamsHandle = 0;
HandleType_t g_HookReturnHandle = 0;
std::thread::id g_MainThreadId;
bool DHooks::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
HandleError err;
g_HookSetupHandle = handlesys->CreateType("HookSetup", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookSetupHandle == 0)
{
snprintf(error, maxlength, "Could not create hook setup handle type (err: %d)", err);
return false;
}
g_HookParamsHandle = handlesys->CreateType("HookParams", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookParamsHandle == 0)
{
snprintf(error, maxlength, "Could not create hook params handle type (err: %d)", err);
return false;
}
g_HookReturnHandle = handlesys->CreateType("HookReturn", this, 0, NULL, NULL, myself->GetIdentity(), &err);
if(g_HookReturnHandle == 0)
{
snprintf(error, maxlength, "Could not create hook return handle type (err: %d)", err);
return false;
}
if (!g_pPreDetours.init())
{
snprintf(error, maxlength, "Could not initialize pre hook detours hashmap.");
return false;
}
if (!g_pPostDetours.init())
{
snprintf(error, maxlength, "Could not initialize post hook detours hashmap.");
return false;
}
sharesys->AddDependency(myself, "bintools.ext", true, true);
sharesys->AddDependency(myself, "sdktools.ext", true, true);
sharesys->AddDependency(myself, "sdkhooks.ext", true, true);
sharesys->RegisterLibrary(myself, "dhooks");
plsys->AddPluginsListener(this);
sharesys->AddNatives(myself, g_Natives);
g_pEntityListener = new DHooksEntityListener();
g_pSignatures = new SignatureGameConfig();
g_MainThreadId = std::this_thread::get_id();
return true;
}
void DHooks::OnHandleDestroy(HandleType_t type, void *object)
{
if(type == g_HookSetupHandle)
{
delete (HookSetup *)object;
}
else if(type == g_HookParamsHandle)
{
delete (HookParamsStruct *)object;
}
else if(type == g_HookReturnHandle)
{
delete (HookReturnStruct *)object;
}
}
void DHooks::SDK_OnAllLoaded()
{
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks);
g_pSDKHooks->AddEntityListener(g_pEntityListener);
gameconfs->AddUserConfigHook("Functions", g_pSignatures);
}
void DHooks::SDK_OnUnload()
{
CleanupHooks();
CleanupDetours();
if(g_pEntityListener)
{
g_pEntityListener->CleanupListeners();
g_pEntityListener->CleanupRemoveList();
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
delete g_pEntityListener;
}
plsys->RemovePluginsListener(this);
handlesys->RemoveType(g_HookSetupHandle, myself->GetIdentity());
handlesys->RemoveType(g_HookParamsHandle, myself->GetIdentity());
handlesys->RemoveType(g_HookReturnHandle, myself->GetIdentity());
gameconfs->RemoveUserConfigHook("Functions", g_pSignatures);
}
bool DHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
{
if(!SetupHookManager(ismm))
{
snprintf(error, maxlength, "Failed to get IHookManagerAutoGen iface");
return false;
}
return true;
}
void DHooks::OnPluginUnloaded(IPlugin *plugin)
{
CleanupHooks(plugin->GetBaseContext());
RemoveAllCallbacksForContext(plugin->GetBaseContext());
if(g_pEntityListener)
{
g_pEntityListener->CleanupListeners(plugin->GetBaseContext());
}
}
// The next 3 functions handle cleanup if our interfaces are going to be unloaded
bool DHooks::QueryRunning(char *error, size_t maxlength)
{
SM_CHECK_IFACE(SDKTOOLS, g_pSDKTools);
SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
SM_CHECK_IFACE(SDKHOOKS, g_pSDKHooks);
return true;
}
void DHooks::NotifyInterfaceDrop(SMInterface *pInterface)
{
if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKHOOKS_NAME) == 0)
{
if(g_pEntityListener)
{
// If this fails, remove this line and just delete the ent listener instead
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
g_pEntityListener->CleanupListeners();
delete g_pEntityListener;
g_pEntityListener = NULL;
}
g_pSDKHooks = NULL;
}
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_BINTOOLS_NAME) == 0)
{
g_pBinTools = NULL;
}
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKTOOLS_NAME) == 0)
{
g_pSDKTools = NULL;
}
}

View File

@ -0,0 +1,134 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Sample Extension
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
/**
* @file extension.h
* @brief Sample extension code header.
*/
#include "smsdk_ext.h"
#include <ISDKHooks.h>
#include <IBinTools.h>
#include <ISDKTools.h>
#include "sdk-hacks.h"
#include <thread>
/**
* @brief Sample implementation of the SDK Extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
*/
class DHooks : public SDKExtension, public ISMEntityListener, public IPluginsListener, public IHandleTypeDispatch
{
public:
virtual void OnHandleDestroy(HandleType_t type, void *object);
public: //IPluginsListener
virtual void OnPluginUnloaded(IPlugin *plugin);
public:
/**
* @brief This is called after the initial loading sequence has been processed.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @param late Whether or not the module was loaded after map load.
* @return True to succeed loading, false to fail.
*/
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
*/
virtual void SDK_OnUnload();
/**
* @brief This is called once all known extensions have been loaded.
* Note: It is is a good idea to add natives here, if any are provided.
*/
virtual void SDK_OnAllLoaded();
/**
* @brief Called when the pause state is changed.
*/
//virtual void SDK_OnPauseChange(bool paused);
/**
* @brief this is called when Core wants to know if your extension is working.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @return True if working, false otherwise.
*/
virtual bool QueryRunning(char *error, size_t maxlength);
//virtual bool QueryInterfaceDrop(SMInterface *pInterface);
virtual void NotifyInterfaceDrop(SMInterface *pInterface);
virtual void OnCoreMapEnd();
public:
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @param late Whether or not Metamod considers this a late load.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
/**
* @brief Called when Metamod is detaching, after the extension version is called.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
/**
* @brief Called when Metamod's pause state is changing.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param paused Pause state being set.
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
#endif
};
extern SourceHook::IHookManagerAutoGen *g_pHookManager;
extern sp_nativeinfo_t g_Natives[];
extern std::thread::id g_MainThreadId;
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_

View File

@ -0,0 +1,138 @@
#include "listeners.h"
#include "vhook.h"
using namespace SourceHook;
std::vector<EntityListener> g_EntityListeners;
std::vector<DHooksManager *>g_pRemoveList;
void FrameCleanupHooks(void *data)
{
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
delete manager;
g_pRemoveList.erase(g_pRemoveList.begin() + i);
}
}
void DHooks::OnCoreMapEnd()
{
for(int i = g_pHooks.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_GameRules)
{
delete manager;
g_pHooks.erase(g_pHooks.begin() + i);
}
}
}
void DHooksEntityListener::CleanupListeners(IPluginContext *pContext)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
if(pContext == NULL || pContext == g_EntityListeners.at(i).callback->GetParentRuntime()->GetDefaultContext())
{
g_EntityListeners.erase(g_EntityListeners.begin() + i);
}
}
for (int i = g_pRemoveList.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
IPluginFunction *cb = manager->callback->plugin_callback;
if (pContext == NULL || (cb && pContext == cb->GetParentRuntime()->GetDefaultContext()))
{
manager->callback->plugin_callback = nullptr;
manager->remove_callback = nullptr;
}
}
}
void DHooksEntityListener::CleanupRemoveList()
{
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
{
DHooksManager *manager = g_pRemoveList.at(i);
delete manager;
}
g_pRemoveList.clear();
}
void DHooksEntityListener::OnEntityCreated(CBaseEntity *pEntity, const char *classname)
{
int entity = gamehelpers->EntityToBCompatRef(pEntity);
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.type == ListenType_Created)
{
IPluginFunction *callback = listerner.callback;
callback->PushCell(entity);
callback->PushString(classname);
callback->Execute(NULL);
}
}
}
void DHooksEntityListener::OnEntityDestroyed(CBaseEntity *pEntity)
{
int entity = gamehelpers->EntityToBCompatRef(pEntity);
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.type == ListenType_Deleted)
{
IPluginFunction *callback = listerner.callback;
callback->PushCell(gamehelpers->EntityToBCompatRef(pEntity));
callback->Execute(NULL);
}
}
for(int i = g_pHooks.size() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_Entity && manager->callback->entity == entity)
{
if(g_pRemoveList.size() == 0)
{
smutils->AddFrameAction(&FrameCleanupHooks, NULL);
}
g_pRemoveList.push_back(manager);
g_pHooks.erase(g_pHooks.begin() + i);
}
}
}
bool DHooksEntityListener::AddPluginEntityListener(ListenType type, IPluginFunction *callback)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.callback == callback && listerner.type == type)
{
return true;
}
}
EntityListener listener;
listener.callback = callback;
listener.type = type;
g_EntityListeners.push_back(listener);
return true;
}
bool DHooksEntityListener::RemovePluginEntityListener(ListenType type, IPluginFunction *callback)
{
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
{
EntityListener listerner = g_EntityListeners.at(i);
if(listerner.callback == callback && listerner.type == type)
{
g_EntityListeners.erase(g_EntityListeners.begin() + i);
return true;
}
}
return false;
}

View File

@ -0,0 +1,32 @@
#ifndef _INCLUDE_LISTENERS_H_
#define _INCLUDE_LISTENERS_H_
#include "extension.h"
#include "vhook.h"
enum ListenType
{
ListenType_Created,
ListenType_Deleted
};
class DHooksEntityListener : public ISMEntityListener
{
public:
virtual void OnEntityCreated(CBaseEntity *pEntity, const char *classname);
virtual void OnEntityDestroyed(CBaseEntity *pEntity);
void CleanupListeners(IPluginContext *func = NULL);
void CleanupRemoveList();
bool AddPluginEntityListener(ListenType type, IPluginFunction *callback);
bool RemovePluginEntityListener(ListenType type, IPluginFunction *callback);
};
struct EntityListener
{
ListenType type;
IPluginFunction *callback;
};
extern std::vector<DHooksManager *> g_pHooks;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
#ifndef _INCLUDE_NATIVES_H_
#define _INCLUDE_NATIVES_H_
#include "extension.h"
#include "vhook.h"
#include "listeners.h"
extern DHooksEntityListener *g_pEntityListener;
extern ISDKTools *g_pSDKTools;
extern HandleType_t g_HookSetupHandle;
extern HandleType_t g_HookParamsHandle;
extern HandleType_t g_HookReturnHandle;
extern std::vector<DHooksManager *> g_pHooks;
#endif

View File

@ -0,0 +1,75 @@
#ifndef _INCLUDE_SDK_HACKS_H_
#define _INCLUDE_SDK_HACKS_H_
class SDKVector
{
public:
SDKVector(float x1, float y1, float z1)
{
this->x = x1;
this->y = y1;
this->z = z1;
}
SDKVector(void)
{
this->x = 0.0;
this->y = 0.0;
this->z = 0.0;
}
float x;
float y;
float z;
};
struct string_t
{
public:
bool operator!() const { return ( pszValue == NULL ); }
bool operator==( const string_t &rhs ) const { return ( pszValue == rhs.pszValue ); }
bool operator!=( const string_t &rhs ) const { return ( pszValue != rhs.pszValue ); }
bool operator<( const string_t &rhs ) const { return ((void *)pszValue < (void *)rhs.pszValue ); }
const char *ToCStr() const { return ( pszValue ) ? pszValue : ""; }
protected:
const char *pszValue;
};
struct castable_string_t : public string_t // string_t is used in unions, hence, no constructor allowed
{
castable_string_t() { pszValue = NULL; }
castable_string_t( const char *pszFrom ) { pszValue = (pszFrom && *pszFrom) ? pszFrom : 0; }
};
#define NULL_STRING castable_string_t()
#define STRING( string_t_obj ) (string_t_obj).ToCStr()
#define MAKE_STRING( c_str ) castable_string_t( c_str )
#define FL_EDICT_FREE (1<<1)
struct edict_t
{
public:
bool IsFree()
{
return (m_fStateFlags & FL_EDICT_FREE) != 0;
}
private:
int m_fStateFlags;
};
class CBaseHandle
{
/*public:
bool IsValid() const {return m_Index != INVALID_EHANDLE_INDEX;}
int GetEntryIndex() const
{
if ( !IsValid() )
return NUM_ENT_ENTRIES-1;
return m_Index & ENT_ENTRY_MASK;
}*/
private:
unsigned long m_Index;
};
#endif

View File

@ -0,0 +1,574 @@
#include <signatures.h>
SignatureGameConfig *g_pSignatures;
enum ParseState
{
PState_None,
PState_Root,
PState_Function,
PState_Arguments,
PState_Argument
};
ParseState g_ParseState;
unsigned int g_IgnoreLevel;
// The parent section type of a platform specific "windows" or "linux" section.
ParseState g_PlatformOnlyState;
SignatureWrapper *g_CurrentSignature;
std::string g_CurrentFunctionName;
ArgumentInfo g_CurrentArgumentInfo;
SignatureWrapper *SignatureGameConfig::GetFunctionSignature(const char *function)
{
auto sig = signatures_.find(function);
if (!sig.found())
return nullptr;
return sig->value;
}
/**
* Game config "Functions" section parsing.
*/
SMCResult SignatureGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
// We're ignoring the parent section. Ignore all child sections as well.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel++;
return SMCResult_Continue;
}
// Handle platform specific sections first.
#if defined WIN32
if (!strcmp(name, "windows"))
#elif defined _LINUX
if (!strcmp(name, "linux"))
#elif defined _OSX
if (!strcmp(name, "mac"))
#endif
{
// We're already in a section for a different OS that we're ignoring. Can't have a section for our OS in here.
if (g_IgnoreLevel > 0)
{
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// We don't support nested (useless) sections of the same OS like "windows" { "windows" { "foo" "bar" } }
if (g_PlatformOnlyState != PState_None)
{
smutils->LogError(myself, "Duplicate platform specific section for \"%s\". Already parsing only for that OS: line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
// This is a specific block for us.
g_PlatformOnlyState = g_ParseState;
return SMCResult_Continue;
}
#if defined WIN32
else if (!strcmp(name, "linux") || !strcmp(name, "mac"))
#elif defined _LINUX
else if (!strcmp(name, "windows") || !strcmp(name, "mac"))
#elif defined _OSX
else if (!strcmp(name, "windows") || !strcmp(name, "linux"))
#endif
{
if (g_PlatformOnlyState != PState_None)
{
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// A specific block for a different platform.
g_IgnoreLevel++;
return SMCResult_Continue;
}
switch (g_ParseState)
{
case PState_Root:
{
auto sig = signatures_.find(name);
if (sig.found())
g_CurrentSignature = sig->value;
else
g_CurrentSignature = new SignatureWrapper();
g_CurrentFunctionName = name;
g_ParseState = PState_Function;
break;
}
case PState_Function:
{
if (!strcmp(name, "arguments"))
{
g_ParseState = PState_Arguments;
}
else
{
smutils->LogError(myself, "Unknown subsection \"%s\" (expected \"arguments\"): line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
break;
}
case PState_Arguments:
{
g_ParseState = PState_Argument;
g_CurrentArgumentInfo.name = name;
// Reset the parameter info.
ParamInfo info;
memset(&info, 0, sizeof(info));
info.flags = PASSFLAG_BYVAL;
g_CurrentArgumentInfo.info = info;
// See if we already have info about this argument.
for (auto &arg : g_CurrentSignature->args) {
if (!arg.name.compare(name)) {
// Continue changing that argument now.
g_CurrentArgumentInfo.info = arg.info;
break;
}
}
break;
}
default:
smutils->LogError(myself, "Unknown subsection \"%s\": line: %i col: %i", name, states->line, states->col);
return SMCResult_HaltFail;
}
return SMCResult_Continue;
}
SMCResult SignatureGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
// We don't care for anything in this section or subsections.
if (g_IgnoreLevel > 0)
return SMCResult_Continue;
switch (g_ParseState)
{
case PState_Function:
if (!strcmp(key, "signature"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->signature = value;
}
else if (!strcmp(key, "address"))
{
if (g_CurrentSignature->signature.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->address = value;
}
else if (!strcmp(key, "offset"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->signature.length() > 0)
{
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->offset = value;
}
else if (!strcmp(key, "callconv"))
{
CallingConvention callConv;
if (!strcmp(value, "cdecl"))
callConv = CallConv_CDECL;
else if (!strcmp(value, "thiscall"))
callConv = CallConv_THISCALL;
else if (!strcmp(value, "stdcall"))
callConv = CallConv_STDCALL;
else if (!strcmp(value, "fastcall"))
callConv = CallConv_FASTCALL;
else
{
smutils->LogError(myself, "Invalid calling convention \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->callConv = callConv;
}
else if (!strcmp(key, "hooktype"))
{
HookType hookType;
if (!strcmp(value, "entity"))
hookType = HookType_Entity;
else if (!strcmp(value, "gamerules"))
hookType = HookType_GameRules;
else if (!strcmp(value, "raw"))
hookType = HookType_Raw;
else
{
smutils->LogError(myself, "Invalid hook type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
g_CurrentSignature->hookType = hookType;
}
else if (!strcmp(key, "return"))
{
g_CurrentSignature->retType = GetReturnTypeFromString(value);
if (g_CurrentSignature->retType == ReturnType_Unknown)
{
smutils->LogError(myself, "Invalid return type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "this"))
{
if (!strcmp(value, "ignore"))
g_CurrentSignature->thisType = ThisPointer_Ignore;
else if (!strcmp(value, "entity"))
g_CurrentSignature->thisType = ThisPointer_CBaseEntity;
else if (!strcmp(value, "address"))
g_CurrentSignature->thisType = ThisPointer_Address;
else
{
smutils->LogError(myself, "Invalid this type \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else
{
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
break;
case PState_Argument:
if (!strcmp(key, "type"))
{
g_CurrentArgumentInfo.info.type = GetHookParamTypeFromString(value);
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
{
smutils->LogError(myself, "Invalid argument type \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "size"))
{
g_CurrentArgumentInfo.info.size = static_cast<int>(strtol(value, NULL, 0));
if (g_CurrentArgumentInfo.info.size < 1)
{
smutils->LogError(myself, "Invalid argument size \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
}
else if (!strcmp(key, "flags"))
{
size_t flags = 0;
if (strstr(value, "byval"))
flags |= PASSFLAG_BYVAL;
if (strstr(value, "byref"))
flags |= PASSFLAG_BYREF;
if (strstr(value, "odtor"))
flags |= PASSFLAG_ODTOR;
if (strstr(value, "octor"))
flags |= PASSFLAG_OCTOR;
if (strstr(value, "oassignop"))
flags |= PASSFLAG_OASSIGNOP;
#ifdef PASSFLAG_OCOPYCTOR
if (strstr(value, "ocopyctor"))
flags |= PASSFLAG_OCOPYCTOR;
#endif
#ifdef PASSFLAG_OUNALIGN
if (strstr(value, "ounalign"))
flags |= PASSFLAG_OUNALIGN;
#endif
g_CurrentArgumentInfo.info.flags = flags;
}
else if (!strcmp(key, "register"))
{
g_CurrentArgumentInfo.info.custom_register = GetCustomRegisterFromString(value);
if (g_CurrentArgumentInfo.info.custom_register == Register_t::None)
{
smutils->LogError(myself, "Invalid register \"%s\": line: %i col: %i", value, states->line, states->col);
return SMCResult_HaltFail;
}
}
else
{
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
break;
default:
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SMCResult_HaltFail;
}
return SMCResult_Continue;
}
SMCResult SignatureGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
{
// We were ignoring this section.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel--;
return SMCResult_Continue;
}
// We were in a section only for our OS.
if (g_PlatformOnlyState == g_ParseState)
{
g_PlatformOnlyState = PState_None;
return SMCResult_Continue;
}
switch (g_ParseState)
{
case PState_Function:
g_ParseState = PState_Root;
if (!g_CurrentSignature->address.length() && !g_CurrentSignature->signature.length() && !g_CurrentSignature->offset.length())
{
smutils->LogError(myself, "Function \"%s\" doesn't have a \"signature\", \"offset\" nor \"address\" set: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// Save this function signature in our cache.
signatures_.insert(g_CurrentFunctionName.c_str(), g_CurrentSignature);
g_CurrentFunctionName = "";
g_CurrentSignature = nullptr;
break;
case PState_Arguments:
g_ParseState = PState_Function;
break;
case PState_Argument:
g_ParseState = PState_Arguments;
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
{
smutils->LogError(myself, "Missing argument type for argument \"%s\": line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
// The size wasn't set in the config. See if that's fine and we can guess it from the type.
if (!g_CurrentArgumentInfo.info.size)
{
if (g_CurrentArgumentInfo.info.type == HookParamType_Object)
{
smutils->LogError(myself, "Object param \"%s\" being set with no size: line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SMCResult_HaltFail;
}
else
{
g_CurrentArgumentInfo.info.size = GetParamTypeSize(g_CurrentArgumentInfo.info.type);
}
}
if (g_CurrentArgumentInfo.info.pass_type == SourceHook::PassInfo::PassType::PassType_Unknown)
g_CurrentArgumentInfo.info.pass_type = GetParamTypePassType(g_CurrentArgumentInfo.info.type);
// See if we were changing an existing argument.
bool changed = false;
for (auto &arg : g_CurrentSignature->args)
{
if (!arg.name.compare(g_CurrentArgumentInfo.name))
{
arg.info = g_CurrentArgumentInfo.info;
changed = true;
break;
}
}
// This was a new argument. Add it to the end of the list.
if (!changed)
g_CurrentSignature->args.push_back(g_CurrentArgumentInfo);
g_CurrentArgumentInfo.name = "";
break;
}
return SMCResult_Continue;
}
void SignatureGameConfig::ReadSMC_ParseStart()
{
g_ParseState = PState_Root;
g_IgnoreLevel = 0;
g_PlatformOnlyState = PState_None;
g_CurrentSignature = nullptr;
g_CurrentFunctionName = "";
g_CurrentArgumentInfo.name = "";
}
ReturnType SignatureGameConfig::GetReturnTypeFromString(const char *str)
{
if (!strcmp(str, "void"))
return ReturnType_Void;
else if (!strcmp(str, "int"))
return ReturnType_Int;
else if (!strcmp(str, "bool"))
return ReturnType_Bool;
else if (!strcmp(str, "float"))
return ReturnType_Float;
else if (!strcmp(str, "string"))
return ReturnType_String;
else if (!strcmp(str, "stringptr"))
return ReturnType_StringPtr;
else if (!strcmp(str, "charptr"))
return ReturnType_CharPtr;
else if (!strcmp(str, "vector"))
return ReturnType_Vector;
else if (!strcmp(str, "vectorptr"))
return ReturnType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return ReturnType_CBaseEntity;
else if (!strcmp(str, "edict"))
return ReturnType_Edict;
return ReturnType_Unknown;
}
HookParamType SignatureGameConfig::GetHookParamTypeFromString(const char *str)
{
if (!strcmp(str, "int"))
return HookParamType_Int;
else if (!strcmp(str, "bool"))
return HookParamType_Bool;
else if (!strcmp(str, "float"))
return HookParamType_Float;
else if (!strcmp(str, "string"))
return HookParamType_String;
else if (!strcmp(str, "stringptr"))
return HookParamType_StringPtr;
else if (!strcmp(str, "charptr"))
return HookParamType_CharPtr;
else if (!strcmp(str, "vectorptr"))
return HookParamType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return HookParamType_CBaseEntity;
else if (!strcmp(str, "objectptr"))
return HookParamType_ObjectPtr;
else if (!strcmp(str, "edict"))
return HookParamType_Edict;
else if (!strcmp(str, "object"))
return HookParamType_Object;
return HookParamType_Unknown;
}
Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
{
if (!strcmp(str, "al"))
return AL;
else if (!strcmp(str, "cl"))
return CL;
else if (!strcmp(str, "dl"))
return DL;
else if (!strcmp(str, "bl"))
return BL;
else if (!strcmp(str, "ah"))
return AH;
else if (!strcmp(str, "ch"))
return CH;
else if (!strcmp(str, "dh"))
return DH;
else if (!strcmp(str, "bh"))
return BH;
else if (!strcmp(str, "ax"))
return AX;
else if (!strcmp(str, "cx"))
return CX;
else if (!strcmp(str, "dx"))
return DX;
else if (!strcmp(str, "bx"))
return BX;
else if (!strcmp(str, "sp"))
return SP;
else if (!strcmp(str, "bp"))
return BP;
else if (!strcmp(str, "si"))
return SI;
else if (!strcmp(str, "di"))
return DI;
else if (!strcmp(str, "eax"))
return EAX;
else if (!strcmp(str, "ecx"))
return ECX;
else if (!strcmp(str, "edx"))
return EDX;
else if (!strcmp(str, "ebx"))
return EBX;
else if (!strcmp(str, "esp"))
return ESP;
else if (!strcmp(str, "ebp"))
return EBP;
else if (!strcmp(str, "esi"))
return ESI;
else if (!strcmp(str, "edi"))
return EDI;
else if (!strcmp(str, "mm0"))
return MM0;
else if (!strcmp(str, "mm1"))
return MM1;
else if (!strcmp(str, "mm2"))
return MM2;
else if (!strcmp(str, "mm3"))
return MM3;
else if (!strcmp(str, "mm4"))
return MM4;
else if (!strcmp(str, "mm5"))
return MM5;
else if (!strcmp(str, "mm6"))
return MM6;
else if (!strcmp(str, "mm7"))
return MM7;
else if (!strcmp(str, "xmm0"))
return XMM0;
else if (!strcmp(str, "xmm1"))
return XMM1;
else if (!strcmp(str, "xmm2"))
return XMM2;
else if (!strcmp(str, "xmm3"))
return XMM3;
else if (!strcmp(str, "xmm4"))
return XMM4;
else if (!strcmp(str, "xmm5"))
return XMM5;
else if (!strcmp(str, "xmm6"))
return XMM6;
else if (!strcmp(str, "xmm7"))
return XMM7;
else if (!strcmp(str, "cs"))
return CS;
else if (!strcmp(str, "ss"))
return SS;
else if (!strcmp(str, "ds"))
return DS;
else if (!strcmp(str, "es"))
return ES;
else if (!strcmp(str, "fs"))
return FS;
else if (!strcmp(str, "gs"))
return GS;
else if (!strcmp(str, "st0"))
return ST0;
return Register_t::None;
}

View File

@ -0,0 +1,53 @@
#ifndef _INCLUDE_SIGNATURES_H_
#define _INCLUDE_SIGNATURES_H_
#include "extension.h"
#include "util.h"
#include <string>
#include <vector>
#include <sm_stringhashmap.h>
struct ArgumentInfo {
ArgumentInfo() : name()
{ }
ArgumentInfo(std::string name, ParamInfo info) : name(name), info(info)
{ }
std::string name;
ParamInfo info;
};
class SignatureWrapper {
public:
std::string signature;
std::string address;
std::string offset;
std::vector<ArgumentInfo> args;
CallingConvention callConv;
HookType hookType;
ReturnType retType;
ThisPointerType thisType;
};
class SignatureGameConfig : public ITextListener_SMC {
public:
SignatureWrapper *GetFunctionSignature(const char *function);
public:
//ITextListener_SMC
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseStart();
private:
ReturnType GetReturnTypeFromString(const char *str);
HookParamType GetHookParamTypeFromString(const char *str);
Register_t GetCustomRegisterFromString(const char *str);
private:
StringHashMap<SignatureWrapper *> signatures_;
};
extern SignatureGameConfig *g_pSignatures;
#endif

View File

@ -0,0 +1,82 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Sample Extension
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
/**
* @file smsdk_config.h
* @brief Contains macros for configuring basic extension information.
*/
/* Basic information exposed publicly */
#define SMEXT_CONF_NAME "DHooks"
#define SMEXT_CONF_DESCRIPTION "Dynamic Hooks"
#define SMEXT_CONF_VERSION "2.2.0-detours17"
#define SMEXT_CONF_AUTHOR "Dr!fter and Peace-Maker"
#define SMEXT_CONF_URL "http://www.sourcemod.net/"
#define SMEXT_CONF_LOGTAG "DHOOKS"
#define SMEXT_CONF_LICENSE "GPL"
#define SMEXT_CONF_DATESTRING __DATE__
/**
* @brief Exposes plugin's main interface.
*/
#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
/**
* @brief Sets whether or not this plugin required Metamod.
* NOTE: Uncomment to enable, comment to disable.
*/
#define SMEXT_CONF_METAMOD
/** Enable interfaces you want to use here by uncommenting lines */
//#define SMEXT_ENABLE_FORWARDSYS
#define SMEXT_ENABLE_HANDLESYS
#define SMEXT_ENABLE_PLAYERHELPERS
//#define SMEXT_ENABLE_DBMANAGER
#define SMEXT_ENABLE_GAMECONF
//#define SMEXT_ENABLE_MEMUTILS
#define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS
//#define SMEXT_ENABLE_THREADER
//#define SMEXT_ENABLE_LIBSYS
//#define SMEXT_ENABLE_MENUS
//#define SMEXT_ENABLE_ADTFACTORY
#define SMEXT_ENABLE_PLUGINSYS
//#define SMEXT_ENABLE_ADMINSYS
//#define SMEXT_ENABLE_TEXTPARSERS
//#define SMEXT_ENABLE_USERMSGS
//#define SMEXT_ENABLE_TRANSLATOR
//#define SMEXT_ENABLE_NINVOKE
//#define SMEXT_ENABLE_ROOTCONSOLEMENU
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_

205
extensions/dhooks/util.cpp Normal file
View File

@ -0,0 +1,205 @@
#include "util.h"
void * GetObjectAddr(HookParamType type, unsigned int flags, void **params, size_t offset)
{
#ifdef WIN32
if (type == HookParamType_Object)
return (void *)((intptr_t)params + offset);
#elif POSIX
if (type == HookParamType_Object && !(flags & PASSFLAG_ODTOR)) //Objects are passed by rrefrence if they contain destructors.
return (void *)((intptr_t)params + offset);
#endif
return *(void **)((intptr_t)params + offset);
}
size_t GetStackParamOffset(HookParamsStruct *paramStruct, unsigned int index)
{
assert(paramStruct->dg->params[index].custom_register == None);
size_t offset = 0;
for (unsigned int i = 0; i < index; i++)
{
// Only care for arguments on the stack before us.
if (paramStruct->dg->params[i].custom_register != None)
continue;
#ifndef WIN32
if (paramStruct->dg->params[i].type == HookParamType_Object && (paramStruct->dg->params[i].flags & PASSFLAG_ODTOR)) //Passed by refrence
{
offset += sizeof(void *);
continue;
}
#endif
offset += paramStruct->dg->params[i].size;
}
return offset;
}
size_t GetRegisterParamOffset(HookParamsStruct *paramStruct, unsigned int index)
{
// TODO: Fix this up and get a pointer to the CDetour
assert(paramStruct->dg->params[index].custom_register != None);
// Need to get the size of the stack arguments first. Register arguments are stored after them in the buffer.
size_t stackSize = 0;
for (int i = paramStruct->dg->params.size() - 1; i >= 0; i--)
{
if (paramStruct->dg->params[i].custom_register == None)
stackSize += paramStruct->dg->params[i].size;
}
size_t offset = stackSize;
for (unsigned int i = 0; i < index; i++)
{
// Only care for arguments passed through a register as well before us.
if (paramStruct->dg->params[i].custom_register == None)
continue;
offset += paramStruct->dg->params[i].size;
}
return offset;
}
size_t GetParamOffset(HookParamsStruct *paramStruct, unsigned int index)
{
if (paramStruct->dg->params[index].custom_register == None)
return GetStackParamOffset(paramStruct, index);
else
return GetRegisterParamOffset(paramStruct, index);
}
size_t GetParamTypeSize(HookParamType type)
{
return sizeof(void *);
}
size_t GetParamsSize(DHooksCallback *dg)//Get the full size, this is for creating the STACK.
{
size_t res = 0;
for (int i = dg->params.size() - 1; i >= 0; i--)
{
res += dg->params.at(i).size;
}
return res;
}
DataType_t DynamicHooks_ConvertParamTypeFrom(HookParamType type)
{
switch (type)
{
case HookParamType_Int:
return DATA_TYPE_INT;
case HookParamType_Bool:
return DATA_TYPE_BOOL;
case HookParamType_Float:
return DATA_TYPE_FLOAT;
case HookParamType_StringPtr:
case HookParamType_CharPtr:
case HookParamType_VectorPtr:
case HookParamType_CBaseEntity:
case HookParamType_ObjectPtr:
case HookParamType_Edict:
return DATA_TYPE_POINTER;
case HookParamType_Object:
return DATA_TYPE_OBJECT;
default:
smutils->LogError(myself, "Unhandled parameter type %d!", type);
}
return DATA_TYPE_POINTER;
}
DataType_t DynamicHooks_ConvertReturnTypeFrom(ReturnType type)
{
switch (type)
{
case ReturnType_Void:
return DATA_TYPE_VOID;
case ReturnType_Int:
return DATA_TYPE_INT;
case ReturnType_Bool:
return DATA_TYPE_BOOL;
case ReturnType_Float:
return DATA_TYPE_FLOAT;
case ReturnType_StringPtr:
case ReturnType_CharPtr:
case ReturnType_VectorPtr:
case ReturnType_CBaseEntity:
case ReturnType_Edict:
return DATA_TYPE_POINTER;
case ReturnType_Vector:
return DATA_TYPE_OBJECT;
default:
smutils->LogError(myself, "Unhandled return type %d!", type);
}
return DATA_TYPE_VOID;
}
Register_t DynamicHooks_ConvertRegisterFrom(PluginRegister reg)
{
switch (reg)
{
case DHookRegister_Default:
return None;
case DHookRegister_AL:
return AL;
case DHookRegister_CL:
return CL;
case DHookRegister_DL:
return DL;
case DHookRegister_BL:
return BL;
case DHookRegister_AH:
return AH;
case DHookRegister_CH:
return CH;
case DHookRegister_DH:
return DH;
case DHookRegister_BH:
return BH;
case DHookRegister_EAX:
return EAX;
case DHookRegister_ECX:
return ECX;
case DHookRegister_EDX:
return EDX;
case DHookRegister_EBX:
return EBX;
case DHookRegister_ESP:
return ESP;
case DHookRegister_EBP:
return EBP;
case DHookRegister_ESI:
return ESI;
case DHookRegister_EDI:
return EDI;
case DHookRegister_XMM0:
return XMM0;
case DHookRegister_XMM1:
return XMM1;
case DHookRegister_XMM2:
return XMM2;
case DHookRegister_XMM3:
return XMM3;
case DHookRegister_XMM4:
return XMM4;
case DHookRegister_XMM5:
return XMM5;
case DHookRegister_XMM6:
return XMM6;
case DHookRegister_XMM7:
return XMM7;
case DHookRegister_ST0:
return ST0;
}
return None;
}

54
extensions/dhooks/util.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef _INCLUDE_UTIL_FUNCTIONS_H_
#define _INCLUDE_UTIL_FUNCTIONS_H_
#include "vhook.h"
#include "convention.h"
enum PluginRegister
{
// Don't change the register and use the default for the calling convention.
DHookRegister_Default,
// 8-bit general purpose registers
DHookRegister_AL,
DHookRegister_CL,
DHookRegister_DL,
DHookRegister_BL,
DHookRegister_AH,
DHookRegister_CH,
DHookRegister_DH,
DHookRegister_BH,
// 32-bit general purpose registers
DHookRegister_EAX,
DHookRegister_ECX,
DHookRegister_EDX,
DHookRegister_EBX,
DHookRegister_ESP,
DHookRegister_EBP,
DHookRegister_ESI,
DHookRegister_EDI,
// 128-bit XMM registers
DHookRegister_XMM0,
DHookRegister_XMM1,
DHookRegister_XMM2,
DHookRegister_XMM3,
DHookRegister_XMM4,
DHookRegister_XMM5,
DHookRegister_XMM6,
DHookRegister_XMM7,
// 80-bit FPU registers
DHookRegister_ST0
};
size_t GetParamOffset(HookParamsStruct *params, unsigned int index);
void * GetObjectAddr(HookParamType type, unsigned int flags, void **params, size_t offset);
size_t GetParamTypeSize(HookParamType type);
size_t GetParamsSize(DHooksCallback *dg);
DataType_t DynamicHooks_ConvertParamTypeFrom(HookParamType type);
DataType_t DynamicHooks_ConvertReturnTypeFrom(ReturnType type);
Register_t DynamicHooks_ConvertRegisterFrom(PluginRegister reg);
#endif

View File

@ -0,0 +1,45 @@
#include "winres.h"
#include <sourcemod_version.h>
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif
#ifndef SM_GENERATED_BUILD
#define BINARY_NAME "dhooks.ext.dll\0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION SM_VERSION_FILE
PRODUCTVERSION SM_VERSION_FILE
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "DHooks Extension"
VALUE "FileDescription", "SourceMod Dynamic Hooks Extension"
VALUE "FileVersion", SM_VERSION_STRING
VALUE "InternalName", "SourceMod DHooks Extension"
VALUE "LegalCopyright", "Copyright (c) 2021, AlliedModders LLC"
VALUE "OriginalFilename", BINARY_NAME
VALUE "ProductName", "SourceMod DHooks Extension"
VALUE "ProductVersion", SM_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END

View File

@ -0,0 +1,324 @@
#ifndef _INCLUDE_VFUNC_CALL_H_
#define _INCLUDE_VFUNC_CALL_H_
#include "vhook.h"
#include "extension.h"
#include "util.h"
#define PARAMINFO_SWITCH(passType) \
paramInfo[i].flags = dg->params.at(i).flags; \
paramInfo[i].size = dg->params.at(i).size; \
paramInfo[i].type = passType;
#define VSTK_PARAM_SWITCH(paramType) \
if(paramStruct->isChanged[i]) \
{ \
*(paramType *)vptr = *(paramType *)newAddr; \
} \
else \
{ \
*(paramType *)vptr = *(paramType *)orgAddr; \
} \
if(i + 1 != dg->params.size()) \
{ \
vptr += dg->params.at(i).size; \
} \
break;
#define VSTK_PARAM_SWITCH_OBJECT() \
memcpy(vptr, objAddr, dg->params.at(i).size); \
if(i + 1 != dg->params.size()) \
{ \
vptr += dg->params.at(i).size; \
} \
break;
template <class T>
T CallVFunction(DHooksCallback *dg, HookParamsStruct *paramStruct, void *iface)
{
SourceMod::PassInfo *paramInfo = NULL;
SourceMod::PassInfo returnInfo;
if(dg->returnType != ReturnType_Void)
{
returnInfo.flags = dg->returnFlag;
returnInfo.size = sizeof(T);
if( dg->returnType != ReturnType_Vector)
{
returnInfo.type = PassType_Basic;
}
else
{
returnInfo.type = PassType_Object;
}
}
ICallWrapper *pCall;
size_t size = GetParamsSize(dg);
unsigned char *vstk = (unsigned char *)malloc(sizeof(void *) + size);
unsigned char *vptr = vstk;
*(void **)vptr = iface;
if(paramStruct)
{
vptr += sizeof(void *);
paramInfo = (SourceMod::PassInfo *)malloc(sizeof(SourceMod::PassInfo) * dg->params.size());
for(int i = 0; i < (int)dg->params.size(); i++)
{
size_t offset = GetParamOffset(paramStruct, i);
void *orgAddr = (void **)((intptr_t)paramStruct->orgParams + offset);
void *newAddr = (void **)((intptr_t)paramStruct->newParams + offset);
switch(dg->params.at(i).type)
{
case HookParamType_Int:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(int);
case HookParamType_Bool:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(cell_t);
case HookParamType_Float:
PARAMINFO_SWITCH(PassType_Float);
VSTK_PARAM_SWITCH(float);
case HookParamType_String:
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH(string_t);
case HookParamType_StringPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(string_t *);
case HookParamType_CharPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(char *);
case HookParamType_VectorPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(SDKVector *);
case HookParamType_CBaseEntity:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(CBaseEntity *);
case HookParamType_Edict:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(edict_t *);
case HookParamType_Object:
{
void *objAddr = GetObjectAddr(HookParamType_Object, paramStruct->dg->params.at(i).flags, paramStruct->orgParams, offset);
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH_OBJECT();
}
default:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(void *);
}
}
}
T ret = 0;
if(dg->returnType == ReturnType_Void)
{
pCall = g_pBinTools->CreateVCall(dg->offset, 0, 0, NULL, paramInfo, dg->params.size());
pCall->Execute(vstk, NULL);
}
else
{
pCall = g_pBinTools->CreateVCall(dg->offset, 0, 0, &returnInfo, paramInfo, dg->params.size());
pCall->Execute(vstk, &ret);
}
pCall->Destroy();
free(vstk);
if(paramInfo != NULL)
{
free(paramInfo);
}
return ret;
}
template <>
SDKVector CallVFunction<SDKVector>(DHooksCallback *dg, HookParamsStruct *paramStruct, void *iface)
{
SourceMod::PassInfo *paramInfo = NULL;
SourceMod::PassInfo returnInfo;
if(dg->returnType != ReturnType_Void)
{
returnInfo.flags = dg->returnFlag;
returnInfo.size = sizeof(SDKVector);
returnInfo.type = PassType_Object;
}
ICallWrapper *pCall;
size_t size = GetParamsSize(dg);
unsigned char *vstk = (unsigned char *)malloc(sizeof(void *) + size);
unsigned char *vptr = vstk;
*(void **)vptr = iface;
if(paramStruct)
{
vptr += sizeof(void *);
paramInfo = (SourceMod::PassInfo *)malloc(sizeof(SourceMod::PassInfo) * dg->params.size());
for(int i = 0; i < (int)dg->params.size(); i++)
{
size_t offset = GetParamOffset(paramStruct, i);
void *orgAddr = *(void **)((intptr_t)paramStruct->orgParams + offset);
void *newAddr = *(void **)((intptr_t)paramStruct->newParams + offset);
switch (dg->params.at(i).type)
{
case HookParamType_Int:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(int);
case HookParamType_Bool:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(cell_t);
case HookParamType_Float:
PARAMINFO_SWITCH(PassType_Float);
VSTK_PARAM_SWITCH(float);
case HookParamType_String:
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH(string_t);
case HookParamType_StringPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(string_t *);
case HookParamType_CharPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(char *);
case HookParamType_VectorPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(SDKVector *);
case HookParamType_CBaseEntity:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(CBaseEntity *);
case HookParamType_Edict:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(edict_t *);
case HookParamType_Object:
{
void *objAddr = GetObjectAddr(HookParamType_Object, paramStruct->dg->params.at(i).flags, paramStruct->orgParams, offset);
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH_OBJECT();
}
default:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(void *);
}
}
}
SDKVector ret;
pCall = g_pBinTools->CreateVCall(dg->offset, 0, 0, &returnInfo, paramInfo, dg->params.size());
pCall->Execute(vstk, &ret);
pCall->Destroy();
free(vstk);
if(paramInfo != NULL)
{
free(paramInfo);
}
return ret;
}
#ifndef WIN32
template <>
string_t CallVFunction<string_t>(DHooksCallback *dg, HookParamsStruct *paramStruct, void *iface)
{
SourceMod::PassInfo *paramInfo = NULL;
SourceMod::PassInfo returnInfo;
if(dg->returnType != ReturnType_Void)
{
returnInfo.flags = dg->returnFlag;
returnInfo.size = sizeof(string_t);
returnInfo.type = PassType_Object;
}
ICallWrapper *pCall;
size_t size = GetParamsSize(dg);
unsigned char *vstk = (unsigned char *)malloc(sizeof(void *) + size);
unsigned char *vptr = vstk;
*(void **)vptr = iface;
if(paramStruct)
{
vptr += sizeof(void *);
paramInfo = (SourceMod::PassInfo *)malloc(sizeof(SourceMod::PassInfo) * dg->params.size());
for(int i = 0; i < dg->params.size(); i++)
{
size_t offset = GetParamOffset(paramStruct, i);
void *orgAddr = *(void **)((intptr_t)paramStruct->orgParams + offset);
void *newAddr = *(void **)((intptr_t)paramStruct->newParams + offset);
switch (dg->params.at(i).type)
{
case HookParamType_Int:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(int);
case HookParamType_Bool:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(cell_t);
case HookParamType_Float:
PARAMINFO_SWITCH(PassType_Float);
VSTK_PARAM_SWITCH(float);
case HookParamType_String:
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH(string_t);
case HookParamType_StringPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(string_t *);
case HookParamType_CharPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(char *);
case HookParamType_VectorPtr:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(SDKVector *);
case HookParamType_CBaseEntity:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(CBaseEntity *);
case HookParamType_Edict:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(edict_t *);
case HookParamType_Object:
{
void *objAddr = GetObjectAddr(HookParamType_Object, paramStruct->dg->params.at(i).flags, paramStruct->orgParams, offset);
PARAMINFO_SWITCH(PassType_Object);
VSTK_PARAM_SWITCH_OBJECT();
}
default:
PARAMINFO_SWITCH(PassType_Basic);
VSTK_PARAM_SWITCH(void *);
}
}
}
string_t ret;
pCall = g_pBinTools->CreateVCall(dg->offset, 0, 0, &returnInfo, paramInfo, dg->params.size());
pCall->Execute(vstk, &ret);
pCall->Destroy();
free(vstk);
if(paramInfo != NULL)
{
free(paramInfo);
}
return ret;
}
#endif
#endif

1007
extensions/dhooks/vhook.cpp Normal file

File diff suppressed because it is too large Load Diff

274
extensions/dhooks/vhook.h Normal file
View File

@ -0,0 +1,274 @@
#ifndef _INCLUDE_VHOOK_H_
#define _INCLUDE_VHOOK_H_
#include "extension.h"
#include <sourcehook.h>
#include <sh_vector.h>
#include <sourcehook_pibuilder.h>
#include <registers.h>
#include <vector>
enum CallingConvention
{
CallConv_CDECL,
CallConv_THISCALL,
CallConv_STDCALL,
CallConv_FASTCALL,
};
enum MRESReturn
{
MRES_ChangedHandled = -2, // Use changed values and return MRES_Handled
MRES_ChangedOverride, // Use changed values and return MRES_Override
MRES_Ignored, // plugin didn't take any action
MRES_Handled, // plugin did something, but real function should still be called
MRES_Override, // call real function, but use my return value
MRES_Supercede // skip real function; use my return value
};
enum ObjectValueType
{
ObjectValueType_Int = 0,
ObjectValueType_Bool,
ObjectValueType_Ehandle,
ObjectValueType_Float,
ObjectValueType_CBaseEntityPtr,
ObjectValueType_IntPtr,
ObjectValueType_BoolPtr,
ObjectValueType_EhandlePtr,
ObjectValueType_FloatPtr,
ObjectValueType_Vector,
ObjectValueType_VectorPtr,
ObjectValueType_CharPtr,
ObjectValueType_String
};
enum HookParamType
{
HookParamType_Unknown,
HookParamType_Int,
HookParamType_Bool,
HookParamType_Float,
HookParamType_String,
HookParamType_StringPtr,
HookParamType_CharPtr,
HookParamType_VectorPtr,
HookParamType_CBaseEntity,
HookParamType_ObjectPtr,
HookParamType_Edict,
HookParamType_Object
};
enum ReturnType
{
ReturnType_Unknown,
ReturnType_Void,
ReturnType_Int,
ReturnType_Bool,
ReturnType_Float,
ReturnType_String,
ReturnType_StringPtr,
ReturnType_CharPtr,
ReturnType_Vector,
ReturnType_VectorPtr,
ReturnType_CBaseEntity,
ReturnType_Edict
};
enum ThisPointerType
{
ThisPointer_Ignore,
ThisPointer_CBaseEntity,
ThisPointer_Address
};
enum HookType
{
HookType_Entity,
HookType_GameRules,
HookType_Raw
};
struct ParamInfo
{
HookParamType type;
size_t size;
unsigned int flags;
SourceHook::PassInfo::PassType pass_type;
Register_t custom_register;
};
#ifdef WIN32
#define OBJECT_OFFSET sizeof(void *)
#else
#define OBJECT_OFFSET (sizeof(void *)*2)
#endif
class HookReturnStruct
{
public:
~HookReturnStruct();
public:
ReturnType type;
bool isChanged;
void *orgResult;
void *newResult;
};
class DHooksInfo
{
public:
SourceHook::CVector<ParamInfo> params;
int offset;
unsigned int returnFlag;
ReturnType returnType;
bool post;
IPluginFunction *plugin_callback;
int entity;
ThisPointerType thisType;
HookType hookType;
};
class DHooksCallback : public SourceHook::ISHDelegate, public DHooksInfo
{
public:
virtual bool IsEqual(ISHDelegate *pOtherDeleg){return false;};
virtual void DeleteThis()
{
*(void ***)this = this->oldvtable;
g_pSM->GetScriptingEngine()->FreePageMemory(this->newvtable[2]);
delete this->newvtable;
delete this;
};
virtual void Call() {};
public:
void **newvtable;
void **oldvtable;
};
#ifdef WIN32
void *Callback(DHooksCallback *dg, void **stack, size_t *argsizep);
float Callback_float(DHooksCallback *dg, void **stack, size_t *argsizep);
SDKVector *Callback_vector(DHooksCallback *dg, void **stack, size_t *argsizep);
#else
void *Callback(DHooksCallback *dg, void **stack);
float Callback_float(DHooksCallback *dg, void **stack);
SDKVector *Callback_vector(DHooksCallback *dg, void **stack);
string_t *Callback_stringt(DHooksCallback *dg, void **stack);
#endif
bool SetupHookManager(ISmmAPI *ismm);
void CleanupHooks(IPluginContext *pContext = NULL);
size_t GetParamTypeSize(HookParamType type);
SourceHook::PassInfo::PassType GetParamTypePassType(HookParamType type);
void *GenerateThunk(ReturnType type);
static DHooksCallback *MakeHandler(ReturnType type)
{
DHooksCallback *dg = new DHooksCallback();
dg->returnType = type;
dg->oldvtable = *(void ***)dg;
dg->newvtable = new void *[3];
dg->newvtable[0] = dg->oldvtable[0];
dg->newvtable[1] = dg->oldvtable[1];
dg->newvtable[2] = GenerateThunk(type);
*(void ***)dg = dg->newvtable;
return dg;
}
class HookParamsStruct
{
public:
HookParamsStruct()
{
this->orgParams = NULL;
this->newParams = NULL;
this->dg = NULL;
this->isChanged = NULL;
}
~HookParamsStruct();
public:
void **orgParams;
void **newParams;
bool *isChanged;
DHooksInfo *dg;
};
class HookSetup
{
public:
HookSetup(ReturnType returnType, unsigned int returnFlag, HookType hookType, ThisPointerType thisType, int offset, IPluginFunction *callback)
{
this->returnType = returnType;
this->returnFlag = returnFlag;
this->hookType = hookType;
this->callConv = CallConv_THISCALL;
this->thisType = thisType;
this->offset = offset;
this->funcAddr = nullptr;
this->callback = callback;
};
HookSetup(ReturnType returnType, unsigned int returnFlag, CallingConvention callConv, ThisPointerType thisType, void *funcAddr)
{
this->returnType = returnType;
this->returnFlag = returnFlag;
this->hookType = HookType_Raw;
this->callConv = callConv;
this->thisType = thisType;
this->offset = -1;
this->funcAddr = funcAddr;
this->callback = nullptr;
};
~HookSetup(){};
bool IsVirtual()
{
return this->offset != -1;
}
public:
unsigned int returnFlag;
ReturnType returnType;
HookType hookType;
CallingConvention callConv;
ThisPointerType thisType;
SourceHook::CVector<ParamInfo> params;
int offset;
void *funcAddr;
IPluginFunction *callback;
};
class DHooksManager
{
public:
DHooksManager(HookSetup *setup, void *iface, IPluginFunction *remove_callback, IPluginFunction *plugincb, bool post);
~DHooksManager()
{
if(this->hookid)
{
g_SHPtr->RemoveHookByID(this->hookid);
if(this->remove_callback)
{
this->remove_callback->PushCell(this->hookid);
this->remove_callback->Execute(NULL);
}
if(this->pManager)
{
g_pHookManager->ReleaseHookMan(this->pManager);
}
}
}
public:
intptr_t addr;
int hookid;
DHooksCallback *callback;
IPluginFunction *remove_callback;
SourceHook::HookManagerPubFunc pManager;
};
size_t GetStackArgsSize(DHooksCallback *dg);
cell_t GetThisPtr(void *iface, ThisPointerType type);
extern IBinTools *g_pBinTools;
extern HandleType_t g_HookParamsHandle;
extern HandleType_t g_HookReturnHandle;
#endif

1067
plugins/include/dhooks.inc Normal file

File diff suppressed because it is too large Load Diff