Last active
September 26, 2024 23:13
-
-
Save TheRouletteBoi/e1e9925699ee8d708881e167e397058b to your computer and use it in GitHub Desktop.
Hooking any function including imports and exports. PowerPC, PPC, PS3, Playstation 3
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
#include "DetourHook.hpp" | |
#define POWERPC_REGISTERINDEX_R0 0 | |
#define POWERPC_REGISTERINDEX_R1 1 | |
#define POWERPC_REGISTERINDEX_R2 2 | |
#define POWERPC_REGISTERINDEX_R3 3 | |
#define POWERPC_REGISTERINDEX_R4 4 | |
#define POWERPC_REGISTERINDEX_R5 5 | |
#define POWERPC_REGISTERINDEX_R6 6 | |
#define POWERPC_REGISTERINDEX_R7 7 | |
#define POWERPC_REGISTERINDEX_R8 8 | |
#define POWERPC_REGISTERINDEX_R9 9 | |
#define POWERPC_REGISTERINDEX_R10 10 | |
#define POWERPC_REGISTERINDEX_R11 11 | |
#define POWERPC_REGISTERINDEX_R12 12 | |
#define POWERPC_REGISTERINDEX_R13 13 | |
#define POWERPC_REGISTERINDEX_R14 14 | |
#define POWERPC_REGISTERINDEX_R15 15 | |
#define POWERPC_REGISTERINDEX_R16 16 | |
#define POWERPC_REGISTERINDEX_R17 17 | |
#define POWERPC_REGISTERINDEX_R18 18 | |
#define POWERPC_REGISTERINDEX_R19 19 | |
#define POWERPC_REGISTERINDEX_R20 20 | |
#define POWERPC_REGISTERINDEX_R21 21 | |
#define POWERPC_REGISTERINDEX_R22 22 | |
#define POWERPC_REGISTERINDEX_R23 23 | |
#define POWERPC_REGISTERINDEX_R24 24 | |
#define POWERPC_REGISTERINDEX_R25 25 | |
#define POWERPC_REGISTERINDEX_R26 26 | |
#define POWERPC_REGISTERINDEX_R27 27 | |
#define POWERPC_REGISTERINDEX_R28 28 | |
#define POWERPC_REGISTERINDEX_R29 29 | |
#define POWERPC_REGISTERINDEX_R30 30 | |
#define POWERPC_REGISTERINDEX_R31 31 | |
#define POWERPC_REGISTERINDEX_SP 1 | |
#define POWERPC_REGISTERINDEX_RTOC 2 | |
#define MASK_N_BITS(N) ( ( 1 << ( N ) ) - 1 ) | |
#define POWERPC_HI(X) ( ( X >> 16 ) & 0xFFFF ) | |
#define POWERPC_LO(X) ( X & 0xFFFF ) | |
// PowerPC most significant bit is addressed as bit 0 in documentation. | |
#define POWERPC_BIT32(N) ( 31 - N ) | |
// Opcode is bits 0-5. | |
// Allowing for op codes ranging from 0-63. | |
#define POWERPC_OPCODE(OP) (uint32_t)( OP << 26 ) | |
#define POWERPC_OPCODE_ADDI POWERPC_OPCODE( 14 ) | |
#define POWERPC_OPCODE_ADDIS POWERPC_OPCODE( 15 ) | |
#define POWERPC_OPCODE_BC POWERPC_OPCODE( 16 ) | |
#define POWERPC_OPCODE_B POWERPC_OPCODE( 18 ) | |
#define POWERPC_OPCODE_BCCTR POWERPC_OPCODE( 19 ) | |
#define POWERPC_OPCODE_ORI POWERPC_OPCODE( 24 ) | |
#define POWERPC_OPCODE_EXTENDED POWERPC_OPCODE( 31 ) // Use extended opcodes. | |
#define POWERPC_OPCODE_STW POWERPC_OPCODE( 36 ) | |
#define POWERPC_OPCODE_LWZ POWERPC_OPCODE( 32 ) | |
#define POWERPC_OPCODE_LD POWERPC_OPCODE( 58 ) | |
#define POWERPC_OPCODE_STD POWERPC_OPCODE( 62 ) | |
#define POWERPC_OPCODE_MASK POWERPC_OPCODE( 63 ) | |
#define POWERPC_EXOPCODE(OP) ( OP << 1 ) | |
#define POWERPC_EXOPCODE_BCCTR POWERPC_EXOPCODE( 528 ) | |
#define POWERPC_EXOPCODE_MTSPR POWERPC_EXOPCODE( 467 ) | |
// SPR field is encoded as two 5 bit bitfields. | |
#define POWERPC_SPR(SPR) (uint32_t)( ( ( SPR & 0x1F ) << 5 ) | ( ( SPR >> 5 ) & 0x1F ) ) | |
// Instruction helpers. | |
// rD - Destination register. | |
// rS - Source register. | |
// rA/rB - Register inputs. | |
// SPR - Special purpose register. | |
// UIMM/SIMM - Unsigned/signed immediate. | |
#define POWERPC_ADDI(rD, rA, SIMM) (uint32_t)( POWERPC_OPCODE_ADDI | ( rD << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | SIMM ) | |
#define POWERPC_ADDIS(rD, rA, SIMM) (uint32_t)( POWERPC_OPCODE_ADDIS | ( rD << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | SIMM ) | |
#define POWERPC_LIS(rD, SIMM) POWERPC_ADDIS( rD, 0, SIMM ) // Mnemonic for addis %rD, 0, SIMM | |
#define POWERPC_LI(rD, SIMM) POWERPC_ADDI( rD, 0, SIMM ) // Mnemonic for addi %rD, 0, SIMM | |
#define POWERPC_MTSPR(SPR, rS) (uint32_t)( POWERPC_OPCODE_EXTENDED | ( rS << POWERPC_BIT32( 10 ) ) | ( POWERPC_SPR( SPR ) << POWERPC_BIT32( 20 ) ) | POWERPC_EXOPCODE_MTSPR ) | |
#define POWERPC_MTCTR(rS) POWERPC_MTSPR( 9, rS ) // Mnemonic for mtspr 9, rS | |
#define POWERPC_ORI(rS, rA, UIMM) (uint32_t)( POWERPC_OPCODE_ORI | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | UIMM ) | |
#define POWERPC_BCCTR(BO, BI, LK) (uint32_t)( POWERPC_OPCODE_BCCTR | ( BO << POWERPC_BIT32( 10 ) ) | ( BI << POWERPC_BIT32( 15 ) ) | ( LK & 1 ) | POWERPC_EXOPCODE_BCCTR ) | |
#define POWERPC_STD(rS, DS, rA) (uint32_t)( POWERPC_OPCODE_STD | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | ( (int16_t)DS & 0xFFFF ) ) | |
#define POWERPC_LD(rS, DS, rA) (uint32_t)( POWERPC_OPCODE_LD | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | ( (int16_t)DS & 0xFFFF ) ) | |
// Branch related fields. | |
#define POWERPC_BRANCH_LINKED 1 | |
#define POWERPC_BRANCH_ABSOLUTE 2 | |
#define POWERPC_BRANCH_TYPE_MASK ( POWERPC_BRANCH_LINKED | POWERPC_BRANCH_ABSOLUTE ) | |
#define POWERPC_BRANCH_OPTIONS_ALWAYS ( 20 ) | |
uint8_t DetourHook::s_TrampolineBuffer[]{}; | |
size_t DetourHook::s_TrampolineSize = 0; | |
DetourHook::DetourHook() | |
: m_HookTarget(nullptr), m_HookAddress(nullptr), m_TrampolineAddress(nullptr), m_OriginalLength(0) | |
{ | |
memset(m_TrampolineOpd, 0, sizeof(m_TrampolineOpd)); | |
memset(m_OriginalInstructions, 0, sizeof(m_OriginalInstructions)); | |
} | |
DetourHook::DetourHook(uint32_t fnAddress, uintptr_t fnCallback) | |
: m_HookTarget(nullptr), m_HookAddress(nullptr), m_TrampolineAddress(nullptr), m_OriginalLength(0) | |
{ | |
memset(m_TrampolineOpd, 0, sizeof(m_TrampolineOpd)); | |
memset(m_OriginalInstructions, 0, sizeof(m_OriginalInstructions)); | |
Hook(fnAddress, fnCallback); | |
} | |
DetourHook::~DetourHook() | |
{ | |
UnHook(); | |
} | |
size_t DetourHook::GetHookSize(const void* branchTarget, bool linked, bool preserveRegister) | |
{ | |
return JumpWithOptions(nullptr, branchTarget, linked, preserveRegister, POWERPC_BRANCH_OPTIONS_ALWAYS, 0, POWERPC_REGISTERINDEX_R0); | |
} | |
size_t DetourHook::Jump(void* destination, const void* branchTarget, bool linked, bool preserveRegister) | |
{ | |
return JumpWithOptions(destination, branchTarget, linked, preserveRegister, POWERPC_BRANCH_OPTIONS_ALWAYS, 0, POWERPC_REGISTERINDEX_R0); | |
} | |
size_t DetourHook::JumpWithOptions(void* destination, const void* branchTarget, bool linked, bool preserveRegister, uint32_t branchOptions, uint8_t conditionRegisterBit, uint8_t registerIndex) | |
{ | |
uint32_t BranchFarAsm[] = { | |
POWERPC_LIS(registerIndex, POWERPC_HI((uint32_t)branchTarget)), // lis %rX, branchTarget@hi | |
POWERPC_ORI(registerIndex, registerIndex, POWERPC_LO((uint32_t)branchTarget)), // ori %rX, %rX, branchTarget@lo | |
POWERPC_MTCTR(registerIndex), // mtctr %rX | |
POWERPC_BCCTR(branchOptions, conditionRegisterBit, linked) // bcctr (bcctr 20, 0 == bctr) | |
}; | |
uint32_t BranchFarAsmPreserve[] = { | |
POWERPC_STD(registerIndex, -0x30, POWERPC_REGISTERINDEX_R1), // std %rX, -0x30(%r1) | |
POWERPC_LIS(registerIndex, POWERPC_HI((uint32_t)branchTarget)), // lis %rX, branchTarget@hi | |
POWERPC_ORI(registerIndex, registerIndex, POWERPC_LO((uint32_t)branchTarget)), // ori %rX, %rX, branchTarget@lo | |
POWERPC_MTCTR(registerIndex), // mtctr %rX | |
POWERPC_LD(registerIndex, -0x30, POWERPC_REGISTERINDEX_R1), // ld %rX, -0x30(%r1) | |
POWERPC_BCCTR(branchOptions, conditionRegisterBit, linked) // bcctr (bcctr 20, 0 == bctr) | |
}; | |
uint32_t* BranchAsm = preserveRegister ? BranchFarAsmPreserve : BranchFarAsm; | |
size_t BranchAsmSize = preserveRegister ? sizeof(BranchFarAsmPreserve) : sizeof(BranchFarAsm); | |
if (destination) | |
WriteProcessMemory(sys_process_getpid(), destination, BranchAsm, BranchAsmSize); | |
return BranchAsmSize; | |
} | |
size_t DetourHook::RelocateBranch(uint32_t* destination, uint32_t* source) | |
{ | |
uint32_t Instruction = *source; | |
uint32_t InstructionAddress = (uint32_t)source; | |
// Absolute branches dont need to be handled. | |
if (Instruction & POWERPC_BRANCH_ABSOLUTE) | |
{ | |
WriteProcessMemory(sys_process_getpid(), destination, &Instruction, sizeof(Instruction)); | |
return sizeof(Instruction); | |
} | |
int32_t BranchOffsetBitSize = 0; | |
int32_t BranchOffsetBitBase = 0; | |
uint32_t BranchOptions = 0; | |
uint8_t ConditionRegisterBit = 0; | |
switch (Instruction & POWERPC_OPCODE_MASK) | |
{ | |
// B - Branch | |
// [Opcode] [Address] [Absolute] [Linked] | |
// 0-5 6-29 30 31 | |
// | |
// Example | |
// 010010 0000 0000 0000 0000 0000 0001 0 0 | |
case POWERPC_OPCODE_B: | |
BranchOffsetBitSize = 24; | |
BranchOffsetBitBase = 2; | |
BranchOptions = POWERPC_BRANCH_OPTIONS_ALWAYS; | |
ConditionRegisterBit = 0; | |
break; | |
// BC - Branch Conditional | |
// [Opcode] [Branch Options] [Condition Register] [Address] [Absolute] [Linked] | |
// 0-5 6-10 11-15 16-29 30 31 | |
// | |
// Example | |
// 010000 00100 00001 00 0000 0000 0001 0 0 | |
case POWERPC_OPCODE_BC: | |
BranchOffsetBitSize = 14; | |
BranchOffsetBitBase = 2; | |
BranchOptions = (Instruction >> POWERPC_BIT32(10)) & MASK_N_BITS(5); | |
ConditionRegisterBit = (Instruction >> POWERPC_BIT32(15)) & MASK_N_BITS(5); | |
break; | |
} | |
// Even though the address part of the instruction begins from bit 29 in the case of bc and b. | |
// The value of the first bit is 4 as all addresses are aligned to for 4 for code therefore, | |
// the branch offset can be caluclated by anding in place and removing any suffix bits such as the | |
// link register or absolute flags. | |
int32_t BranchOffset = Instruction & (MASK_N_BITS(BranchOffsetBitSize) << BranchOffsetBitBase); | |
// Check if the MSB of the offset is set. | |
if (BranchOffset >> ((BranchOffsetBitSize + BranchOffsetBitBase) - 1)) | |
{ | |
// Add the nessasary bits to our integer to make it negative. | |
BranchOffset |= ~MASK_N_BITS(BranchOffsetBitSize + BranchOffsetBitBase); | |
} | |
void* BranchAddress = reinterpret_cast<void*>(InstructionAddress + BranchOffset); | |
return JumpWithOptions(destination, BranchAddress, Instruction & POWERPC_BRANCH_LINKED, true, BranchOptions, ConditionRegisterBit, POWERPC_REGISTERINDEX_R0); | |
} | |
size_t DetourHook::RelocateCode(uint32_t* destination, uint32_t* source) | |
{ | |
uint32_t Instruction = *source; | |
switch (Instruction & POWERPC_OPCODE_MASK) | |
{ | |
case POWERPC_OPCODE_B: // B BL BA BLA | |
case POWERPC_OPCODE_BC: // BEQ BNE BLT BGE | |
return RelocateBranch(destination, source); | |
default: | |
WriteProcessMemory(sys_process_getpid(), destination, &Instruction, sizeof(Instruction)); | |
return sizeof(Instruction); | |
} | |
} | |
void DetourHook::Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride) | |
{ | |
m_HookAddress = reinterpret_cast<void*>(fnAddress); | |
m_HookTarget = reinterpret_cast<void*>(*reinterpret_cast<uintptr_t*>(fnCallback)); | |
// Get the size of the hook but don't hook anything yet | |
size_t HookSize = GetHookSize(m_HookTarget, false, false); | |
// Save the original instructions for unhooking later on. | |
WriteProcessMemory(sys_process_getpid(), m_OriginalInstructions, m_HookAddress, HookSize); | |
m_OriginalLength = HookSize; | |
// Create trampoline and copy and fix instructions to the trampoline. | |
m_TrampolineAddress = &s_TrampolineBuffer[s_TrampolineSize]; | |
for (size_t i = 0; i < (HookSize / sizeof(uint32_t)); i++) | |
{ | |
uint32_t* InstructionAddress = reinterpret_cast<uint32_t*>((uint32_t)m_HookAddress + (i * sizeof(uint32_t))); | |
s_TrampolineSize += RelocateCode((uint32_t*)&s_TrampolineBuffer[s_TrampolineSize], InstructionAddress); | |
} | |
// Trampoline branches back to the original function after the branch we used to hook. | |
void* AfterBranchAddress = reinterpret_cast<void*>((uint32_t)m_HookAddress + HookSize); | |
s_TrampolineSize += Jump(&s_TrampolineBuffer[s_TrampolineSize], AfterBranchAddress, false, true); | |
// Finally write the branch to the function that we are hooking. | |
Jump(m_HookAddress, m_HookTarget, false, false); | |
m_TrampolineOpd[0] = reinterpret_cast<uint32_t>(m_TrampolineAddress); | |
m_TrampolineOpd[1] = tocOverride != 0 ? tocOverride : GetCurrentToc(); | |
} | |
bool DetourHook::UnHook() | |
{ | |
if (m_HookAddress && m_OriginalLength) | |
{ | |
WriteProcessMemory(sys_process_getpid(), m_HookAddress, m_OriginalInstructions, m_OriginalLength); | |
m_OriginalLength = 0; | |
m_HookAddress = nullptr; | |
return true; | |
} | |
return false; | |
} | |
ImportExportHook::ImportExportHook(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback) | |
: DetourHook(), m_LibaryName(libaryName), m_Fnid(fnid) | |
{ | |
HookByFnid(type, libaryName, fnid, fnCallback); | |
} | |
ImportExportHook::~ImportExportHook() | |
{ | |
UnHook(); | |
} | |
void ImportExportHook::Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride) | |
{ | |
// not implemented | |
} | |
bool ImportExportHook::UnHook() | |
{ | |
// not implemented | |
return false; | |
} | |
void ImportExportHook::HookByFnid(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback) | |
{ | |
opd_s* fnOpd = nullptr; | |
switch (type) | |
{ | |
case HookType::Import: | |
{ | |
fnOpd = FindImportByName(libaryName.c_str(), fnid); | |
break; | |
} | |
case HookType::Export: | |
{ | |
fnOpd = FindExportByName(libaryName.c_str(), fnid); | |
break; | |
} | |
} | |
if (fnOpd == nullptr) | |
return; | |
DetourHook::Hook(fnOpd->func, fnCallback, fnOpd->toc); | |
} | |
opd_s* ImportExportHook::FindExportByName(const char* module, uint32_t fnid) | |
{ | |
#ifdef VSH_PROC | |
uint32_t* segment15 = *reinterpret_cast<uint32_t**>(0x1008C); // 0x1008C or 0x10094 | |
uint32_t exportAdressTable = segment15[0x984 / sizeof(uint32_t)]; | |
exportStub_s* exportStub = reinterpret_cast<exportStub_s*>(exportAdressTable); | |
#else | |
uint32_t* sysProcessPrxInfo = *reinterpret_cast<uint32_t**>(0x101DC); // 0x101DC or 0x101E4 | |
uint32_t exportAdressTable = sysProcessPrxInfo[4]; | |
exportStub_s* exportStub = reinterpret_cast<exportStub_s*>(exportAdressTable); | |
#endif | |
while (exportStub->ssize == 0x1C00) | |
{ | |
if (!strcmp(module, exportStub->name)) | |
{ | |
for (int16_t i = 0; i < exportStub->exports; i++) | |
{ | |
if (exportStub->fnid[i] == fnid) | |
{ | |
return exportStub->stub[i]; | |
} | |
} | |
} | |
exportStub++; | |
} | |
return nullptr; | |
} | |
opd_s* ImportExportHook::FindImportByName(const char* module, uint32_t fnid) | |
{ | |
#ifdef VSH_PROC | |
uint32_t* segment15 = *reinterpret_cast<uint32_t**>(0x1008C); // 0x1008C or 0x10094 | |
uint32_t exportAdressTable = segment15[0x984 / sizeof(uint32_t)]; | |
importStub_s* importStub = reinterpret_cast<importStub_s*>(exportAdressTable); | |
#else | |
uint32_t* sysProcessPrxInfo = *reinterpret_cast<uint32_t**>(0x101DC); // 0x101DC or 0x101E4 | |
uint32_t importAdressTable = sysProcessPrxInfo[6]; | |
importStub_s* importStub = reinterpret_cast<importStub_s*>(importAdressTable); | |
#endif | |
while (importStub->ssize == 0x2C00) | |
{ | |
if (!strcmp(module, importStub->name)) | |
{ | |
for (int16_t i = 0; i < importStub->imports; i++) | |
{ | |
if (importStub->fnid[i] == fnid) | |
{ | |
return importStub->stub[i]; | |
} | |
} | |
} | |
importStub++; | |
} | |
return nullptr; | |
} |
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
/** | |
reference https://gist.github.com/iMoD1998/4aa48d5c990535767a3fc3251efc0348 (10x cleaner version than legacy detour class) | |
reference https://github.com/skiff/libpsutil/blob/master/libpsutil/system/memory.cpp (tocOverride idea) | |
reference https://pastebin.com/yezsesij (legacy detour class) | |
Check https://pastebin.com/VCfMb46E (for GetCurrentToc(), WriteProcessMemory, MARK_AS_EXECUTABLE) | |
TODO: | |
- make derive class for imports and exports | |
- make derive class for class symbol hooking. e.g: "17CNetworkObjectMgr" on gtav | |
- make derive class for hooking a single instruction b or bl | |
- make derive class for hooking a vtable functions or integrate with "class symbol hooking" | |
- ???? HookByFnid @ImportExportHook "not implemented" seriously whats the point of the derived class. <- that needs to be changed | |
*/ | |
#include <string> | |
struct opd_s | |
{ | |
uint32_t sub; | |
uint32_t toc; | |
}; | |
struct importStub_s | |
{ | |
int16_t ssize; | |
int16_t header1; | |
int16_t header2; | |
int16_t imports; | |
int32_t zero1; | |
int32_t zero2; | |
const char* name; | |
uint32_t* fnid; | |
opd_s** stub; | |
int32_t zero3; | |
int32_t zero4; | |
int32_t zero5; | |
int32_t zero6; | |
}; | |
struct exportStub_s | |
{ | |
int16_t ssize; | |
int16_t header1; | |
int16_t header2; | |
int16_t exports; // number of exports | |
int32_t zero1; | |
int32_t zero2; | |
const char* name; | |
uint32_t* fnid; | |
opd_s** stub; | |
}; | |
#define MARK_AS_EXECUTABLE __attribute__((section(".text"))) | |
class DetourHook | |
{ | |
public: | |
DetourHook(); | |
DetourHook(uintptr_t fnAddress, uintptr_t fnCallback); | |
DetourHook(DetourHook const&) = delete; | |
DetourHook(DetourHook&&) = delete; | |
DetourHook& operator=(DetourHook const&) = delete; | |
DetourHook& operator=(DetourHook&&) = delete; | |
virtual ~DetourHook(); | |
virtual void Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride = 0); | |
virtual bool UnHook(); | |
// also works | |
/*template<typename T> | |
T GetOriginal() const | |
{ | |
return T(m_TrampolineOpd); | |
}*/ | |
template <typename R, typename... TArgs> | |
R GetOriginal(TArgs... args) | |
{ | |
R(*original)(TArgs...) = (R(*)(TArgs...))m_TrampolineOpd; | |
return original(args...); | |
} | |
private: | |
/*** | |
* Writes an unconditional branch to the destination address that will branch to the target address. | |
* @param destination Where the branch will be written to. | |
* @param branchTarget The address the branch will jump to. | |
* @param linked Branch is a call or a jump? aka bl or b | |
* @param preserveRegister Preserve the register clobbered after loading the branch address. | |
* @returns size of relocating the instruction in bytes | |
*/ | |
size_t Jump(void* destination, const void* branchTarget, bool linked, bool preserveRegister); | |
/*** | |
* Writes both conditional and unconditional branches using the count register to the destination address that will branch to the target address. | |
* @param destination Where the branch will be written to. | |
* @param branchTarget The address the branch will jump to. | |
* @param linked Branch is a call or a jump? aka bl or b | |
* @param preserveRegister Preserve the register clobbered after loading the branch address. | |
* @param branchOptions Options for determining when a branch to be followed. | |
* @param conditionRegisterBit The bit of the condition register to compare. | |
* @param registerIndex Register to use when loading the destination address into the count register. | |
* @returns size of relocating the instruction in bytes | |
*/ | |
size_t JumpWithOptions(void* destination, const void* branchTarget, bool linked, bool preserveRegister, | |
uint32_t branchOptions, uint8_t conditionRegisterBit, uint8_t registerIndex); | |
/*** | |
* Copies and fixes relative branch instructions to a new location. | |
* @param destination Where to write the new branch. | |
* @param source Address to the instruction that is being relocated. | |
* @returns size of relocating the instruction in bytes | |
*/ | |
size_t RelocateBranch(uint32_t* destination, uint32_t* source); | |
/*** | |
* Copies an instruction enusuring things such as PC relative offsets are fixed. | |
* @param destination Where to write the new instruction(s). | |
* @param source Address to the instruction that is being copied. | |
* @returns size of relocating the instruction in bytes | |
*/ | |
size_t RelocateCode(uint32_t* destination, uint32_t* source); | |
/*** | |
* Get's size of method hook in bytes | |
* @param branchTarget The address the branch will jump to. | |
* @param linked Branch is a call or a jump? aka bl or b | |
* @param preserveRegister Preserve the register clobbered after loading the branch address. | |
* @returns size of hook in bytes | |
*/ | |
size_t GetHookSize(const void* branchTarget, bool linked, bool preserveRegister); | |
protected: | |
const void* m_HookTarget; // The funtion we are pointing the hook to. | |
void* m_HookAddress; // The function we are hooking. | |
uint8_t* m_TrampolineAddress; // Pointer to the trampoline for this detour. | |
uint32_t m_TrampolineOpd[2]; // opd_s of the trampoline for this detour. | |
uint8_t m_OriginalInstructions[30]; // Any bytes overwritten by the hook. | |
size_t m_OriginalLength; // The amount of bytes overwritten by the hook. | |
// Shared | |
MARK_AS_EXECUTABLE static uint8_t s_TrampolineBuffer[1024]; | |
static size_t s_TrampolineSize; | |
}; | |
// list of fnids https://github.com/aerosoul94/ida_gel/blob/master/src/ps3/ps3.xml | |
class ImportExportHook : public DetourHook | |
{ | |
public: | |
enum HookType { Import = 0, Export = 1 }; | |
public: | |
ImportExportHook(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback); | |
virtual ~ImportExportHook(); | |
virtual void Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride = 0) override; | |
virtual bool UnHook() override; | |
static opd_s* FindExportByName(const char* module, uint32_t fnid); | |
static opd_s* FindImportByName(const char* module, uint32_t fnid); | |
private: | |
void HookByFnid(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback); | |
private: | |
std::string m_LibaryName; | |
uint32_t m_Fnid; | |
}; |
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
#include "DetourHook.hpp" | |
// hook any function using detour class | |
DetourHook* StatSetIntHk; | |
uint32_t StatSetIntHook(uint32_t statHash, uint32_t value, uint32_t save) | |
{ | |
printf("StatSetIntHook\n"); | |
printf("statHash 0x%X\n", statHash); | |
return StatSetIntHk->GetOriginal<uint32_t>(statHash, value, save); | |
} | |
ImportExportHook* sysPpuThreadCreateHk; | |
int sysPpuThreadCreateHook(sys_ppu_thread_t* thread_id, void(*entry)(uint64_t), | |
uint64_t arg, int prio, size_t stacksize, uint64_t flags, const char* threadname) | |
{ | |
printf("sysPpuThreadCreateHook\n"); | |
printf("thread_id: 0x%016llX\n", thread_id); | |
printf("entry: 0x%016llX\n", entry); | |
printf("arg: 0x%016llX\n", arg); | |
printf("prio: 0x%X\n", prio); | |
printf("stacksize: 0x%X\n", stacksize); | |
printf("flags: 0x%016llX\n", flags); | |
printf("threadname: %s\n", threadname); | |
return sysPpuThreadCreateHk->GetOriginal<int>(thread_id, entry, arg, prio, stacksize, flags, threadname); | |
} | |
ImportExportHook* cellFsOpenHk; | |
CellFsErrno cellFsOpenHook(const char* path, int flags, int* fd, const void* arg, uint64_t size) | |
{ | |
printf("cellFsOpenHook\n"); | |
printf("path: %s\n", path); | |
printf("flags: 0x%X\n", flags); | |
printf("fd: 0x%X\n", fd); | |
printf("arg: 0x%X\n", arg); | |
printf("size: 0x%X\n", size); | |
if (strcmp(path, "/dev_hdd0/game/BLES01807/USRDIR/dlc/dlc_mpPilot/DLC.edat") == 0) | |
{ | |
path = "/dev_hdd0/tmp/Semjases/Mods/dlc/dlc_mpPilot/DLC.edat"; | |
} | |
return cellFsOpenHk->GetOriginal<CellFsErrno>(path, flags, fd, arg, size); | |
} | |
int main() | |
{ | |
// https://imgur.com/rdBtu09 | |
StatSetIntHk = new DetourHook(0x42BE0C, (uintptr_t)StatSetIntHook); | |
sysPpuThreadCreateHk = new ImportExportHook(ImportExportHook::Import, "sysPrxForUser", 0x24A1EA07, (uintptr_t)sysPpuThreadCreateHook); | |
cellFsOpenHk = new ImportExportHook(ImportExportHook::Import, "sys_fs", 0x718BF5F8, (uintptr_t)cellFsOpenHook); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment