/* * MIT License * * Copyright (c) 2017 Rokas Kupstys * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #pragma once #include #include #include #define HOOKER_ERROR (false) #define HOOKER_SUCCESS (true) #define HOOKER_MEM_R (1 << 0) #define HOOKER_MEM_W (1 << 1) #define HOOKER_MEM_X (1 << 2) #define HOOKER_MEM_RW (HOOKER_MEM_R|HOOKER_MEM_W) #define HOOKER_MEM_RX (HOOKER_MEM_R|HOOKER_MEM_X) #define HOOKER_MEM_RWX (HOOKER_MEM_R|HOOKER_MEM_W|HOOKER_MEM_X) /// Memory protection flags are platform specific (not a combination of above flags) and should not be converted. #define HOOKER_MEM_PLATFORM (1 << 31) /// Write a call instruction (5 bytes on x86/64). #define HOOKER_HOOK_CALL (1 << 0) /// Write a jump instruction (5 bytes on x86/64). #define HOOKER_HOOK_JMP (1 << 1) /// Use fat jump (16 for call and 14 bytes for jmp on x64). Has no effect on x86. #define HOOKER_HOOK_FAT (1 << 2) #if __cplusplus extern "C" { #endif /// Call any address with arbitrary amount of arguments. Returns value of specified type. /// \param returnType of return value. /// \param address of a call. #define hooker_call(returnType, address, ...) (returnType(*)(...))(address)(__VA_ARGS__); /// Call any address with arbitrary amount of arguments. Does not return anything. /// \param address of a call. #define hooker_callv(address, ...) (void(*)(...))(address)(__VA_ARGS__); /// Change protection of memory range. /// \param p memory address. /// \param size of memory at address p. /// \param protection a combination of HOOKER_MEM_* flags. /// \param original_protection on supported platforms will be set to current memory protection mode. May be null. If not null - always initialize to a best-guess current protection flags value, because on some platforms (like linux) this variable will not be set. bool hooker_mem_protect(void* p, size_t size, size_t protection, size_t* original_protection); /// Get mnemonic size of current platform. size_t hooker_get_mnemonic_size(void* address, size_t min_size); /// Hotpatch a call. void* hooker_hotpatch(void* location, void* new_proc); /// Unhotpatch a call. bool hooker_unhotpatch(void* location); /// Writes a jump or call hook from `address` to `new_proc`. /// \param address a pointer where hook should be written /// \param new_proc a pointer where hook should point to. /// \param flags any of HOOKER_HOOK_* flags. They may not be combined. /// \param nops of bytes to nop after hook instruction. Specify -1 to autocalculate. /// \returns null on failure or non-null on success. bool hooker_write_instruction(void* address, void* new_proc, size_t flags, size_t nops); /// Writes a 5 byte jump with E9 opcode. Difference between pointers is limited to 32bit. /// \param address a pointer where hook should be written /// \param new_proc a pointer where hook should point to. /// \returns null on failure or non-null on success. bool hooker_write_jmp(void* address, void* new_proc); /// Writes a 5 byte call with E8 opcode. Difference between pointers is limited to 32bit. /// \param address a pointer where hook should be written /// \param new_proc a pointer where hook should point to. /// \returns null on failure or non-null on success. bool hooker_write_call(void* address, void* new_proc); /// Redirect call to custom proc. /// \param address a start of original call. Warning: It should not contain any relatively-addressed instructions like calls or jumps. /// \param new_proc a proc that will be called instead of original one. /// \returns pointer, calling which will invoke original proc. It is user's responsibility to call original code when necessary. void* hooker_hook(void* address, void* new_proc, size_t flags); /// Unhook a hook created by hooker_hook(.., .., HOOKER_HOOK_REDIRECT, ..). /// \param address where hook was written to. /// \param original result of hooker_hook() call. void hooker_unhook(void* address, void* original); /// Return address in object's vmt which is pointing to specified method. /// \param object is a pointer to a c++ object. /// \param method is a pointer to a c++ object method. size_t* hooker_get_vmt_address(void* object, void* method); /// Find a first occurrence of memory pattern. /// \param start a pointer to beginning of memory range. /// \param size a size of memory range. If size is 0 then entire memory space will be searched. If pattern does not exist this will likely result in a crash. Negative size will search backwards. /// \param pattern a array of bytes to search for. /// \param pattern_len a length of pattern array. /// \param wildcard byte in the pattern array. It must be of same size as indicated by `pattern_len`. void* hooker_find_pattern(void* start, int size, const uint8_t* pattern, size_t pattern_len, uint8_t wildcard); /// Find a first occurrence of memory pattern. /// \param start a pointer to beginning of memory range. /// \param size a size of memory range. If size is 0 then entire memory space will be searched. If pattern does not exist this will likely result in a crash. Negative size will search backwards. /// \param pattern a array of bytes to search for. /// \param pattern_len a length of pattern array. /// \param wildcard array where values may be one of: 0? = 1, ?0 = 2, ?? = 3. void* hooker_find_pattern_ex(void* start, int size, const uint8_t* pattern, size_t pattern_len, const uint8_t* wildcard); /// Fill memory with nops (0x90 opcode). /// \param start of the memory address. /// \param size of the memory that will be filled. /// \returns HOOKER_SUCCESS or HOOKER_ERROR. bool hooker_nop(void* start, size_t size); /// Write bytes to specified memory address. /// \param start of the memory address. /// \param data to be written. /// \param size of data. bool hooker_write(void* start, void* data, size_t size); /// Searches for symbol in specified library. On Windows LoadLibrary() will be called if its not loaded yet, otherwise GetModuleHandle() will be used. /// On linux dlopen(RTLD_NODELETE) and dlclose() will always be called. /// \param lib_name string with dynamic library name. /// \param sym_name string with exported symbol name. /// \returns pointer to resolved dynamic symbol. void* hooker_dlsym(const char* lib_name, const char* sym_name); #if _WIN32 /// Replaces entry in import address table of specified module. /// \param mod_name string with name of module whose import table is to be modified. /// \param imp_mod_name string with name of module which module specified in `mod_name` imports. /// \param imp_proc_name string with name of symbol imported from module specified in `imp_mod_name`. /// \param new_proc a pointer that should replace old entry in import address table. /// \returns original value that was in import address table or null pointer on failure. void* hooker_hook_iat(const char* mod_name, const char* imp_mod_name, const char* imp_proc_name, void* new_proc); #endif // Define following macro in a single translation unit in order to use library without building it. #ifdef HOOKER_IMPLEMENTATION # include "hooker.c" #endif // HOOKER_IMPLEMENTATION #if __cplusplus }; #endif