diff options
Diffstat (limited to 'libs/usvfs/src/thooklib/ttrampolinepool.h')
| -rw-r--r-- | libs/usvfs/src/thooklib/ttrampolinepool.h | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/libs/usvfs/src/thooklib/ttrampolinepool.h b/libs/usvfs/src/thooklib/ttrampolinepool.h new file mode 100644 index 0000000..5b44c51 --- /dev/null +++ b/libs/usvfs/src/thooklib/ttrampolinepool.h @@ -0,0 +1,222 @@ +/* +Userspace Virtual Filesystem + +Copyright (C) 2015 Sebastian Herbord. All rights reserved. + +This file is part of usvfs. + +usvfs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +usvfs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with usvfs. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include "asmjit_sane.h" +#include <logging.h> +#include <map> +#include <vector> +#include <windows_sane.h> + +#ifdef _MSC_VER +#pragma warning(disable : 4714) +#include <boost/config/compiler/visualc.hpp> +#else +#include <boost/config/compiler/gcc.hpp> +#endif +#include <boost/predef.h> +#include <boost/thread.hpp> +#include <mutex> + +// #include <boost/thread/mutex.hpp> + +namespace HookLib +{ + +/// +/// trampolines are runtime-generated mini-functions that are used to call the +/// original code of a function +/// +class TrampolinePool +{ +public: + /// Call initialize before you use the TrampolinePool + static void initialize(); + + static TrampolinePool& instance() + { + // This is a very very sensitive place so we want to keep this function as simple as + // possible and having it inlined probably cann't hurt + return *s_Instance; + } + + void setBlock(bool block); + + /// + /// store a stub without moving code from the original function. This is used in cases + /// where the hook can be placed without overwriting logic (i.e. hot-patchable + /// functions and when chaining hooks) + /// \param reroute the stub function to call before the regular function (on x86 this + /// needs to be cdecl calling convention!) + /// \param original the original function + /// \param returnAddress address under which the original functionality can be + /// reached. + /// for the first hook this should be (original + 2), otherwise + /// the address of the next hook in the chain + /// \return address of the created trampoline function + /// + LPVOID storeStub(LPVOID reroute, LPVOID original, LPVOID returnAddress); + + /// + /// store a stub, moving part of the original function to the trampoline + /// \param reroute the stub function to call before the regular function (on x86 this + /// needs to be cdecl calling convention!) + /// \param original the original function + /// \param preambleSize number of bytes from the original function to backup. Needs to + /// correspond to complete instructions + /// \param rerouteOffset offset in bytes from the created trampoline to the preamble + /// that leads back to the original code + /// \return address of the created trampoline function + /// + LPVOID storeStub(LPVOID reroute, LPVOID original, size_t preambleSize, + size_t* rerouteOffset); + + /// + /// store a trampoline for hot-patchable functions, where the original function + /// is unharmed. + /// \param reroute the reroute function + /// \param original original function + /// \param returnAddress address under which the original functionality can be + /// reached. + /// \return address of the trampoline function + /// + LPVOID storeTrampoline(LPVOID reroute, LPVOID original, LPVOID returnAddress); + + /// + /// store a trampoline, copying a part of the original function to the trampoline. + /// This is used for the case where the hooking mechanism needs to overwrite part of + /// the function + /// \param reroute the reroute function + /// \param original original function + /// \param preambleSize number of bytes from the original function to backup. Needs to + /// correspond to complete instructions + /// \param rerouteOffset offset in bytes from the created trampoline to the preamble + /// that leads us back to the original code + /// \return address of the trampoline function + /// + LPVOID storeTrampoline(LPVOID reroute, LPVOID original, size_t preambleSize, + size_t* rerouteOffset); + + /// + /// \param addressNear used to find a trampoline buffer near the jump instruction + /// \return retrieve address of current trampoline buffer + /// + LPVOID currentBufferAddress(LPVOID addressNear); + + /// + /// \brief forces the barrier(s) for the current thread to be released + /// + void forceUnlockBarrier(); + +private: + struct BufferList + { + size_t offset; + std::vector<LPVOID> buffers; + }; + + typedef std::map<LPVOID, BufferList> BufferMap; + static const intptr_t ADDRESS_MASK = + 0xFFFFFFFFFF000000LL; // mask to "round" addresses to consolidate near + // trampolines + +private: + TrampolinePool(); + + TrampolinePool& operator=(const TrampolinePool& reference); // not implemented + + /** + * @brief allocates a buffer with read, write and execute rights near the + * specified adress. The purpose is that we want to be able to jump from + * adressNear to generated code with a 5-byte jump, even on x64 systems. + * @param addressNear the reference adress + * @note the resulting buffer is stored in the m_Buffers map + */ + BufferMap::iterator allocateBuffer(LPVOID addressNear); + + void addBarrier(LPVOID rerouteAddr, LPVOID original, asmjit::X86Assembler& assembler); + +#if BOOST_ARCH_X86_64 + void copyCode(asmjit::X86Assembler& assembler, LPVOID source, size_t numBytes); +#endif // BOOST_ARCH_X86_64 + + BufferList& getBufferList(LPVOID address); + + LPVOID roundAddress(LPVOID address) const; + +public: + static LPVOID __stdcall barrier(LPVOID function); + static LPVOID __stdcall release(LPVOID function); + + LPVOID barrierInt(LPVOID function); + LPVOID releaseInt(LPVOID function); + + void addCallToStub(asmjit::X86Assembler& assembler, LPVOID original, LPVOID reroute); + +private: + /// + /// \brief add a jump to an address outside the custom generated asm code (without + /// modifying registers) + /// \param assembler the assembler generator to write to + /// \param destination destination address + /// \note this currently generates a lot code on x64, may be overly complicated + /// + void addAbsoluteJump(asmjit::X86Assembler& assembler, uint64_t destination); + + DWORD determinePageSize(); + +private: +#if BOOST_ARCH_X86_64 + static const int SIZE_OF_JUMP = 13; +#elif BOOST_ARCH_X86_32 + static const int SIZE_OF_JUMP = 5; +#endif + + // the hook lib tries to allocate buffer close to the hooked functions, + // for 32-bits application, this often falls on 0x40000000, which is the + // address at which DLLs can be loaded on Windows + // + // some old-style DRM (e.g. Dragon Age 2) check that address and prevent + // the game from running if it's not 0x40000000, so we give them a bit of + // spaces before allocating our buffers + // + static constexpr uintptr_t MIN_ALLOC_ADDR = 0x40000000 + 0x200000; + + static TrampolinePool* s_Instance; + + bool m_FullBlock{false}; + + BufferMap m_Buffers; + + typedef std::map<void*, void*> TThreadMap; + boost::thread_specific_ptr<TThreadMap> m_ThreadGuards; + + LPVOID m_BarrierAddr; + LPVOID m_ReleaseAddr; + + DWORD m_BufferSize = {1024}; + size_t m_SearchRange; + uint64_t m_AddressMask; + + int m_MaxTrampolineSize; +}; + +} // namespace HookLib |
