commit 42ae9a90f0eb8a72fae9b6311533129f7ba82e33 Author: Redacted Date: Mon Jul 22 22:55:56 2024 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52c505d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.idea +/.cache +/.ccls-cache +/compile_commands.json +/cmake-build-debug +/build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..88f22c1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.18) +project(FunctionHook) +set(CMAKE_CXX_STANDARD 20) + +file(GLOB_RECURSE HEADERS "include/*.h") + +#TODO more architectures. +if (UNIX AND NOT APPLE) + file(GLOB_RECURSE SOURCES "src/linux64/*.cpp") + add_library(FunctionHook SHARED ${SOURCES}) +endif() + +if (WIN32) + file(GLOB_RECURSE SOURCES "src/windows/*.cpp") + add_library(FunctionHook STATIC ${SOURCES}) +endif() + +target_include_directories(FunctionHook PUBLIC ${PROJECT_SOURCE_DIR}/include) + +add_executable(FunctionHookDemo main.cpp) +target_link_libraries(FunctionHookDemo PUBLIC FunctionHook) + +set_target_properties(FunctionHook PROPERTIES LINKER_LANGUAGE CXX) +set_target_properties(FunctionHookDemo PROPERTIES LINKER_LANGUAGE CXX) \ No newline at end of file diff --git a/include/FunctionHook/Hook.h b/include/FunctionHook/Hook.h new file mode 100644 index 0000000..785c69c --- /dev/null +++ b/include/FunctionHook/Hook.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace FunctionHooking { + class Detour { + private: + void* source = nullptr; //The function to be hooked. + void* destination = nullptr; //The function we are redirecting execution to. + uint8_t overwritten_bytes[13] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t hook_bytes[13] = {0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x41, 0xFF, 0xE2}; + void createHook(void* source_address, void* destination_address); + public: + void removeHook(); + Detour(void* source_address, void* destination_address); + Detour() = default; + + //TODO This is *technically* not thread safe. + template + inline typename std::enable_if::value, T>::type callOriginal(Args... args) { + removeHook(); + const T result = reinterpret_cast(source)(args...); + createHook(source, destination); + return result; + } + + template + inline typename std::enable_if::value, void>::type callOriginal(Args... args) { + removeHook(); + reinterpret_cast(source)(args...); + createHook(source, destination); + } + }; +} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..4363f78 --- /dev/null +++ b/main.cpp @@ -0,0 +1,38 @@ +#include +#include + +using namespace FunctionHooking; + +Detour voidDetour; +Detour stringDetour; + +void original_void() { + std::cout << "Original void function." << std::endl; +} + +void hook_void() { + std::cout << "Void hook function." << std::endl; + voidDetour.callOriginal(); +} + +std::string original_string(const std::string& string) { + std::cout << string + " original function." << std::endl; + return string; +} + +std::string hook_string(const std::string& string) { + std::cout << string + " hook function." << std::endl; + + return stringDetour.callOriginal(string); +} + +int main() { + //Set up hooks. + voidDetour = FunctionHooking::Detour((void*) original_void, (void*) hook_void); + stringDetour = FunctionHooking::Detour((void*) original_string, (void*) hook_string); + + original_void(); + std::cout << std::endl; + original_string("String"); + return 0; +} \ No newline at end of file diff --git a/src/linux64/Hook.cpp b/src/linux64/Hook.cpp new file mode 100644 index 0000000..27298ea --- /dev/null +++ b/src/linux64/Hook.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +void FunctionHooking::Detour::createHook(void* source_address, void* destination_address) { + source = source_address; + destination = destination_address; + + //Save the bytes to be overwritten from the function prelude. + memcpy(&overwritten_bytes, source_address, 13); + + //Put the destination address into the jump. + memcpy(&hook_bytes[2], &destination_address, sizeof(void*)); + + //Make the target memory page writable. + mprotect((void*)((uintptr_t)source_address & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_WRITE|PROT_EXEC); + + //Write the jmp to the beginning of the function prelude. + memcpy(source_address, hook_bytes, sizeof(hook_bytes)); + + //Make the memory page non-writable again. + mprotect((void*)((uintptr_t)source_address & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_EXEC); +} + +void FunctionHooking::Detour::removeHook() { + + //Make the target memory page writable. + mprotect((void*)((uintptr_t)source & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_WRITE|PROT_EXEC); + + //Replace our hook with the bytes that were originally there. + memcpy(source, &overwritten_bytes, 13); + + //Make the memory page non-writable again. + mprotect((void*)((uintptr_t)source & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_EXEC); +} + +FunctionHooking::Detour::Detour(void *source_address, void *destination_address) { + createHook(source_address, destination_address); +}