-
-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* remove libudis86 * switch to safetyhook * Don't leak memory * fix issue with the trampoline * Add x86_64 detour support (windows) * Allow to differentiate platforms --------- Co-authored-by: Kenzzer <[email protected]>
- Loading branch information
Showing
27 changed files
with
1,034 additions
and
12,632 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
281 changes: 281 additions & 0 deletions
281
extensions/dhooks/DynamicHooks/conventions/x86_64MicrosoftDefault.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
Oops, something went wrong.