diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b93c32..a18a804 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,8 @@ -cmake_minimum_required(VERSION 2.6) -project(nekohack) +cmake_minimum_required(VERSION 3.14) +project(crackem) -set(CMAKE_CXX_STANDARD 17) -file(GLOB_RECURSE NEKOHACK_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") -add_library(nekohack STATIC ${NEKOHACK_SOURCES}) -target_include_directories(nekohack PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/hack") -target_include_directories(nekohack PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/") -target_link_libraries(nekohack PUBLIC dl) +set(CRACKEM_BUILD_SHARED OFF CACHE BOOL "Build the shared library") +set(CRACKEM_BUILD_STATIC ON CACHE BOOL "Build the static library") -add_library(nekohack-dummy SHARED "${CMAKE_CURRENT_SOURCE_DIR}/example/dummy.cpp") -add_executable(nekohack-example "${CMAKE_CURRENT_SOURCE_DIR}/example/main.cpp") -target_include_directories(nekohack-example PRIVATE nekohack) -target_link_libraries(nekohack-example PRIVATE nekohack nekohack-dummy) +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/") diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..0ff792b --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,27 @@ +[ +{ + "directory": "/home/julianacat/github/crackem/build/src", + "command": "/usr/bin/clang++ -I/home/julianacat/github/crackem/src -I/home/julianacat/github/crackem/src/.. -std=gnu++17 -o CMakeFiles/crackem_static.dir/encrypt.cpp.o -c /home/julianacat/github/crackem/src/encrypt.cpp", + "file": "/home/julianacat/github/crackem/src/encrypt.cpp" +}, +{ + "directory": "/home/julianacat/github/crackem/build/src", + "command": "/usr/bin/clang++ -I/home/julianacat/github/crackem/src -I/home/julianacat/github/crackem/src/.. -std=gnu++17 -o CMakeFiles/crackem_static.dir/memory.cpp.o -c /home/julianacat/github/crackem/src/memory.cpp", + "file": "/home/julianacat/github/crackem/src/memory.cpp" +}, +{ + "directory": "/home/julianacat/github/crackem/build/src", + "command": "/usr/bin/clang++ -I/home/julianacat/github/crackem/src -I/home/julianacat/github/crackem/src/.. -std=gnu++17 -o CMakeFiles/crackem_static.dir/sharedlibrary.cpp.o -c /home/julianacat/github/crackem/src/sharedlibrary.cpp", + "file": "/home/julianacat/github/crackem/src/sharedlibrary.cpp" +}, +{ + "directory": "/home/julianacat/github/crackem/build/src", + "command": "/usr/bin/clang++ -I/home/julianacat/github/crackem/src -I/home/julianacat/github/crackem/src/.. -std=gnu++17 -o CMakeFiles/crackem_static.dir/signature.cpp.o -c /home/julianacat/github/crackem/src/signature.cpp", + "file": "/home/julianacat/github/crackem/src/signature.cpp" +}, +{ + "directory": "/home/julianacat/github/crackem/build/src", + "command": "/usr/bin/clang++ -I/home/julianacat/github/crackem/src -I/home/julianacat/github/crackem/src/.. -std=gnu++17 -o CMakeFiles/crackem_static.dir/vfthook.cpp.o -c /home/julianacat/github/crackem/src/vfthook.cpp", + "file": "/home/julianacat/github/crackem/src/vfthook.cpp" +} +] \ No newline at end of file diff --git a/example.cpp b/example.cpp new file mode 100644 index 0000000..e69de29 diff --git a/example/dummy.cpp b/example/dummy.cpp deleted file mode 100644 index 3abcaf3..0000000 --- a/example/dummy.cpp +++ /dev/null @@ -1,8 +0,0 @@ - -extern "C" { - -int GetInt() { - return 5; -} - -} diff --git a/example/main.cpp b/example/main.cpp deleted file mode 100644 index 74f548a..0000000 --- a/example/main.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include -#include -using namespace neko; - -int main() { - hack::SharedLibrary dummysl("libnekohack-dummy"); - std::cout << "Lmap: " << dummysl.GetLMap() << std::endl; - - using GetInt_f = int (*)(); - GetInt_f GetInt = dummysl.GetSym("GetInt"); - std::cout << "GetInt Location: " << std::hex << GetInt << std::endl; - std::cout << "GetInt: " << GetInt() << std::endl; - -} diff --git a/include/hack/asmhook.hpp b/include/hack/asmhook.hpp deleted file mode 100644 index 7d27251..0000000 --- a/include/hack/asmhook.hpp +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Nekohack: Break things in cool ways! - * Copyright (C) 2018 Rebekah Rowe - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -namespace neko::hack { - -// Overwrites calls in a function to the destination -/*class Overwrite { -public: - Overwrite(){} - Overwrite(void* func, std::size_t length, void* dest) { - this->Hook(func, length, dest); - } - Hook(void* func, std::size_t length, void* dest) { - - } -private: - void* overwritten = nullptr; -}; - -class ASMHook { -public: - Hook(); - ~Hook(); - enum Type {kOverwrite - AddJump -};*/ - -} diff --git a/include/hack/vfthook.hpp b/include/hack/vfthook.hpp deleted file mode 100644 index a901033..0000000 --- a/include/hack/vfthook.hpp +++ /dev/null @@ -1,59 +0,0 @@ - -/* - * Nekohack: Break things in cool ways! - * Copyright (C) 2018 Rebekah Rowe - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -#include -#include - -namespace neko::hack { - -// Virtual function table -using VF = void*; -using VFT = VF[]; - -// Virtual Function Table Hook -class VFTHook { -public: - VFTHook(); - ~VFTHook(); - template - void Init(TPtr instance) { - this->IntInit(reinterpret_cast(instance)); - } - template - void Hook(TPtr func, std::size_t idx) { - this->IntHook(reinterpret_cast(func), idx); - } - void Finish(); - void Undo(); - template - inline T GetOriginal(std::size_t idx) const { - return reinterpret_cast(this->original_data[idx]); - } - bool IsHooked(); - std::size_t GetSize(){ return this->size; } -private: - void IntInit(void* instance); - void IntHook(VF func_ptr, std::size_t); - void*** vtable = nullptr; - void** original_data = nullptr; - std::unique_ptr replacement; - std::size_t size; -}; - -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..1b1e789 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,32 @@ + +file(GLOB_RECURSE sources "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +file(GLOB_RECURSE headers "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp") + +add_custom_target(crackem) + +macro(ApplyCommon in_target) + add_dependencies(crackem ${in_target}) + set_property(TARGET ${in_target} PROPERTY OUTPUT_NAME crackem) + set_property(TARGET ${in_target} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + set_property(TARGET ${in_target} PROPERTY CXX_STANDARD 17) + + target_include_directories(${in_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/") + target_include_directories(${in_target} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../") + if (UNIX AND NOT APPLE) + target_link_libraries(${in_target} PUBLIC rt pthread) + endif () + + install(TARGETS ${in_target} DESTINATION lib) +endmacro() + +if(CRACKEM_BUILD_SHARED) + add_library(crackem_shared SHARED ${sources}) + ApplyCommon(crackem_shared) +endif(CRACKEM_BUILD_SHARED) +if(CRACKEM_BUILD_STATIC) + add_library(crackem_static STATIC ${sources}) + ApplyCommon(crackem_static) +endif(CRACKEM_BUILD_STATIC) + + +install(FILES ${headers} DESTINATION include/crackem) diff --git a/src/asmhook.hpp b/src/asmhook.hpp index 473b66d..4e0acde 100644 --- a/src/asmhook.hpp +++ b/src/asmhook.hpp @@ -1,6 +1,6 @@ /* - * Nekohack: Break things in cool ways! + * Crackem: Break things in cool ways! * Copyright (C) 2018 Rebekah Rowe * * This program is free software: you can redistribute it and/or modify @@ -17,10 +17,30 @@ * along with this program. If not, see . */ -#include +#pragma once -namespace neko::hack { +namespace crackem { -RAsmOp* asm = r_asm_op_new(void); +// Overwrites calls in a function to the destination +/*class Overwrite { +public: + Overwrite(){} + Overwrite(void* func, std::size_t length, void* dest) { + this->Hook(func, length, dest); + } + Hook(void* func, std::size_t length, void* dest) { + + } +private: + void* overwritten = nullptr; +}; + +class ASMHook { +public: + Hook(); + ~Hook(); + enum Type {kOverwrite + AddJump +};*/ } diff --git a/include/hack/memory.hpp b/src/memory.hpp similarity index 93% rename from include/hack/memory.hpp rename to src/memory.hpp index ec9d995..6f8e8aa 100644 --- a/include/hack/memory.hpp +++ b/src/memory.hpp @@ -19,10 +19,10 @@ #include -namespace neko::hack::mem { +namespace crackem::mem { template -inline auto Offset(Ptr src, std::ptrdiff_t offset) { +inline auto Offset(Ptr* src, std::ptrdiff_t offset) { return reinterpret_cast(reinterpret_cast(src) + offset); } diff --git a/src/sharedlibrary.cpp b/src/sharedlibrary.cpp index 6655c3f..038baa6 100644 --- a/src/sharedlibrary.cpp +++ b/src/sharedlibrary.cpp @@ -17,97 +17,110 @@ * along with this program. If not, see . */ +#include #include +#include #include "memory.hpp" #include "sharedlibrary.hpp" -namespace neko::hack { +namespace crackem { -struct Passthrough { - std::string name; - fs::path path; - void* begin; - std::size_t size; -}; - -SharedLibrary::SharedLibrary(){} -SharedLibrary::SharedLibrary(std::string_view _name) : name(_name) { - try { - this->Init(_name); - } catch(...){} -} - -void SharedLibrary::Init(std::string_view _name){ - if (!init_flag) { - this->name = _name; - if (this->name.empty()) - throw std::runtime_error("SharedLibrary: no name avaiable."); - Passthrough pass; - pass.name = '/' + std::string(_name) + '.'; - if (!dl_iterate_phdr([](struct dl_phdr_info* info, size_t, void* _pass) { - auto* pass = reinterpret_cast(_pass); - if (!strstr(info->dlpi_name, pass->name.c_str())) - return 0; - - pass->path = info->dlpi_name; - pass->begin = reinterpret_cast(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr); - pass->size = info->dlpi_phdr[0].p_memsz; - return 1; - }, reinterpret_cast(&pass))) - throw std::runtime_error("SharedLibrary: Unable to find loaded library"); - - this->path = pass.path; - this->_begin = pass.begin; - this->_end = mem::Offset(pass.begin, pass.size); - this->_size = pass.size; - - this->lmap = static_cast(dlopen(this->path.c_str(), RTLD_NOLOAD)); - if (const char* error = dlerror()) - throw std::runtime_error("SharedLibrary recieved dlerror: " + std::string(error)); - - this->init_flag = true; +SharedLibrary::SharedLibrary() : loaded(false) {} +SharedLibrary::SharedLibrary(std::string_view _name) { this->Load(_name); } +SharedLibrary::SharedLibrary(const SharedLibrary& v) { *this = v; } +SharedLibrary::SharedLibrary(SharedLibrary&& v) { *this = v; } +SharedLibrary& SharedLibrary::operator=(SharedLibrary&& v) { + this->loaded = false; + this->name = v.name; + if (v.loaded) { + v.loaded = false; + this->path = std::move(v.path); + this->_begin = v._begin; + this->_end = v._end; + this->_size = v._size; + this->lmap = v.lmap; + this->loaded = true; } + return *this; } -void SharedLibrary::ForceInit(std::string_view _name) { - while(!this->init_flag) { - try { - this->Init(); - } catch(...) {} +SharedLibrary& SharedLibrary::operator=(const SharedLibrary& v) { + this->loaded = false; + this->name = v.name; + if (v.loaded) { + this->path = v.path; + this->_begin = v._begin; + this->_end = v._end; + this->_size = v._size; + this->lmap = v.lmap; + this->loaded = true; } + return *this; } -std::string_view SharedLibrary::GetName() { - return name; +void SharedLibrary::Load(std::string_view _name) { + this->name = _name; + this->Load(); } -void* SharedLibrary::begin() { - this->Init(); +void SharedLibrary::Load() { + assert(!this->loaded && !this->name.empty()); + this->loaded = false; + + if (!dl_iterate_phdr([](struct dl_phdr_info* info, size_t, void* _pass) { + auto* _this = reinterpret_cast(_pass); + _this->path = info->dlpi_name; + if (_this->path.stem() != _this->name/* || _this->path.extention() ==*/) + return 0; + + _this->_begin = reinterpret_cast(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr); + _this->_size = info->dlpi_phdr[0].p_memsz; + return 1; + }, reinterpret_cast(this))) + return; + + this->loaded = true; + this->_end = mem::Offset(this->_begin, this->_size); + this->lmap = static_cast(dlopen(this->path.c_str(), RTLD_NOLOAD)); + if (const char* error = dlerror()) + throw std::runtime_error("SharedLibrary recieved dlerror(dlopen): " + std::string(error)); +} +bool SharedLibrary::IsLoaded() const { + return this->loaded; +} +std::string_view SharedLibrary::GetName() const { + assert(this->loaded); + return this->name; +} +const fs::path& SharedLibrary::GetPath() const { + assert(this->loaded); + return this->path; +} +void* SharedLibrary::begin() const { + assert(this->loaded); return this->_begin; } -void* SharedLibrary::end() { - this->Init(); +void* SharedLibrary::end() const { + assert(this->loaded); return this->_end; } -std::size_t SharedLibrary::size() { - this->Init(); +std::size_t SharedLibrary::size() const { + assert(this->loaded); return this->_size; } -LMap SharedLibrary::GetLMap() { - this->Init(); +Handle SharedLibrary::GetHandle() const { + assert(this->loaded); return this->lmap; } -void SharedLibrary::Clear(){ - this->init_flag = false; - this->name = std::string_view(); -} -void* SharedLibrary::GetSymInternal(SymStr name) { +void* SharedLibrary::GetSymInternal(SymStr name) const { #if defined(__linux__) - void* symbol = dlsym(this->GetLMap(), name); + void* symbol = dlsym(this->GetHandle(), name); if (const char* error = dlerror()) - throw std::runtime_error("SharedLibrary recieved dlerror: " + std::string(error)); + throw std::runtime_error("SharedLibrary recieved dlerror(dlsym): " + std::string(error)); return symbol; #elif defined(_WIN32) return GetProcAddress(this->GetLMap(), name); + #else + assert(false); #endif } diff --git a/include/hack/sharedlibrary.hpp b/src/sharedlibrary.hpp similarity index 67% rename from include/hack/sharedlibrary.hpp rename to src/sharedlibrary.hpp index 9a1f4ae..08aa66d 100644 --- a/include/hack/sharedlibrary.hpp +++ b/src/sharedlibrary.hpp @@ -21,45 +21,53 @@ #if defined(__linux__) #include // link maps -using LMap = link_map*; +using Handle = link_map*; using SymStr = const char*; #elif defined(_WIN32) #include // loadlibrary -using LMap = HMODULE; +using Handle = HMODULE; using SymStr = LPCSTR; #endif #include #include -namespace fs = std::filesystem; -namespace neko::hack { +namespace crackem { +namespace fs = std::filesystem; class SharedLibrary { public: SharedLibrary(); SharedLibrary(std::string_view _name); - void Init() { this->Init(this->name); } - void ForceInit() { this->ForceInit(this->name); } - void Init(std::string_view _name); - void ForceInit(std::string_view _name); - std::string_view GetName(); - void* begin(); - void* end(); - std::size_t size(); - void Clear(); - LMap GetLMap(); + SharedLibrary(const SharedLibrary&); + SharedLibrary(SharedLibrary&&); + void Load(); + void Load(std::string_view _name); + + SharedLibrary& operator=(SharedLibrary&& v); + SharedLibrary& operator=(const SharedLibrary& v); + + bool IsLoaded() const; + std::string_view GetName() const; + const fs::path& GetPath() const; + void* begin() const; + void* end() const; + std::size_t size() const; + Handle GetHandle() const; + + void Clear() const; template T GetSym(SymStr s) { return reinterpret_cast(this->GetSymInternal(s)); } private: - void* GetSymInternal(SymStr); - bool init_flag = false; + void* GetSymInternal(SymStr) const; + + bool loaded; std::string_view name; fs::path path; void* _begin; void* _end; std::size_t _size; - LMap lmap; + Handle lmap; }; } diff --git a/src/signature.cpp b/src/signature.cpp index c1064f0..632fee2 100644 --- a/src/signature.cpp +++ b/src/signature.cpp @@ -24,58 +24,41 @@ #include "signature.hpp" -class Pattern { -public: - Pattern(std::string_view input) { - while (!input.empty()) { - // Discard whitespace - std::size_t non_white_start = input.find_first_not_of(' '); - if (non_white_start == std::string_view::npos) break; - input.remove_prefix(non_white_start); +namespace crackem::sig { - // act on delim - // Wildcard - if (input.front() == '?') { - this->wilds.push_back(true); - std::size_t next = input.find_first_not_of('?'); - if (next == std::string_view::npos) - break; - input.remove_prefix(next); - } else { - this->wilds.push_back(false); - if (input.size() < 2) - break; +// Original function by learn_more -- https://github.com/learn-more/findpattern-bench +static bool InRange(uint8_t x, uint8_t a, uint8_t b) { return x >= a && x <= b; } +static uint8_t GetBits(uint8_t x) { return InRange((x & (~0x20)), 'A', 'F') ? ((x & (~0x20)) - 'A' + 0xA): (InRange(x, '0', '9') ? x - '0': 0); } +static uint8_t GetByte(const char* x) { return GetBits(x[0]) << 4 | GetBits(x[1]); } - std::string hex = {input[0], input[1]}; - uint8_t nibble = std::stoul(hex, nullptr, 16); - this->pat.push_back(*reinterpret_cast(nibble)); - input.remove_prefix(2); - } - } - } - inline std::byte Get(std::size_t i) { - assert(!this->IsWild(i)); - return pat[i]; - } - inline bool IsWild(std::size_t i) { - auto find = std::find(wilds.begin(), wilds.end(), i); - return find != wilds.end(); - } - inline std::size_t size(){ - return this->pat.size(); - } -private: -std::vector pat; -std::vector wilds; -}; +uintptr_t FindPattern(uintptr_t start_address, uintptr_t end_address, const char* target_pattern) { + const char* pattern = target_pattern; + + uintptr_t first_match = 0; + + for (uintptr_t position = start_address; position < end_address; position++) { + if (!*pattern) + return first_match; + + const uint8_t pattern_current = *reinterpret_cast(pattern); + const uint8_t memory_current = *reinterpret_cast(position); + + if (pattern_current == '\?' || memory_current == GetByte(pattern)) { + if (!first_match) + first_match = position; + + if (!pattern[2]) + return first_match; + + pattern += pattern_current != '\?' ? 3 : 2; + } + else { + pattern = target_pattern; + first_match = 0; + } + } + + return NULL; +} -void* SearchN(void* _begin, void* _end, std::string_view _pat){ - return nullptr; - /*Pattern pat(_pat); - std::byte* begin = reinterpret_cast(_begin); - std::byte* end = reinterpret_cast(_end); - auto find = std::find_if(begin, end, [&](std::byte* s) { - // Pattern equality - for (int ) - });*/ } diff --git a/include/hack/signature.hpp b/src/signature.hpp similarity index 86% rename from include/hack/signature.hpp rename to src/signature.hpp index b5e38f6..9e2ef0f 100644 --- a/include/hack/signature.hpp +++ b/src/signature.hpp @@ -21,8 +21,8 @@ #include -namespace neko::hack::sig { +namespace crackem::sig { -void* SearchN(void* where, std::size_t len, std::string_view pat); +uintptr_t FindPattern(uintptr_t start_address, uintptr_t end_address, const char* target_pattern); } diff --git a/src/vfthook.cpp b/src/vfthook.cpp index 58e4221..1d200d9 100644 --- a/src/vfthook.cpp +++ b/src/vfthook.cpp @@ -1,50 +1,74 @@ #include +#include "memory.hpp" #include "vfthook.hpp" -namespace neko::hack { +namespace crackem::vft::hook { -VFTHook::VFTHook(){} -VFTHook::~VFTHook() { - try { - this->Undo(); - }catch(...){} -} - -void VFTHook::IntInit(void* instance) { - try { - this->Undo(); - }catch(...){} - - this->vtable = reinterpret_cast(instance); - this->original_data = *this->vtable; - - this->size = 0; - while (this->original_data[this->size]) this->size++; - - this->replacement = std::make_unique(this->size); - memcpy(this->replacement.get(), this->original_data, sizeof(VF) * this->size); +struct Ref::HookData { + bool Verify() { + return this->magic = kMagic; + } + static HookData* Get(VInst* v) { + return mem::Offset(*v, -int(sizeof(HookData))); + } + VInst* instance; + VFT* original; + std::size_t size; + constexpr static uint32_t kMagic = 0xCAFEBEEF; + uint32_t magic; + VF* replacement[]; }; -void VFTHook::Finish() { - if (this->IsHooked()) - throw std::logic_error("VTFHook: Cant finish, hook in place"); - *this->vtable = this->replacement.get(); -} -void VFTHook::Undo() { - if (!this->IsHooked()) - throw std::logic_error("VTFHook: Cant undo a hook that isnt there"); -} -bool VFTHook::IsHooked() { - if (!this->vtable || !this->replacement) - return false; - return *this->vtable == this->replacement.get(); -} -void VFTHook::IntHook(VF func, std::size_t idx) { - //if (this->original[idx] != this->replacement[idx]) - //throw std::logic_error("Already Hooked"); - this->replacement[idx] = func; +Ref::HookData* Ref::IntCreate(VInst* v) { + if (HookData::Get(v)->Verify()) + throw std::runtime_error("Hook already exists!"); + + std::size_t size = 0; + while ((*v)[size]) + size++; + + HookData* hd = reinterpret_cast(malloc(sizeof(HookData) + (sizeof(VF*) * size))); + hd->instance = v; + hd->original = *v; + hd->size = size; + hd->magic = HookData::kMagic; + memcpy(hd->replacement, hd->original, sizeof(VF*) * size); + + *hd->instance = hd->replacement; + return hd; +} +Ref::HookData* Ref::IntGet(VInst* v) { + HookData* r = HookData::Get(v); + if (r->Verify()) + return r; + else + throw std::runtime_error("Hook not found!"); +} +bool Ref::IntContains(VInst* v) { + return HookData::Get(v)->Verify(); +} +void Ref::IntReplace(int idx, VF* fn) const { + assert(this->data && !this->Exists(idx)); + this->data->replacement[idx] = fn; +} +VF* Ref::IntGetOriginal(int idx) const { + assert(this->data); + return this->data->original[idx]; +} +void Ref::Clear(int idx) const { + assert(this->data && this->Exists(idx)); + this->data->replacement[idx] = this->data->original[idx]; +} +bool Ref::Exists(int idx) const { + assert(this->data); + return this->data->original[idx] == this->data->replacement[idx]; +} +void Ref::Destroy() { + assert(this->data); + + this->data = nullptr; } } diff --git a/src/vfthook.hpp b/src/vfthook.hpp new file mode 100644 index 0000000..3c18b5a --- /dev/null +++ b/src/vfthook.hpp @@ -0,0 +1,72 @@ + +/* + * Nekohack: Break things in cool ways! + * Copyright (C) 2018 Rebekah Rowe + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include + +namespace crackem::vft::hook { + +using VF = void; +using VFT = VF*; +using VInst = VFT*; + +class Ref { + struct HookData; + static HookData* IntCreate(VInst*); + static HookData* IntGet(VInst*); + static bool IntContains(VInst*); + + HookData* data = nullptr; + void IntReplace(int idx, VF*) const; + VF* IntGetOriginal(int idx) const; +public: + Ref() {} + Ref(HookData* _data) : data(_data) { assert(this->data); } + void Clear(int idx) const; + bool Exists(int idx) const; + void Destroy(); + + template Ref(T* inst) : Ref(Ref::Get(inst)) { + static_assert(!std::is_same_v); + } + template void Replace(int idx, T replace_func) const { + this->IntReplace(idx, reinterpret_cast(&replace_func)); + } + template + T GetOriginal(std::size_t idx) const { + return reinterpret_cast(this->IntGetOriginal(idx)); + } + template + Ret CallOriginal(std::size_t idx, T... args) const { + using Func_t = Ret (*)(T...); + return this->GetOriginal(idx)(args...); + } + template static Ref Create(T* instance) { + return IntCreate(reinterpret_cast(instance)); + } + template static Ref Get(T* instance) { + return IntGet(reinterpret_cast(instance)); + } + template static bool Contains(T* instance) { + return IntContains(reinterpret_cast(instance)); + } +}; + +}