/** 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 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 T GetOriginal() const { return T(m_TrampolineOpd); }*/ template 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; };