Import DHooks + Dynamic Detouring
This is the latest DHooks version from 1314f2d1b4
This commit is contained in:
parent
45e9da90fc
commit
3c0f700a6b
@ -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',
|
||||
|
63
extensions/dhooks/AMBuilder
Normal file
63
extensions/dhooks/AMBuilder
Normal 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)]
|
290
extensions/dhooks/DynamicHooks/convention.h
Normal file
290
extensions/dhooks/DynamicHooks/convention.h
Normal 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
|
48
extensions/dhooks/DynamicHooks/conventions/x86GccCdecl.h
Normal file
48
extensions/dhooks/DynamicHooks/conventions/x86GccCdecl.h
Normal 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
|
@ -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));
|
||||
}
|
59
extensions/dhooks/DynamicHooks/conventions/x86GccThiscall.h
Normal file
59
extensions/dhooks/DynamicHooks/conventions/x86GccThiscall.h
Normal 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
|
187
extensions/dhooks/DynamicHooks/conventions/x86MsCdecl.cpp
Normal file
187
extensions/dhooks/DynamicHooks/conventions/x86MsCdecl.cpp
Normal 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);
|
||||
}
|
||||
}
|
88
extensions/dhooks/DynamicHooks/conventions/x86MsCdecl.h
Normal file
88
extensions/dhooks/DynamicHooks/conventions/x86MsCdecl.h
Normal 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
|
61
extensions/dhooks/DynamicHooks/conventions/x86MsFastcall.cpp
Normal file
61
extensions/dhooks/DynamicHooks/conventions/x86MsFastcall.cpp
Normal 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;
|
||||
}
|
||||
}
|
72
extensions/dhooks/DynamicHooks/conventions/x86MsFastcall.h
Normal file
72
extensions/dhooks/DynamicHooks/conventions/x86MsFastcall.h
Normal 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
|
194
extensions/dhooks/DynamicHooks/conventions/x86MsStdcall.cpp
Normal file
194
extensions/dhooks/DynamicHooks/conventions/x86MsStdcall.cpp
Normal 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);
|
||||
}
|
||||
}
|
88
extensions/dhooks/DynamicHooks/conventions/x86MsStdcall.h
Normal file
88
extensions/dhooks/DynamicHooks/conventions/x86MsStdcall.h
Normal 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
|
239
extensions/dhooks/DynamicHooks/conventions/x86MsThiscall.cpp
Normal file
239
extensions/dhooks/DynamicHooks/conventions/x86MsThiscall.cpp
Normal 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();
|
||||
}
|
93
extensions/dhooks/DynamicHooks/conventions/x86MsThiscall.h
Normal file
93
extensions/dhooks/DynamicHooks/conventions/x86MsThiscall.h
Normal 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
|
503
extensions/dhooks/DynamicHooks/hook.cpp
Normal file
503
extensions/dhooks/DynamicHooks/hook.cpp
Normal file
@ -0,0 +1,503 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "hook.h"
|
||||
#include "utilities.h"
|
||||
#include <asm/asm.h>
|
||||
#include <macro-assembler-x86.h>
|
||||
#include "extension.h"
|
||||
|
||||
using namespace sp;
|
||||
|
||||
// ============================================================================
|
||||
// >> DEFINITIONS
|
||||
// ============================================================================
|
||||
#define JMP_SIZE 6
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CHook
|
||||
// ============================================================================
|
||||
CHook::CHook(void* pFunc, ICallingConvention* pConvention)
|
||||
{
|
||||
m_pFunc = pFunc;
|
||||
m_pRegisters = new CRegisters(pConvention->GetRegisters());
|
||||
m_pCallingConvention = pConvention;
|
||||
|
||||
if (!m_hookHandler.init())
|
||||
return;
|
||||
|
||||
if (!m_RetAddr.init())
|
||||
return;
|
||||
|
||||
unsigned char* pTarget = (unsigned char *) pFunc;
|
||||
|
||||
// Determine the number of bytes we need to copy
|
||||
int iBytesToCopy = copy_bytes(pTarget, NULL, JMP_SIZE);
|
||||
|
||||
// Create a buffer for the bytes to copy + a jump to the rest of the
|
||||
// function.
|
||||
unsigned char* pCopiedBytes = (unsigned char *) smutils->GetScriptingEngine()->AllocatePageMemory(iBytesToCopy + JMP_SIZE);
|
||||
|
||||
// Fill the array with NOP instructions
|
||||
memset(pCopiedBytes, 0x90, iBytesToCopy + JMP_SIZE);
|
||||
|
||||
// Copy the required bytes to our array
|
||||
copy_bytes(pTarget, pCopiedBytes, JMP_SIZE);
|
||||
|
||||
// Write a jump after the copied bytes to the function/bridge + number of bytes to copy
|
||||
WriteJMP(pCopiedBytes + iBytesToCopy, pTarget + iBytesToCopy);
|
||||
|
||||
// Save the trampoline
|
||||
m_pTrampoline = (void *) pCopiedBytes;
|
||||
|
||||
// Create the bridge function
|
||||
m_pBridge = CreateBridge();
|
||||
|
||||
// Write a jump to the bridge
|
||||
WriteJMP((unsigned char *) pFunc, m_pBridge);
|
||||
}
|
||||
|
||||
CHook::~CHook()
|
||||
{
|
||||
// Copy back the previously copied bytes
|
||||
copy_bytes((unsigned char *) m_pTrampoline, (unsigned char *) m_pFunc, JMP_SIZE);
|
||||
|
||||
// Free the trampoline buffer
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pTrampoline);
|
||||
|
||||
// Free the asm bridge and new return address
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge);
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr);
|
||||
|
||||
delete m_pRegisters;
|
||||
delete m_pCallingConvention;
|
||||
}
|
||||
|
||||
void CHook::AddCallback(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
if (!pCallback)
|
||||
return;
|
||||
|
||||
HookTypeMap::Insert i = m_hookHandler.findForAdd(eHookType);
|
||||
if (!i.found()) {
|
||||
HookHandlerSet set;
|
||||
set.init();
|
||||
m_hookHandler.add(i, eHookType, std::move(set));
|
||||
}
|
||||
|
||||
i->value.add(pCallback);
|
||||
}
|
||||
|
||||
void CHook::RemoveCallback(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
return;
|
||||
|
||||
r->value.removeIfExists(pCallback);
|
||||
}
|
||||
|
||||
bool CHook::IsCallbackRegistered(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
return false;
|
||||
|
||||
return r->value.has(pCallback);
|
||||
}
|
||||
|
||||
bool CHook::AreCallbacksRegistered()
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(HOOKTYPE_PRE);
|
||||
if (r.found() && r->value.elements() > 0)
|
||||
return true;
|
||||
|
||||
r = m_hookHandler.find(HOOKTYPE_POST);
|
||||
if (r.found() && r->value.elements() > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ReturnAction_t CHook::HookHandler(HookType_t eHookType)
|
||||
{
|
||||
if (eHookType == HOOKTYPE_POST)
|
||||
{
|
||||
ReturnAction_t lastPreReturnAction = m_LastPreReturnAction.back();
|
||||
m_LastPreReturnAction.pop_back();
|
||||
if (lastPreReturnAction == ReturnAction_Override)
|
||||
m_pCallingConvention->RestoreReturnValue(m_pRegisters);
|
||||
if (lastPreReturnAction < ReturnAction_Supercede)
|
||||
m_pCallingConvention->RestoreCallArguments(m_pRegisters);
|
||||
}
|
||||
|
||||
ReturnAction_t returnAction = ReturnAction_Ignored;
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
{
|
||||
// Still save the arguments for the post hook even if there
|
||||
// is no pre-handler registered.
|
||||
if (eHookType == HOOKTYPE_PRE)
|
||||
{
|
||||
m_LastPreReturnAction.push_back(returnAction);
|
||||
m_pCallingConvention->SaveCallArguments(m_pRegisters);
|
||||
}
|
||||
return returnAction;
|
||||
}
|
||||
|
||||
HookHandlerSet &callbacks = r->value;
|
||||
for(HookHandlerSet::iterator it=callbacks.iter(); !it.empty(); it.next())
|
||||
{
|
||||
ReturnAction_t result = ((HookHandlerFn) *it)(eHookType, this);
|
||||
if (result > returnAction)
|
||||
returnAction = result;
|
||||
}
|
||||
|
||||
if (eHookType == HOOKTYPE_PRE)
|
||||
{
|
||||
m_LastPreReturnAction.push_back(returnAction);
|
||||
if (returnAction == ReturnAction_Override)
|
||||
m_pCallingConvention->SaveReturnValue(m_pRegisters);
|
||||
if (returnAction < ReturnAction_Supercede)
|
||||
m_pCallingConvention->SaveCallArguments(m_pRegisters);
|
||||
}
|
||||
|
||||
return returnAction;
|
||||
}
|
||||
|
||||
void* __cdecl CHook::GetReturnAddress(void* pESP)
|
||||
{
|
||||
ReturnAddressMap::Result r = m_RetAddr.find(pESP);
|
||||
assert(r.found());
|
||||
if (!r.found())
|
||||
{
|
||||
smutils->LogError(myself, "FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pRetAddr = r->value.back();
|
||||
r->value.pop_back();
|
||||
|
||||
// Clear the stack address from the cache now that we ran the last post hook.
|
||||
if (r->value.empty())
|
||||
m_RetAddr.remove(r);
|
||||
|
||||
return pRetAddr;
|
||||
}
|
||||
|
||||
void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
|
||||
{
|
||||
ReturnAddressMap::Insert i = m_RetAddr.findForAdd(pESP);
|
||||
if (!i.found())
|
||||
m_RetAddr.add(i, pESP, std::vector<void *>());
|
||||
|
||||
i->value.push_back(pRetAddr);
|
||||
}
|
||||
|
||||
void* CHook::CreateBridge()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
Label label_supercede;
|
||||
|
||||
// Write a redirect to the post-hook code
|
||||
Write_ModifyReturnAddress(masm);
|
||||
|
||||
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
|
||||
Write_CallHandler(masm, HOOKTYPE_PRE);
|
||||
masm.cmpb(r8_al, ReturnAction_Supercede);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(masm, HOOKTYPE_PRE);
|
||||
|
||||
masm.j(equal, &label_supercede);
|
||||
|
||||
// Jump to the trampoline
|
||||
masm.jmp(ExternalAddress(m_pTrampoline));
|
||||
|
||||
// This code will be executed if a pre-hook returns ReturnAction_Supercede
|
||||
masm.bind(&label_supercede);
|
||||
|
||||
// Finally, return to the caller
|
||||
// This will still call post hooks, but will skip the original function.
|
||||
masm.ret(m_pCallingConvention->GetPopSize());
|
||||
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
|
||||
{
|
||||
// Save scratch registers that are used by SetReturnAddress
|
||||
static void* pEAX = NULL;
|
||||
static void* pECX = NULL;
|
||||
static void* pEDX = NULL;
|
||||
|
||||
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
|
||||
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
|
||||
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
|
||||
|
||||
// Store the return address in eax
|
||||
masm.movl(eax, Operand(esp, 0));
|
||||
|
||||
// Save the original return address by using the current esp as the key.
|
||||
// This should be unique until we have returned to the original caller.
|
||||
void (__cdecl CHook::*SetReturnAddress)(void*, void*) = &CHook::SetReturnAddress;
|
||||
masm.push(esp);
|
||||
masm.push(eax);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)SetReturnAddress));
|
||||
masm.addl(esp, 12);
|
||||
|
||||
// Restore scratch registers
|
||||
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
|
||||
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
|
||||
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
|
||||
|
||||
// Override the return address. This is a redirect to our post-hook code
|
||||
m_pNewRetAddr = CreatePostCallback();
|
||||
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
|
||||
}
|
||||
|
||||
void* CHook::CreatePostCallback()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
|
||||
int iPopSize = m_pCallingConvention->GetPopSize();
|
||||
|
||||
// Subtract the previously added bytes (stack size + return address), so
|
||||
// that we can access the arguments again
|
||||
masm.subl(esp, iPopSize+4);
|
||||
|
||||
// Call the post-hook handler
|
||||
Write_CallHandler(masm, HOOKTYPE_POST);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(masm, HOOKTYPE_POST);
|
||||
|
||||
// Save scratch registers that are used by GetReturnAddress
|
||||
static void* pEAX = NULL;
|
||||
static void* pECX = NULL;
|
||||
static void* pEDX = NULL;
|
||||
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
|
||||
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
|
||||
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
|
||||
|
||||
// Get the original return address
|
||||
void* (__cdecl CHook::*GetReturnAddress)(void*) = &CHook::GetReturnAddress;
|
||||
masm.push(esp);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)GetReturnAddress));
|
||||
masm.addl(esp, 8);
|
||||
|
||||
// Save the original return address
|
||||
static void* pRetAddr = NULL;
|
||||
masm.movl(Operand(ExternalAddress(&pRetAddr)), eax);
|
||||
|
||||
// Restore scratch registers
|
||||
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
|
||||
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
|
||||
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
|
||||
|
||||
// Add the bytes again to the stack (stack size + return address), so we
|
||||
// don't corrupt the stack.
|
||||
masm.addl(esp, iPopSize+4);
|
||||
|
||||
// Jump to the original return address
|
||||
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
|
||||
|
||||
// Generate the code
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t) = &CHook::HookHandler;
|
||||
|
||||
// Save the registers so that we can access them in our handlers
|
||||
Write_SaveRegisters(masm, type);
|
||||
|
||||
// Align the stack to 16 bytes.
|
||||
masm.subl(esp, 8);
|
||||
|
||||
// Call the global hook handler
|
||||
masm.push(type);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)HookHandler));
|
||||
masm.addl(esp, 16);
|
||||
}
|
||||
|
||||
void CHook::Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
|
||||
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
|
||||
{
|
||||
switch(vecRegistersToSave[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
case AL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress)), r8_al); break;
|
||||
case CL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress)), r8_cl); break;
|
||||
case DL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress)), r8_dl); break;
|
||||
case BL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress)), r8_bl); break;
|
||||
case AH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress)), r8_ah); break;
|
||||
case CH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress)), r8_ch); break;
|
||||
case DH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress)), r8_dh); break;
|
||||
case BH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress)), r8_bh); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
case EAX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress)), eax); break;
|
||||
case ECX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress)), ecx); break;
|
||||
case EDX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress)), edx); break;
|
||||
case EBX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress)), ebx); break;
|
||||
case ESP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress)), esp); break;
|
||||
case EBP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress)), ebp); break;
|
||||
case ESI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress)), esi); break;
|
||||
case EDI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress)), edi); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
// TODO: Also provide movups?
|
||||
case XMM0: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress)), xmm0); break;
|
||||
case XMM1: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress)), xmm1); break;
|
||||
case XMM2: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress)), xmm2); break;
|
||||
case XMM3: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress)), xmm3); break;
|
||||
case XMM4: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress)), xmm4); break;
|
||||
case XMM5: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress)), xmm5); break;
|
||||
case XMM6: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress)), xmm6); break;
|
||||
case XMM7: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress)), xmm7); break;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
case ST0:
|
||||
// Don't mess with the FPU stack in a pre-hook. The float return is returned in st0,
|
||||
// so only load it in a post hook to avoid writing back NaN.
|
||||
if (type == HOOKTYPE_POST)
|
||||
masm.fst32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
|
||||
break;
|
||||
//case ST1: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress)), st1); break;
|
||||
//case ST2: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress)), st2); break;
|
||||
//case ST3: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress)), st3); break;
|
||||
//case ST4: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress)), st4); break;
|
||||
//case ST5: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress)), st5); break;
|
||||
//case ST6: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress)), st6); break;
|
||||
//case ST7: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress)), st7); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
|
||||
for (size_t i = 0; i < vecRegistersToSave.size(); i++)
|
||||
{
|
||||
switch (vecRegistersToSave[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
case AL: masm.movl(r8_al, Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress))); break;
|
||||
case CL: masm.movl(r8_cl, Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress))); break;
|
||||
case DL: masm.movl(r8_dl, Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress))); break;
|
||||
case BL: masm.movl(r8_bl, Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress))); break;
|
||||
case AH: masm.movl(r8_ah, Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress))); break;
|
||||
case CH: masm.movl(r8_ch, Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress))); break;
|
||||
case DH: masm.movl(r8_dh, Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress))); break;
|
||||
case BH: masm.movl(r8_bh, Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress))); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
case EAX: masm.movl(eax, Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress))); break;
|
||||
case ECX: masm.movl(ecx, Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress))); break;
|
||||
case EDX: masm.movl(edx, Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress))); break;
|
||||
case EBX: masm.movl(ebx, Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress))); break;
|
||||
case ESP: masm.movl(esp, Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress))); break;
|
||||
case EBP: masm.movl(ebp, Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress))); break;
|
||||
case ESI: masm.movl(esi, Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress))); break;
|
||||
case EDI: masm.movl(edi, Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress))); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
// TODO: Also provide movups?
|
||||
case XMM0: masm.movaps(xmm0, Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress))); break;
|
||||
case XMM1: masm.movaps(xmm1, Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress))); break;
|
||||
case XMM2: masm.movaps(xmm2, Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress))); break;
|
||||
case XMM3: masm.movaps(xmm3, Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress))); break;
|
||||
case XMM4: masm.movaps(xmm4, Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress))); break;
|
||||
case XMM5: masm.movaps(xmm5, Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress))); break;
|
||||
case XMM6: masm.movaps(xmm6, Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress))); break;
|
||||
case XMM7: masm.movaps(xmm7, Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress))); break;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
case ST0:
|
||||
if (type == HOOKTYPE_POST) {
|
||||
// Replace the top of the FPU stack.
|
||||
// Copy st0 to st0 and pop -> just pop the FPU stack.
|
||||
masm.fstp(st0);
|
||||
// Push a value to the FPU stack.
|
||||
// TODO: Only write back when changed? Save full 80bits for that case.
|
||||
// Avoid truncation of the data if it's unchanged.
|
||||
masm.fld32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
|
||||
}
|
||||
break;
|
||||
//case ST1: masm.movl(st1, tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress))); break;
|
||||
//case ST2: masm.movl(st2, tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress))); break;
|
||||
//case ST3: masm.movl(st3, tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress))); break;
|
||||
//case ST4: masm.movl(st4, tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress))); break;
|
||||
//case ST5: masm.movl(st5, tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress))); break;
|
||||
//case ST6: masm.movl(st6, tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress))); break;
|
||||
//case ST7: masm.movl(st7, tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress))); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
}
|
217
extensions/dhooks/DynamicHooks/hook.h
Normal file
217
extensions/dhooks/DynamicHooks/hook.h
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _HOOK_H
|
||||
#define _HOOK_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "registers.h"
|
||||
#include "convention.h"
|
||||
#include <am-hashmap.h>
|
||||
#include <am-hashset.h>
|
||||
|
||||
// ============================================================================
|
||||
// >> HookType_t
|
||||
// ============================================================================
|
||||
enum HookType_t
|
||||
{
|
||||
// Callback will be executed before the original function.
|
||||
HOOKTYPE_PRE,
|
||||
|
||||
// Callback will be executed after the original function.
|
||||
HOOKTYPE_POST
|
||||
};
|
||||
|
||||
enum ReturnAction_t
|
||||
{
|
||||
ReturnAction_Ignored, // handler didn't take any action
|
||||
ReturnAction_Handled, // plugin did something, but real function should still be called
|
||||
ReturnAction_Override, // call real function, but use my return value
|
||||
ReturnAction_Supercede // skip real function; use my return value
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> TYPEDEFS
|
||||
// ============================================================================
|
||||
class CHook;
|
||||
typedef ReturnAction_t (*HookHandlerFn)(HookType_t, CHook*);
|
||||
|
||||
#ifdef __linux__
|
||||
#define __cdecl
|
||||
#endif
|
||||
|
||||
struct IntegerPolicy
|
||||
{
|
||||
static inline uint32_t hash(size_t i) {
|
||||
return ke::HashInteger<sizeof(size_t)>(i);
|
||||
}
|
||||
static inline bool matches(size_t i1, size_t i2) {
|
||||
return i1 == i2;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ke::HashSet<HookHandlerFn*, ke::PointerPolicy<HookHandlerFn>> HookHandlerSet;
|
||||
typedef ke::HashMap<HookType_t, HookHandlerSet, IntegerPolicy> HookTypeMap;
|
||||
typedef ke::HashMap<void*, std::vector<void*>, ke::PointerPolicy<void>> ReturnAddressMap;
|
||||
|
||||
namespace sp
|
||||
{
|
||||
class MacroAssembler;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
|
||||
class CHook
|
||||
{
|
||||
private:
|
||||
friend class CHookManager;
|
||||
|
||||
/*
|
||||
Creates a new function hook.
|
||||
|
||||
@param <pFunc>:
|
||||
The address of the function to hook
|
||||
|
||||
@param <pConvention>:
|
||||
The calling convention of <pFunc>.
|
||||
*/
|
||||
CHook(void* pFunc, ICallingConvention* pConvention);
|
||||
~CHook();
|
||||
|
||||
public:
|
||||
/*
|
||||
Adds a hook handler to the hook.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be added.
|
||||
*/
|
||||
void AddCallback(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Removes a hook handler to the hook.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be removed.
|
||||
*/
|
||||
void RemoveCallback(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Checks if a hook handler is already added.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be checked.
|
||||
*/
|
||||
bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Checks if there are any hook handlers added to this hook.
|
||||
*/
|
||||
bool AreCallbacksRegistered();
|
||||
|
||||
template<class T>
|
||||
T GetArgument(int iIndex)
|
||||
{
|
||||
return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetArgument(int iIndex, T value)
|
||||
{
|
||||
void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
|
||||
*(T *) pPtr = value;
|
||||
m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T GetReturnValue()
|
||||
{
|
||||
return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetReturnValue(T value)
|
||||
{
|
||||
void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters);
|
||||
*(T *) pPtr = value;
|
||||
m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr);
|
||||
}
|
||||
|
||||
private:
|
||||
void* CreateBridge();
|
||||
|
||||
void Write_ModifyReturnAddress(sp::MacroAssembler& masm);
|
||||
void Write_CallHandler(sp::MacroAssembler& masm, HookType_t type);
|
||||
void Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type);
|
||||
void Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type);
|
||||
|
||||
void* CreatePostCallback();
|
||||
|
||||
ReturnAction_t __cdecl HookHandler(HookType_t type);
|
||||
|
||||
void* __cdecl GetReturnAddress(void* pESP);
|
||||
void __cdecl SetReturnAddress(void* pRetAddr, void* pESP);
|
||||
|
||||
public:
|
||||
|
||||
HookTypeMap m_hookHandler;
|
||||
|
||||
// Address of the original function
|
||||
void* m_pFunc;
|
||||
|
||||
ICallingConvention* m_pCallingConvention;
|
||||
|
||||
// Address of the bridge
|
||||
void* m_pBridge;
|
||||
|
||||
// Address of the trampoline
|
||||
void* m_pTrampoline;
|
||||
|
||||
// Register storage
|
||||
CRegisters* m_pRegisters;
|
||||
|
||||
// New return address
|
||||
void* m_pNewRetAddr;
|
||||
|
||||
ReturnAddressMap m_RetAddr;
|
||||
|
||||
// Save the last return action of the pre HookHandler for use in the post handler.
|
||||
std::vector<ReturnAction_t> m_LastPreReturnAction;
|
||||
};
|
||||
|
||||
#endif // _HOOK_H
|
107
extensions/dhooks/DynamicHooks/manager.cpp
Normal file
107
extensions/dhooks/DynamicHooks/manager.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "manager.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CHookManager
|
||||
// ============================================================================
|
||||
CHook* CHookManager::HookFunction(void* pFunc, ICallingConvention* pConvention)
|
||||
{
|
||||
if (!pFunc)
|
||||
return NULL;
|
||||
|
||||
CHook* pHook = FindHook(pFunc);
|
||||
if (pHook)
|
||||
{
|
||||
delete pConvention;
|
||||
return pHook;
|
||||
}
|
||||
|
||||
pHook = new CHook(pFunc, pConvention);
|
||||
m_Hooks.push_back(pHook);
|
||||
return pHook;
|
||||
}
|
||||
|
||||
void CHookManager::UnhookFunction(void* pFunc)
|
||||
{
|
||||
if (!pFunc)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
||||
{
|
||||
CHook* pHook = m_Hooks[i];
|
||||
if (pHook->m_pFunc == pFunc)
|
||||
{
|
||||
m_Hooks.erase(m_Hooks.begin() + i);
|
||||
delete pHook;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CHook* CHookManager::FindHook(void* pFunc)
|
||||
{
|
||||
if (!pFunc)
|
||||
return NULL;
|
||||
|
||||
for(size_t i = 0; i < m_Hooks.size(); i++)
|
||||
{
|
||||
CHook* pHook = m_Hooks[i];
|
||||
if (pHook->m_pFunc == pFunc)
|
||||
return pHook;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CHookManager::UnhookAllFunctions()
|
||||
{
|
||||
for(size_t i = 0; i < m_Hooks.size(); i++)
|
||||
delete m_Hooks[i];
|
||||
|
||||
m_Hooks.clear();
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> GetHookManager
|
||||
// ============================================================================
|
||||
CHookManager* GetHookManager()
|
||||
{
|
||||
static CHookManager* s_pManager = new CHookManager;
|
||||
return s_pManager;
|
||||
}
|
86
extensions/dhooks/DynamicHooks/manager.h
Normal file
86
extensions/dhooks/DynamicHooks/manager.h
Normal 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
|
509
extensions/dhooks/DynamicHooks/registers.cpp
Normal file
509
extensions/dhooks/DynamicHooks/registers.cpp
Normal 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;
|
||||
}
|
||||
}
|
462
extensions/dhooks/DynamicHooks/registers.h
Normal file
462
extensions/dhooks/DynamicHooks/registers.h
Normal 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
|
73
extensions/dhooks/DynamicHooks/utilities.cpp
Normal file
73
extensions/dhooks/DynamicHooks/utilities.cpp
Normal 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);
|
||||
}
|
41
extensions/dhooks/DynamicHooks/utilities.h
Normal file
41
extensions/dhooks/DynamicHooks/utilities.h
Normal 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
|
633
extensions/dhooks/dynhooks_sourcepawn.cpp
Normal file
633
extensions/dhooks/dynhooks_sourcepawn.cpp
Normal 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;
|
||||
}
|
||||
}
|
42
extensions/dhooks/dynhooks_sourcepawn.h
Normal file
42
extensions/dhooks/dynhooks_sourcepawn.h
Normal 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
|
165
extensions/dhooks/extension.cpp
Normal file
165
extensions/dhooks/extension.cpp
Normal 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;
|
||||
}
|
||||
}
|
134
extensions/dhooks/extension.h
Normal file
134
extensions/dhooks/extension.h
Normal 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_
|
138
extensions/dhooks/listeners.cpp
Normal file
138
extensions/dhooks/listeners.cpp
Normal 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;
|
||||
}
|
32
extensions/dhooks/listeners.h
Normal file
32
extensions/dhooks/listeners.h
Normal 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
|
1513
extensions/dhooks/natives.cpp
Normal file
1513
extensions/dhooks/natives.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
extensions/dhooks/natives.h
Normal file
14
extensions/dhooks/natives.h
Normal 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
|
75
extensions/dhooks/sdk-hacks.h
Normal file
75
extensions/dhooks/sdk-hacks.h
Normal 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
|
574
extensions/dhooks/signatures.cpp
Normal file
574
extensions/dhooks/signatures.cpp
Normal 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;
|
||||
}
|
53
extensions/dhooks/signatures.h
Normal file
53
extensions/dhooks/signatures.h
Normal 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
|
82
extensions/dhooks/smsdk_config.h
Normal file
82
extensions/dhooks/smsdk_config.h
Normal 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
205
extensions/dhooks/util.cpp
Normal 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
54
extensions/dhooks/util.h
Normal 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
|
45
extensions/dhooks/version.rc
Normal file
45
extensions/dhooks/version.rc
Normal 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
|
324
extensions/dhooks/vfunc_call.h
Normal file
324
extensions/dhooks/vfunc_call.h
Normal 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
1007
extensions/dhooks/vhook.cpp
Normal file
File diff suppressed because it is too large
Load Diff
274
extensions/dhooks/vhook.h
Normal file
274
extensions/dhooks/vhook.h
Normal 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
1067
plugins/include/dhooks.inc
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user