mirror of
https://github.com/alliedmodders/sourcemod.git
synced 2025-12-06 18:08:36 +00:00
Add x86_64 detour support (windows)
This commit is contained in:
parent
88ae9d6e26
commit
a019747b42
@ -43,7 +43,9 @@ for cxx in builder.targets:
|
||||
'dynhooks_sourcepawn.cpp',
|
||||
'../../public/smsdk_ext.cpp',
|
||||
# Dynamic Hooks
|
||||
os.path.join('DynamicHooks', 'registers.cpp')
|
||||
os.path.join('DynamicHooks', 'registers.cpp'),
|
||||
os.path.join('DynamicHooks', 'hook.cpp'),
|
||||
os.path.join('DynamicHooks', 'manager.cpp')
|
||||
]
|
||||
SM.AddCDetour(binary)
|
||||
|
||||
@ -56,8 +58,6 @@ for cxx in builder.targets:
|
||||
]
|
||||
# DynamicHooks
|
||||
binary.sources += [
|
||||
os.path.join('DynamicHooks', 'hook.cpp'),
|
||||
os.path.join('DynamicHooks', 'manager.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsCdecl.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsStdcall.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsFastcall.cpp'),
|
||||
@ -72,6 +72,16 @@ for cxx in builder.targets:
|
||||
binary.compiler.defines += ['DHOOKS_DYNAMIC_DETOUR']
|
||||
|
||||
elif binary.compiler.target.arch == 'x86_64':
|
||||
binary.compiler.defines += ['PLATFORM_X64']
|
||||
binary.compiler.defines += ['DYNAMICHOOKS_x86_64', 'DHOOKS_DYNAMIC_DETOUR']
|
||||
|
||||
if binary.compiler.target.platform == 'windows':
|
||||
binary.sources += [
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86_64MicrosoftDefault.cpp')
|
||||
]
|
||||
# Linux follows System V definition
|
||||
elif binary.compiler.target.platform == 'linux':
|
||||
binary.sources += [
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86_64SystemVDefault.cpp')
|
||||
]
|
||||
|
||||
SM.extensions += [builder.Add(binary)]
|
||||
|
||||
@ -0,0 +1,281 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks-x86_64
|
||||
* Copyright (C) 2024 Benoist "Kenzzer" André. All rights reserved.
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86_64MicrosoftDefault.h"
|
||||
#include <smsdk_ext.h>
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
x86_64MicrosoftDefault::x86_64MicrosoftDefault(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
ICallingConvention(vecArgTypes, returnType, iAlignment),
|
||||
m_stackArgs(0)
|
||||
{
|
||||
const Register_t params_reg[] = { RCX, RDX, R8, R9 };
|
||||
const Register_t params_floatreg[] = { XMM0, XMM1, XMM2, XMM3 };
|
||||
//const char* regNames[] = { "RCX or XMM0", "RDX or XMM1", "R8 or XMM2", "R9 or XMM3"};
|
||||
const std::uint8_t num_reg = sizeof(params_reg) / sizeof(Register_t);
|
||||
|
||||
bool used_reg[] = { false, false, false, false };
|
||||
|
||||
// Figure out if any register has been used
|
||||
auto retreg = m_returnType.custom_register;
|
||||
used_reg[0] = (retreg == RCX || retreg == XMM0);
|
||||
used_reg[1] = (retreg == RDX || retreg == XMM1);
|
||||
used_reg[2] = (retreg == R8 || retreg == XMM2);
|
||||
used_reg[3] = (retreg == R9 || retreg == XMM3);
|
||||
|
||||
for (const auto& arg : m_vecArgTypes) {
|
||||
int reg_index = -1;
|
||||
if (arg.custom_register == RCX || arg.custom_register == XMM0) {
|
||||
reg_index = 0;
|
||||
} else if (arg.custom_register == RDX || arg.custom_register == XMM1) {
|
||||
reg_index = 1;
|
||||
} else if (arg.custom_register == R8 || arg.custom_register == XMM2) {
|
||||
reg_index = 2;
|
||||
} else if (arg.custom_register == R9 || arg.custom_register == XMM3) {
|
||||
reg_index = 3;
|
||||
}
|
||||
|
||||
if (reg_index != -1) {
|
||||
if (used_reg[reg_index]) {
|
||||
puts("Argument register is used twice, or shared with return");
|
||||
return;
|
||||
}
|
||||
used_reg[reg_index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special return type
|
||||
if (m_returnType.custom_register == None && m_returnType.type == DATA_TYPE_OBJECT &&
|
||||
// If size unknown, or doesn't fit on 1, 2, 4 or 8 bytes
|
||||
// special place must have been allocated for it
|
||||
(m_returnType.size == 0
|
||||
|| m_returnType.size == 3
|
||||
|| m_returnType.size == 5
|
||||
|| m_returnType.size == 6
|
||||
|| m_returnType.size == 7
|
||||
|| m_returnType.size > 8)) {
|
||||
for (std::uint8_t i = 0; i < num_reg && m_returnType.custom_register == None; i++) {
|
||||
if (!used_reg[i]) {
|
||||
m_returnType.custom_register = params_reg[i];
|
||||
used_reg[i] = true;
|
||||
}
|
||||
// Couldn't find a free register, this is a big problem
|
||||
if (m_returnType.custom_register == None) {
|
||||
puts("Missing free register for return pointer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& arg : m_vecArgTypes) {
|
||||
if (arg.custom_register == None) {
|
||||
for (std::uint8_t i = 0; i < num_reg && arg.custom_register == None; i++) {
|
||||
// Register is unused assign it
|
||||
if (!used_reg[i]) {
|
||||
arg.custom_register = (arg.type == DATA_TYPE_FLOAT || arg.type == DATA_TYPE_DOUBLE) ? params_floatreg[i] : params_reg[i];
|
||||
used_reg[i] = true;
|
||||
}
|
||||
}
|
||||
// Couldn't find a free register, it's therefore a stack parameter
|
||||
if (arg.custom_register == None) {
|
||||
m_stackArgs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Register_t> x86_64MicrosoftDefault::GetRegisters()
|
||||
{
|
||||
std::vector<Register_t> registers;
|
||||
|
||||
registers.push_back(RSP);
|
||||
|
||||
if (m_returnType.custom_register != None)
|
||||
{
|
||||
registers.push_back(m_returnType.custom_register);
|
||||
}
|
||||
else if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
{
|
||||
registers.push_back(XMM0);
|
||||
}
|
||||
else
|
||||
{
|
||||
registers.push_back(RAX);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
auto reg = m_vecArgTypes[i].custom_register;
|
||||
if (reg == None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
registers.push_back(m_vecArgTypes[i].custom_register);
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
|
||||
int x86_64MicrosoftDefault::GetPopSize()
|
||||
{
|
||||
// Clean-up is caller handled
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x86_64MicrosoftDefault::GetArgStackSize()
|
||||
{
|
||||
// Shadow space (32 bytes) + 8 bytes * amount of stack arguments
|
||||
return 32 + 8 * m_stackArgs;
|
||||
}
|
||||
|
||||
void** x86_64MicrosoftDefault::GetStackArgumentPtr(CRegisters* registers)
|
||||
{
|
||||
// Skip shadow space + return address
|
||||
return (void **)(registers->m_rsp->GetValue<uintptr_t>() + 8 + 32);
|
||||
}
|
||||
|
||||
int x86_64MicrosoftDefault::GetArgRegisterSize()
|
||||
{
|
||||
int argRegisterSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register != None)
|
||||
{
|
||||
// It doesn't matter, it's always 8 bytes or less
|
||||
argRegisterSize += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return argRegisterSize;
|
||||
}
|
||||
|
||||
void* x86_64MicrosoftDefault::GetArgumentPtr(unsigned int index, CRegisters* registers)
|
||||
{
|
||||
//g_pSM->LogMessage(myself, "Retrieving argument %d (max args %d) registers %p", index, m_vecArgTypes.size(), registers);
|
||||
if (index >= m_vecArgTypes.size())
|
||||
{
|
||||
//g_pSM->LogMessage(myself, "Not enough arguments");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if this argument was passed in a register.
|
||||
if (m_vecArgTypes[index].custom_register != None)
|
||||
{
|
||||
CRegister* reg = registers->GetRegister(m_vecArgTypes[index].custom_register);
|
||||
if (!reg)
|
||||
{
|
||||
//g_pSM->LogMessage(myself, "Register does not exit");
|
||||
return nullptr;
|
||||
}
|
||||
//g_pSM->LogMessage(myself, "Register arg %d", m_vecArgTypes[index].custom_register);
|
||||
return reg->m_pAddress;
|
||||
}
|
||||
|
||||
// Return address + shadow space
|
||||
size_t offset = 8 + 32;
|
||||
for (unsigned int i = 0; i < index; i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
{
|
||||
// No matter what, the stack is allocated in slices of 8 bytes
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
return (void *) (registers->m_rsp->GetValue<uintptr_t>() + offset);
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::ArgumentPtrChanged(unsigned int index, CRegisters* registers, void* argumentPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void* x86_64MicrosoftDefault::GetReturnPtr(CRegisters* registers)
|
||||
{
|
||||
// Custom return value register
|
||||
if (m_returnType.custom_register != None)
|
||||
{
|
||||
return registers->GetRegister(m_returnType.custom_register)->m_pAddress;
|
||||
}
|
||||
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
{
|
||||
// Floating point register
|
||||
return registers->m_xmm0->m_pAddress;
|
||||
}
|
||||
return registers->m_rax->m_pAddress;
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::SaveReturnValue(CRegisters* registers)
|
||||
{
|
||||
// It doesn't matter what the return value is, it will always be fitting on 8 bytes (or less)
|
||||
std::unique_ptr<uint8_t[]> savedReturn = std::make_unique<uint8_t[]>(8);
|
||||
memcpy(savedReturn.get(), GetReturnPtr(registers), 8);
|
||||
m_pSavedReturnBuffers.push_back(std::move(savedReturn));
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::RestoreReturnValue(CRegisters* registers)
|
||||
{
|
||||
// Like stated in SaveReturnValue, it will always fit within 8 bytes
|
||||
// the actual underlining type does not matter
|
||||
uint8_t* savedReturn = m_pSavedReturnBuffers.back().get();
|
||||
memcpy(GetReturnPtr(registers), savedReturn, 8);
|
||||
ReturnPtrChanged(registers, savedReturn);
|
||||
m_pSavedReturnBuffers.pop_back();
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::SaveCallArguments(CRegisters* registers)
|
||||
{
|
||||
int size = GetArgStackSize() + GetArgRegisterSize();
|
||||
std::unique_ptr<uint8_t[]> savedCallArguments = std::make_unique<uint8_t[]>(size);
|
||||
size_t offset = 0;
|
||||
for (unsigned int i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
// Doesn't matter the type, it will always be within 8 bytes
|
||||
memcpy((void *)((uintptr_t)savedCallArguments.get() + offset), GetArgumentPtr(i, registers), 8);
|
||||
offset += 8;
|
||||
}
|
||||
m_pSavedCallArguments.push_back(std::move(savedCallArguments));
|
||||
}
|
||||
|
||||
void x86_64MicrosoftDefault::RestoreCallArguments(CRegisters* registers)
|
||||
{
|
||||
uint8_t *savedCallArguments = m_pSavedCallArguments.back().get();
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
// Doesn't matter the type, it will always be within 8 bytes
|
||||
memcpy(GetArgumentPtr((unsigned int)i, registers), (void *)((uintptr_t)savedCallArguments + offset), 8);
|
||||
offset += 8;
|
||||
}
|
||||
m_pSavedCallArguments.pop_back();
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks-x86_64
|
||||
* Copyright (C) 2024 Benoist "Kenzzer" André. All rights reserved.
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
#ifndef _X86_64_MICROSOFT_DEFAULT_H
|
||||
#define _X86_64_MICROSOFT_DEFAULT_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "../convention.h"
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
class x86_64MicrosoftDefault : public ICallingConvention
|
||||
{
|
||||
public:
|
||||
x86_64MicrosoftDefault(std::vector<DataTypeSized_t>& vecArgTypes, DataTypeSized_t returnType, int iAlignment = 8);
|
||||
virtual ~x86_64MicrosoftDefault() = default;
|
||||
|
||||
virtual std::vector<Register_t> GetRegisters() override;
|
||||
virtual int GetPopSize() override;
|
||||
|
||||
virtual int GetArgStackSize() override;
|
||||
virtual void** GetStackArgumentPtr(CRegisters* registers) override;
|
||||
|
||||
virtual int GetArgRegisterSize() override;
|
||||
|
||||
virtual void* GetArgumentPtr(unsigned int index, CRegisters* registers) override;
|
||||
virtual void ArgumentPtrChanged(unsigned int index, CRegisters* registers, void* argumentPtr) override;
|
||||
|
||||
virtual void* GetReturnPtr(CRegisters* registers) override;
|
||||
virtual void ReturnPtrChanged(CRegisters* registers, void* returnPtr) override;
|
||||
|
||||
virtual void SaveReturnValue(CRegisters* registers) override;
|
||||
virtual void RestoreReturnValue(CRegisters* registers) override;
|
||||
|
||||
virtual void SaveCallArguments(CRegisters* pRegisters) override;
|
||||
virtual void RestoreCallArguments(CRegisters* pRegisters) override;
|
||||
|
||||
protected:
|
||||
std::uint32_t m_stackArgs;
|
||||
};
|
||||
|
||||
#endif // _X86_64_MICROSOFT_DEFAULT_H
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks-x86_64
|
||||
* Copyright (C) 2024 Benoist "Kenzzer" André. All rights reserved.
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks-x86_64
|
||||
* Copyright (C) 2024 Benoist "Kenzzer" André. All rights reserved.
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
@ -35,12 +35,16 @@
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "hook.h"
|
||||
#include <macro-assembler-x86.h>
|
||||
#include "extension.h"
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
|
||||
#else
|
||||
#include <macro-assembler-x86.h>
|
||||
#include <jit/jit_helpers.h>
|
||||
#include <CDetour/detourhelpers.h>
|
||||
|
||||
using namespace sp;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// >> DEFINITIONS
|
||||
@ -63,7 +67,7 @@ CHook::CHook(void* pFunc, ICallingConvention* pConvention)
|
||||
if (!m_RetAddr.init())
|
||||
return;
|
||||
|
||||
m_pBridge = CreateBridge();
|
||||
CreateBridge();
|
||||
if (!m_pBridge)
|
||||
return;
|
||||
|
||||
@ -214,7 +218,392 @@ void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
|
||||
i->value.push_back(pRetAddr);
|
||||
}
|
||||
|
||||
void* CHook::CreateBridge()
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
using namespace SourceHook::Asm;
|
||||
SourceHook::CPageAlloc SourceHook::Asm::GenBuffer::ms_Allocator(16);
|
||||
|
||||
void PrintFunc(const char* message) {
|
||||
g_pSM->LogMessage(myself, message);
|
||||
}
|
||||
|
||||
void PrintDebug(x64JitWriter& jit, const char* message) {
|
||||
// LogMessage has variadic parameters, this shouldn't be problem on x86_64
|
||||
// but paranoia calls for safety
|
||||
/*
|
||||
union {
|
||||
void (*PrintFunc)(const char* message);
|
||||
std::uint64_t address;
|
||||
} func;
|
||||
|
||||
func.PrintFunc = &PrintFunc;
|
||||
|
||||
// Shadow space
|
||||
MSVC_ONLY(jit.sub(rsp, 40));
|
||||
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(message)));
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(message)));
|
||||
|
||||
jit.mov(rax, func.address);
|
||||
jit.call(rax);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(jit.add(rsp, 40));*/
|
||||
}
|
||||
|
||||
void _PrintRegs(std::uint64_t* rsp, int numregs) {
|
||||
g_pSM->LogMessage(myself, "RSP - %p", rsp);
|
||||
|
||||
for (int i = 0; i < numregs; i++) {
|
||||
g_pSM->LogMessage(myself, "RSP[%d] - %llu", i, rsp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintRegisters(x64JitWriter& jit) {
|
||||
/*
|
||||
union {
|
||||
void (*PrintRegs)(std::uint64_t* rsp, int numregs);
|
||||
std::uint64_t address;
|
||||
} func;
|
||||
func.PrintRegs = &_PrintRegs;
|
||||
// Rax is pushed twice to keep the stack aligned
|
||||
jit.push(rax);
|
||||
jit.push(rax);
|
||||
jit.push(rcx);
|
||||
jit.push(rdx);
|
||||
jit.push(r8);
|
||||
jit.push(r9);
|
||||
|
||||
jit.mov(rcx, rsp);
|
||||
jit.mov(rdx, 5);
|
||||
jit.sub(rsp, 40);
|
||||
jit.mov(rax, func.address);
|
||||
jit.call(rax);
|
||||
jit.add(rsp, 40);
|
||||
|
||||
jit.pop(r9);
|
||||
jit.pop(r8);
|
||||
jit.pop(rdx);
|
||||
jit.pop(rcx);
|
||||
jit.pop(rax);
|
||||
jit.pop(rax);*/
|
||||
}
|
||||
|
||||
void CHook::CreateBridge()
|
||||
{
|
||||
auto& jit = m_bridge;
|
||||
|
||||
//jit.breakpoint();
|
||||
PrintRegisters(jit);
|
||||
|
||||
// Save registers right away
|
||||
Write_SaveRegisters(jit, HOOKTYPE_PRE);
|
||||
|
||||
PrintDebug(jit, "Hook Called");
|
||||
|
||||
Write_ModifyReturnAddress(jit);
|
||||
|
||||
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
|
||||
Write_CallHandler(jit, HOOKTYPE_PRE);
|
||||
|
||||
jit.cmp(rax, ReturnAction_Supercede);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(jit, HOOKTYPE_PRE);
|
||||
|
||||
jit.je(0x0);
|
||||
std::int32_t jumpOff = jit.get_outputpos();
|
||||
|
||||
// Dump regs
|
||||
PrintRegisters(jit);
|
||||
|
||||
// Jump to the trampoline
|
||||
jit.mov(rax, reinterpret_cast<std::uint64_t>(&m_pTrampoline));
|
||||
jit.mov(rax, rax());
|
||||
jit.jump(rax);
|
||||
|
||||
// This code will be executed if a pre-hook returns ReturnAction_Supercede
|
||||
jit.rewrite<std::int32_t>(jumpOff - sizeof(std::int32_t), jit.get_outputpos() - jumpOff);
|
||||
|
||||
PrintDebug(jit, "Hook leave");
|
||||
|
||||
// Finally, return to the caller
|
||||
// This will still call post hooks, but will skip the original function.
|
||||
jit.retn();
|
||||
|
||||
// It's very important to only finalise the jit code creation here
|
||||
// setting the memory to ReadExecute too early can create crashes
|
||||
// as it changes the permissions of the whole memory page
|
||||
m_pBridge = m_bridge.GetData();
|
||||
m_pNewRetAddr = m_postCallback.GetData();
|
||||
|
||||
m_bridge.SetRE();
|
||||
m_postCallback.SetRE();
|
||||
}
|
||||
|
||||
void CHook::Write_ModifyReturnAddress(x64JitWriter& jit)
|
||||
{
|
||||
// Store the return address in rax
|
||||
jit.mov(rax, rsp());
|
||||
|
||||
// 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.
|
||||
union
|
||||
{
|
||||
void (__cdecl CHook::*SetReturnAddress)(void*, void*);
|
||||
std::uint64_t address;
|
||||
} func;
|
||||
func.SetReturnAddress = &CHook::SetReturnAddress;
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes to keep it aligned on 16 bytes
|
||||
MSVC_ONLY(jit.sub(rsp, 40));
|
||||
|
||||
// 1st param (this)
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(this)));
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(this)));
|
||||
|
||||
// 2nd parameter (return address)
|
||||
GCC_ONLY(jit.mov(rsi, rax));
|
||||
MSVC_ONLY(jit.mov(rdx, rax));
|
||||
|
||||
// 3rd parameter (rsp)
|
||||
GCC_ONLY(jit.lea(rdx, rsp()));
|
||||
MSVC_ONLY(jit.lea(r8, rsp(40)));
|
||||
|
||||
// Call SetReturnAddress
|
||||
jit.mov(rax, func.address);
|
||||
jit.call(rax);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(jit.add(rsp, 40));
|
||||
|
||||
// Override the return address. This is a redirect to our post-hook code
|
||||
CreatePostCallback();
|
||||
jit.mov(rax, reinterpret_cast<std::uint64_t>(&m_pNewRetAddr));
|
||||
jit.mov(rax, rax());
|
||||
jit.mov(rsp(), rax);
|
||||
}
|
||||
|
||||
void CHook::CreatePostCallback()
|
||||
{
|
||||
auto& jit = m_postCallback;
|
||||
|
||||
jit.sub(rsp, 8);
|
||||
PrintRegisters(jit);
|
||||
|
||||
// Save registers right away
|
||||
Write_SaveRegisters(jit, HOOKTYPE_POST);
|
||||
|
||||
PrintDebug(jit, "Hook post");
|
||||
PrintRegisters(jit);
|
||||
|
||||
// Call the post-hook handler
|
||||
Write_CallHandler(jit, HOOKTYPE_POST);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(jit, HOOKTYPE_POST);
|
||||
|
||||
// Get return address
|
||||
union
|
||||
{
|
||||
void* (__cdecl CHook::*GetReturnAddress)(void*);
|
||||
std::uint64_t address;
|
||||
} func;
|
||||
func.GetReturnAddress = &CHook::GetReturnAddress;
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes to keep it aligned on 16 bytes
|
||||
MSVC_ONLY(jit.sub(rsp, 40));
|
||||
|
||||
// 1st param (this)
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(this)));
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(this)));
|
||||
|
||||
// 2n parameter (rsp)
|
||||
GCC_ONLY(jit.lea(rsi, rsp()));
|
||||
MSVC_ONLY(jit.lea(rdx, rsp(40)));
|
||||
|
||||
// Call GetReturnAddress
|
||||
jit.mov(rax, func.address);
|
||||
jit.call(rax);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(jit.add(rsp, 40));
|
||||
|
||||
// Jump to the original return address
|
||||
jit.add(rsp, 8);
|
||||
jit.jump(rax);
|
||||
}
|
||||
|
||||
void CHook::Write_CallHandler(x64JitWriter& jit, HookType_t type)
|
||||
{
|
||||
union
|
||||
{
|
||||
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t);
|
||||
std::uint64_t address;
|
||||
} func;
|
||||
|
||||
func.HookHandler = &CHook::HookHandler;
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes to keep it aligned on 16 bytes
|
||||
MSVC_ONLY(jit.sub(rsp, 40));
|
||||
|
||||
// Call the global hook handler
|
||||
|
||||
// 1st param (this)
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(this)));
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(this)));
|
||||
|
||||
// 2nd parameter (type)
|
||||
GCC_ONLY(jit.mov(rsi, type));
|
||||
MSVC_ONLY(jit.mov(rdx, type));
|
||||
|
||||
jit.mov(rax, func.address);
|
||||
jit.call(rax);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(jit.add(rsp, 40));
|
||||
}
|
||||
|
||||
void CHook::Write_SaveRegisters(x64JitWriter& jit, HookType_t type)
|
||||
{
|
||||
// Save RAX
|
||||
jit.push(rax);
|
||||
bool saveRAX = false;
|
||||
|
||||
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
|
||||
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
|
||||
{
|
||||
switch(vecRegistersToSave[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// RAX is saved by default (see above)
|
||||
case Register_t::RAX: saveRAX = true; break;
|
||||
case Register_t::RCX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rcx->m_pAddress)); jit.mov(rax(), rcx); break;
|
||||
case Register_t::RDX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rdx->m_pAddress)); jit.mov(rax(), rdx); break;
|
||||
case Register_t::RBX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rbx->m_pAddress)); jit.mov(rax(), rbx); break;
|
||||
// BEWARE BECAUSE WE PUSHED TO STACK ABOVE, RSP NEEDS TO BE HANDLED DIFFERENTLY
|
||||
case Register_t::RSP:
|
||||
jit.push(r8);
|
||||
// Add +push(rax) & +push(r8)
|
||||
jit.lea(r8, rsp(16));
|
||||
jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rsp->m_pAddress)); jit.mov(rax(), r8);
|
||||
jit.pop(r8);
|
||||
break;
|
||||
case Register_t::RBP: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rbp->m_pAddress)); jit.mov(rax(), rbp); break;
|
||||
case Register_t::RSI: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rsi->m_pAddress)); jit.mov(rax(), rsi); break;
|
||||
case Register_t::RDI: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rdi->m_pAddress)); jit.mov(rax(), rdi); break;
|
||||
|
||||
case Register_t::R8: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r8->m_pAddress)); jit.mov(rax(), r8); break;
|
||||
case Register_t::R9: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r9->m_pAddress)); jit.mov(rax(), r9); break;
|
||||
case Register_t::R10: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r10->m_pAddress)); jit.mov(rax(), r10); break;
|
||||
case Register_t::R11: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r11->m_pAddress)); jit.mov(rax(), r11); break;
|
||||
case Register_t::R12: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r12->m_pAddress)); jit.mov(rax(), r12); break;
|
||||
case Register_t::R13: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r13->m_pAddress)); jit.mov(rax(), r13); break;
|
||||
case Register_t::R14: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r14->m_pAddress)); jit.mov(rax(), r14); break;
|
||||
case Register_t::R15: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r15->m_pAddress)); jit.mov(rax(), r15); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
case Register_t::XMM0: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm0->m_pAddress)); jit.movsd(rax(), xmm0); break;
|
||||
case Register_t::XMM1: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm1->m_pAddress)); jit.movsd(rax(), xmm1); break;
|
||||
case Register_t::XMM2: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm2->m_pAddress)); jit.movsd(rax(), xmm2); break;
|
||||
case Register_t::XMM3: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm3->m_pAddress)); jit.movsd(rax(), xmm3); break;
|
||||
case Register_t::XMM4: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm4->m_pAddress)); jit.movsd(rax(), xmm4); break;
|
||||
case Register_t::XMM5: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm5->m_pAddress)); jit.movsd(rax(), xmm5); break;
|
||||
case Register_t::XMM6: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm6->m_pAddress)); jit.movsd(rax(), xmm6); break;
|
||||
case Register_t::XMM7: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm7->m_pAddress)); jit.movsd(rax(), xmm7); break;
|
||||
|
||||
case Register_t::XMM8: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm8->m_pAddress)); jit.movsd(rax(), xmm8); break;
|
||||
case Register_t::XMM9: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm9->m_pAddress)); jit.movsd(rax(), xmm9); break;
|
||||
case Register_t::XMM10: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm10->m_pAddress)); jit.movsd(rax(), xmm10); break;
|
||||
case Register_t::XMM11: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm11->m_pAddress)); jit.movsd(rax(), xmm11); break;
|
||||
case Register_t::XMM12: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm12->m_pAddress)); jit.movsd(rax(), xmm12); break;
|
||||
case Register_t::XMM13: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm13->m_pAddress)); jit.movsd(rax(), xmm13); break;
|
||||
case Register_t::XMM14: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm14->m_pAddress)); jit.movsd(rax(), xmm14); break;
|
||||
case Register_t::XMM15: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm15->m_pAddress)); jit.movsd(rax(), xmm15); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
|
||||
jit.pop(rax);
|
||||
if (saveRAX) {
|
||||
jit.push(r8);
|
||||
jit.mov(r8, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rax->m_pAddress)); jit.mov(r8(), rax);
|
||||
jit.pop(r8);
|
||||
}
|
||||
}
|
||||
|
||||
void CHook::Write_RestoreRegisters(x64JitWriter& jit, HookType_t type)
|
||||
{
|
||||
// RAX & RSP will be restored last
|
||||
bool restoreRAX = false, restoreRSP = false;
|
||||
|
||||
const auto& vecRegistersToRestore = m_pCallingConvention->GetRegisters();
|
||||
for(size_t i = 0; i < vecRegistersToRestore.size(); i++)
|
||||
{
|
||||
switch(vecRegistersToRestore[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// RAX is handled differently (see below)
|
||||
case Register_t::RAX: restoreRAX = true; break;
|
||||
case Register_t::RCX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rcx->m_pAddress)); jit.mov(rcx, rax()); break;
|
||||
case Register_t::RDX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rdx->m_pAddress)); jit.mov(rdx, rax()); break;
|
||||
case Register_t::RBX: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rbx->m_pAddress)); jit.mov(rbx, rax()); break;
|
||||
// This could be very troublesome, but oh well...
|
||||
case Register_t::RSP: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rsp->m_pAddress)); jit.mov(rsp, rax()); break;
|
||||
case Register_t::RBP: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rbp->m_pAddress)); jit.mov(rbp, rax()); break;
|
||||
case Register_t::RSI: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rsi->m_pAddress)); jit.mov(rsi, rax()); break;
|
||||
case Register_t::RDI: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rdi->m_pAddress)); jit.mov(rdi, rax()); break;
|
||||
|
||||
case Register_t::R8: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r8->m_pAddress)); jit.mov(r8, rax()); break;
|
||||
case Register_t::R9: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r9->m_pAddress)); jit.mov(r9, rax()); break;
|
||||
case Register_t::R10: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r10->m_pAddress)); jit.mov(r10, rax()); break;
|
||||
case Register_t::R11: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r11->m_pAddress)); jit.mov(r11, rax()); break;
|
||||
case Register_t::R12: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r12->m_pAddress)); jit.mov(r12, rax()); break;
|
||||
case Register_t::R13: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r13->m_pAddress)); jit.mov(r13, rax()); break;
|
||||
case Register_t::R14: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r14->m_pAddress)); jit.mov(r14, rax()); break;
|
||||
case Register_t::R15: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_r15->m_pAddress)); jit.mov(r15, rax()); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
case Register_t::XMM0: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm0->m_pAddress)); jit.movsd(xmm0, rax()); break;
|
||||
case Register_t::XMM1: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm1->m_pAddress)); jit.movsd(xmm1, rax()); break;
|
||||
case Register_t::XMM2: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm2->m_pAddress)); jit.movsd(xmm2, rax()); break;
|
||||
case Register_t::XMM3: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm3->m_pAddress)); jit.movsd(xmm3, rax()); break;
|
||||
case Register_t::XMM4: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm4->m_pAddress)); jit.movsd(xmm4, rax()); break;
|
||||
case Register_t::XMM5: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm5->m_pAddress)); jit.movsd(xmm5, rax()); break;
|
||||
case Register_t::XMM6: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm6->m_pAddress)); jit.movsd(xmm6, rax()); break;
|
||||
case Register_t::XMM7: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm7->m_pAddress)); jit.movsd(xmm7, rax()); break;
|
||||
|
||||
case Register_t::XMM8: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm8->m_pAddress)); jit.movsd(xmm8, rax()); break;
|
||||
case Register_t::XMM9: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm9->m_pAddress)); jit.movsd(xmm9, rax()); break;
|
||||
case Register_t::XMM10: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm10->m_pAddress)); jit.movsd(xmm10, rax()); break;
|
||||
case Register_t::XMM11: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm11->m_pAddress)); jit.movsd(xmm11, rax()); break;
|
||||
case Register_t::XMM12: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm12->m_pAddress)); jit.movsd(xmm12, rax()); break;
|
||||
case Register_t::XMM13: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm13->m_pAddress)); jit.movsd(xmm13, rax()); break;
|
||||
case Register_t::XMM14: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm14->m_pAddress)); jit.movsd(xmm14, rax()); break;
|
||||
case Register_t::XMM15: jit.mov(rax, reinterpret_cast<std::uint64_t>(m_pRegisters->m_xmm15->m_pAddress)); jit.movsd(xmm15, rax()); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
|
||||
if (restoreRAX) {
|
||||
jit.push(r8);
|
||||
jit.mov(r8, reinterpret_cast<std::uint64_t>(m_pRegisters->m_rax->m_pAddress));
|
||||
jit.mov(rax, r8());
|
||||
jit.pop(r8);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void CHook::CreateBridge()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
Label label_supercede;
|
||||
@ -242,9 +631,9 @@ void* CHook::CreateBridge()
|
||||
// This will still call post hooks, but will skip the original function.
|
||||
masm.ret(m_pCallingConvention->GetPopSize());
|
||||
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
void* base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
m_pBridge = base;
|
||||
}
|
||||
|
||||
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
|
||||
@ -276,11 +665,11 @@ void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
|
||||
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
|
||||
|
||||
// Override the return address. This is a redirect to our post-hook code
|
||||
m_pNewRetAddr = CreatePostCallback();
|
||||
CreatePostCallback();
|
||||
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
|
||||
}
|
||||
|
||||
void* CHook::CreatePostCallback()
|
||||
void CHook::CreatePostCallback()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
|
||||
@ -328,9 +717,9 @@ void* CHook::CreatePostCallback()
|
||||
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
|
||||
|
||||
// Generate the code
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
void* base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
m_pNewRetAddr = base;
|
||||
}
|
||||
|
||||
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
|
||||
@ -488,3 +877,4 @@ void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -43,6 +43,21 @@
|
||||
#include <am-hashset.h>
|
||||
#include <safetyhook.hpp>
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
#include <sourcehook.h>
|
||||
#include <sh_memory.h>
|
||||
#include "sh_asm_x86_64.h"
|
||||
|
||||
#if SH_COMP == SH_COMP_MSVC
|
||||
# define GCC_ONLY(x)
|
||||
# define MSVC_ONLY(x) x
|
||||
#elif SH_COMP == SH_COMP_GCC
|
||||
# define GCC_ONLY(x) x
|
||||
# define MSVC_ONLY(x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// >> HookType_t
|
||||
// ============================================================================
|
||||
@ -174,14 +189,23 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void* CreateBridge();
|
||||
void CreateBridge();
|
||||
void CreatePostCallback();
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
void Write_ModifyReturnAddress(SourceHook::Asm::x64JitWriter& jit);
|
||||
void Write_CallHandler(SourceHook::Asm::x64JitWriter& jit, HookType_t type);
|
||||
void Write_SaveRegisters(SourceHook::Asm::x64JitWriter& jit, HookType_t type);
|
||||
void Write_RestoreRegisters(SourceHook::Asm::x64JitWriter& jit, HookType_t type);
|
||||
|
||||
SourceHook::Asm::x64JitWriter m_bridge;
|
||||
SourceHook::Asm::x64JitWriter m_postCallback;
|
||||
#else
|
||||
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();
|
||||
#endif
|
||||
|
||||
ReturnAction_t __cdecl HookHandler(HookType_t type);
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@ CRegisters::CRegisters(std::vector<Register_t> registers)
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
m_rax = CreateRegister(registers, RAX, 8);
|
||||
m_rcx = CreateRegister(registers, RCX, 8);
|
||||
m_rdx = CreateRegister(registers, RDX, 8);
|
||||
@ -279,7 +279,7 @@ CRegisters::~CRegisters()
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
DeleteRegister(m_rax);
|
||||
DeleteRegister(m_rcx);
|
||||
DeleteRegister(m_rdx);
|
||||
@ -325,7 +325,7 @@ CRegisters::~CRegisters()
|
||||
DeleteRegister(m_xmm7);
|
||||
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
DeleteRegister(m_xmm8);
|
||||
DeleteRegister(m_xmm9);
|
||||
DeleteRegister(m_xmm10);
|
||||
@ -434,6 +434,42 @@ CRegister* CRegisters::GetRegister(Register_t reg)
|
||||
case EDI:
|
||||
return m_edi;
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
case RAX:
|
||||
return m_rax;
|
||||
case RCX:
|
||||
return m_rcx;
|
||||
case RDX:
|
||||
return m_rdx;
|
||||
case RBX:
|
||||
return m_rbx;
|
||||
case RSP:
|
||||
return m_rsp;
|
||||
case RBP:
|
||||
return m_rbp;
|
||||
case RSI:
|
||||
return m_rsi;
|
||||
case RDI:
|
||||
return m_rdi;
|
||||
|
||||
case R8:
|
||||
return m_r8;
|
||||
case R9:
|
||||
return m_r9;
|
||||
case R10:
|
||||
return m_r10;
|
||||
case R11:
|
||||
return m_r11;
|
||||
case R12:
|
||||
return m_r12;
|
||||
case R13:
|
||||
return m_r13;
|
||||
case R14:
|
||||
return m_r14;
|
||||
case R15:
|
||||
return m_r15;
|
||||
#endif
|
||||
|
||||
case MM0:
|
||||
return m_mm0;
|
||||
case MM1:
|
||||
@ -468,6 +504,25 @@ CRegister* CRegisters::GetRegister(Register_t reg)
|
||||
case XMM7:
|
||||
return m_xmm7;
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
case XMM8:
|
||||
return m_xmm8;
|
||||
case XMM9:
|
||||
return m_xmm9;
|
||||
case XMM10:
|
||||
return m_xmm10;
|
||||
case XMM11:
|
||||
return m_xmm11;
|
||||
case XMM12:
|
||||
return m_xmm12;
|
||||
case XMM13:
|
||||
return m_xmm13;
|
||||
case XMM14:
|
||||
return m_xmm14;
|
||||
case XMM15:
|
||||
return m_xmm15;
|
||||
#endif
|
||||
|
||||
case CS:
|
||||
return m_cs;
|
||||
case SS:
|
||||
@ -499,6 +554,6 @@ CRegister* CRegisters::GetRegister(Register_t reg)
|
||||
return m_st7;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -132,7 +132,7 @@ enum Register_t
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
@ -177,7 +177,7 @@ enum Register_t
|
||||
XMM7,
|
||||
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
XMM8,
|
||||
XMM9,
|
||||
XMM10,
|
||||
@ -373,7 +373,7 @@ public:
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
CRegister* m_rax;
|
||||
CRegister* m_rcx;
|
||||
CRegister* m_rdx;
|
||||
@ -418,7 +418,7 @@ public:
|
||||
CRegister* m_xmm7;
|
||||
|
||||
// 64-bit mode only
|
||||
#ifdef PLATFORM_X64
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
CRegister* m_xmm8;
|
||||
CRegister* m_xmm9;
|
||||
CRegister* m_xmm10;
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
#include <memory>
|
||||
|
||||
#ifdef KE_WINDOWS
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
#include "conventions/x86_64MicrosoftDefault.h"
|
||||
typedef x86_64MicrosoftDefault x86_64DetourCall;
|
||||
#else
|
||||
#include "conventions/x86MsCdecl.h"
|
||||
#include "conventions/x86MsThiscall.h"
|
||||
#include "conventions/x86MsStdcall.h"
|
||||
@ -42,7 +46,10 @@ typedef x86MsCdecl x86DetourCdecl;
|
||||
typedef x86MsThiscall x86DetourThisCall;
|
||||
typedef x86MsStdcall x86DetourStdCall;
|
||||
typedef x86MsFastcall x86DetourFastCall;
|
||||
#endif
|
||||
#elif defined KE_LINUX
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
#else
|
||||
#include "conventions/x86GccCdecl.h"
|
||||
#include "conventions/x86GccThiscall.h"
|
||||
#include "conventions/x86MsStdcall.h"
|
||||
@ -53,6 +60,7 @@ typedef x86GccThiscall x86DetourThisCall;
|
||||
typedef x86MsStdcall x86DetourStdCall;
|
||||
// Uhumm, fastcall on linux?
|
||||
typedef x86MsFastcall x86DetourFastCall;
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported platform."
|
||||
#endif
|
||||
@ -246,9 +254,29 @@ ICallingConvention *ConstructCallingConvention(HookSetup *setup)
|
||||
// TODO: Add support for a custom return register.
|
||||
returnType.custom_register = None;
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
#ifdef WIN32
|
||||
if (setup->callConv == CallConv_THISCALL) {
|
||||
DataTypeSized_t type;
|
||||
type.type = DATA_TYPE_POINTER;
|
||||
type.size = GetDataTypeSize(type, sizeof(void*));
|
||||
type.custom_register = RCX;
|
||||
vecArgTypes.insert(vecArgTypes.begin(), type);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ICallingConvention *pCallConv = nullptr;
|
||||
switch (setup->callConv)
|
||||
{
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
case CallConv_THISCALL:
|
||||
case CallConv_CDECL:
|
||||
case CallConv_STDCALL:
|
||||
case CallConv_FASTCALL:
|
||||
pCallConv = new x86_64DetourCall(vecArgTypes, returnType);
|
||||
break;
|
||||
#else
|
||||
case CallConv_CDECL:
|
||||
pCallConv = new x86DetourCdecl(vecArgTypes, returnType);
|
||||
break;
|
||||
@ -261,6 +289,7 @@ ICallingConvention *ConstructCallingConvention(HookSetup *setup)
|
||||
case CallConv_FASTCALL:
|
||||
pCallConv = new x86DetourFastCall(vecArgTypes, returnType);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown calling convention %d.", setup->callConv);
|
||||
break;
|
||||
@ -624,7 +653,7 @@ HookParamsStruct *CDynamicHooksSourcePawn::GetParamStruct()
|
||||
|
||||
// Save the old parameters passed in a register.
|
||||
size_t offset = stackSize;
|
||||
for (size_t i = 0; i < numArgs; i++)
|
||||
for (size_t i = firstArg; i < numArgs; i++)
|
||||
{
|
||||
// We already saved the stack arguments.
|
||||
if (argTypes[i].custom_register == None)
|
||||
@ -633,7 +662,7 @@ HookParamsStruct *CDynamicHooksSourcePawn::GetParamStruct()
|
||||
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);
|
||||
void *regAddr = callingConvention->GetArgumentPtr(i, m_pDetour->m_pRegisters);
|
||||
memcpy(paramAddr, regAddr, size);
|
||||
offset += size;
|
||||
}
|
||||
@ -656,7 +685,6 @@ void CDynamicHooksSourcePawn::UpdateParamsFromStruct(HookParamsStruct *params)
|
||||
// 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;
|
||||
@ -676,9 +704,15 @@ void CDynamicHooksSourcePawn::UpdateParamsFromStruct(HookParamsStruct *params)
|
||||
}
|
||||
|
||||
// Keep track of the seperate stack and register arguments.
|
||||
if (argTypes[i].custom_register == None)
|
||||
if (argTypes[i].custom_register == None) {
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
stackOffset += 8;
|
||||
#else
|
||||
stackOffset += size;
|
||||
else
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
registerOffset += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,6 +565,42 @@ Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
|
||||
else if (!strcmp(str, "edi"))
|
||||
return EDI;
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
else if (!strcmp(str, "rax"))
|
||||
return RAX;
|
||||
else if (!strcmp(str, "rcx"))
|
||||
return RCX;
|
||||
else if (!strcmp(str, "rdx"))
|
||||
return RDX;
|
||||
else if (!strcmp(str, "rbx"))
|
||||
return RBX;
|
||||
else if (!strcmp(str, "rsp"))
|
||||
return RSP;
|
||||
else if (!strcmp(str, "rbp"))
|
||||
return RBP;
|
||||
else if (!strcmp(str, "rsi"))
|
||||
return RSI;
|
||||
else if (!strcmp(str, "rdi"))
|
||||
return RDI;
|
||||
|
||||
else if (!strcmp(str, "r8"))
|
||||
return R8;
|
||||
else if (!strcmp(str, "r9"))
|
||||
return R9;
|
||||
else if (!strcmp(str, "r10"))
|
||||
return R10;
|
||||
else if (!strcmp(str, "r11"))
|
||||
return R11;
|
||||
else if (!strcmp(str, "r12"))
|
||||
return R12;
|
||||
else if (!strcmp(str, "r13"))
|
||||
return R13;
|
||||
else if (!strcmp(str, "r14"))
|
||||
return R14;
|
||||
else if (!strcmp(str, "r15"))
|
||||
return R15;
|
||||
#endif
|
||||
|
||||
else if (!strcmp(str, "mm0"))
|
||||
return MM0;
|
||||
else if (!strcmp(str, "mm1"))
|
||||
@ -599,6 +635,25 @@ Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
|
||||
else if (!strcmp(str, "xmm7"))
|
||||
return XMM7;
|
||||
|
||||
#ifdef DYNAMICHOOKS_x86_64
|
||||
else if (!strcmp(str, "xmm8"))
|
||||
return XMM8;
|
||||
else if (!strcmp(str, "xmm9"))
|
||||
return XMM9;
|
||||
else if (!strcmp(str, "xmm10"))
|
||||
return XMM10;
|
||||
else if (!strcmp(str, "xmm11"))
|
||||
return XMM11;
|
||||
else if (!strcmp(str, "xmm12"))
|
||||
return XMM12;
|
||||
else if (!strcmp(str, "xmm13"))
|
||||
return XMM13;
|
||||
else if (!strcmp(str, "xmm14"))
|
||||
return XMM14;
|
||||
else if (!strcmp(str, "xmm15"))
|
||||
return XMM15;
|
||||
#endif
|
||||
|
||||
else if (!strcmp(str, "cs"))
|
||||
return CS;
|
||||
else if (!strcmp(str, "ss"))
|
||||
|
||||
@ -53,34 +53,12 @@ using namespace sp;
|
||||
|
||||
#ifdef PLATFORM_X64
|
||||
using namespace SourceHook::Asm;
|
||||
SourceHook::CPageAlloc GenBuffer::ms_Allocator(16);
|
||||
|
||||
void test_func(void* rcx, void* rdx, SDKVector* r8, bool r9)
|
||||
{
|
||||
//g_pSM->LogMessage(myself, "rcx(%p) - rdx(%p) - r8(%p) - r9(%p)", rcx, rdx, r8, r9);
|
||||
}
|
||||
|
||||
SourceHook::Asm::x64JitWriter* GenerateThunk(HookSetup* hook)
|
||||
{
|
||||
auto masm = new x64JitWriter();
|
||||
auto type = hook->returnType;
|
||||
|
||||
/*if (type == ReturnType_Vector)
|
||||
{
|
||||
masm->push(rcx);
|
||||
masm->push(rdx);
|
||||
masm->push(r8);
|
||||
masm->push(r9);
|
||||
masm->sub(rsp, 40);
|
||||
masm->mov(rax, (uintptr_t)test_func);
|
||||
masm->call(rax);
|
||||
masm->add(rsp, 40);
|
||||
masm->pop(r9);
|
||||
masm->pop(r8);
|
||||
masm->pop(rdx);
|
||||
masm->pop(rcx);
|
||||
}*/
|
||||
|
||||
// We're going to transform rbp into our stack
|
||||
masm->push(rbp);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user