Import DHooks + Dynamic Detouring
This is the latest DHooks version from 1314f2d1b4
This commit is contained in:
parent
18f9d65938
commit
0f5f1a814e
@ -804,6 +804,7 @@ else:
|
|||||||
'extensions/clientprefs/AMBuilder',
|
'extensions/clientprefs/AMBuilder',
|
||||||
'extensions/curl/AMBuilder',
|
'extensions/curl/AMBuilder',
|
||||||
'extensions/cstrike/AMBuilder',
|
'extensions/cstrike/AMBuilder',
|
||||||
|
'extensions/dhooks/AMBuilder',
|
||||||
'extensions/geoip/AMBuilder',
|
'extensions/geoip/AMBuilder',
|
||||||
'extensions/mysql/AMBuilder',
|
'extensions/mysql/AMBuilder',
|
||||||
'extensions/pgsql/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