Small rewrite

This commit is contained in:
Rebekah 2020-08-10 16:51:34 -04:00
parent 5f88edcc5b
commit 1d79ce4ca8
16 changed files with 365 additions and 319 deletions

View File

@ -1,15 +1,8 @@
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 3.14)
project(nekohack) project(crackem)
set(CMAKE_CXX_STANDARD 17) set(CRACKEM_BUILD_SHARED OFF CACHE BOOL "Build the shared library")
file(GLOB_RECURSE NEKOHACK_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") set(CRACKEM_BUILD_STATIC ON CACHE BOOL "Build the static library")
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)
add_library(nekohack-dummy SHARED "${CMAKE_CURRENT_SOURCE_DIR}/example/dummy.cpp") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/")
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)

27
compile_commands.json Normal file
View File

@ -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"
}
]

0
example.cpp Normal file
View File

View File

@ -1,8 +0,0 @@
extern "C" {
int GetInt() {
return 5;
}
}

View File

@ -1,15 +0,0 @@
#include <iostream>
#include <hack/sharedlibrary.hpp>
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_f>("GetInt");
std::cout << "GetInt Location: " << std::hex << GetInt << std::endl;
std::cout << "GetInt: " << GetInt() << std::endl;
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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
};*/
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <cstdint>
#include <memory>
namespace neko::hack {
// Virtual function table
using VF = void*;
using VFT = VF[];
// Virtual Function Table Hook
class VFTHook {
public:
VFTHook();
~VFTHook();
template<class TPtr>
void Init(TPtr instance) {
this->IntInit(reinterpret_cast<void*>(instance));
}
template<class TPtr>
void Hook(TPtr func, std::size_t idx) {
this->IntHook(reinterpret_cast<VF>(func), idx);
}
void Finish();
void Undo();
template <class T = void*>
inline T GetOriginal(std::size_t idx) const {
return reinterpret_cast<T>(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<void*[]> replacement;
std::size_t size;
};
}

32
src/CMakeLists.txt Normal file
View File

@ -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)

View File

@ -1,6 +1,6 @@
/* /*
* Nekohack: Break things in cool ways! * Crackem: Break things in cool ways!
* Copyright (C) 2018 Rebekah Rowe * Copyright (C) 2018 Rebekah Rowe
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -17,10 +17,30 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <r_asm.h> #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
};*/
} }

View File

@ -19,10 +19,10 @@
#include <cstdint> #include <cstdint>
namespace neko::hack::mem { namespace crackem::mem {
template <typename Type, class Ptr> template <typename Type, class Ptr>
inline auto Offset(Ptr src, std::ptrdiff_t offset) { inline auto Offset(Ptr* src, std::ptrdiff_t offset) {
return reinterpret_cast<Type*>(reinterpret_cast<uint64_t>(src) + offset); return reinterpret_cast<Type*>(reinterpret_cast<uint64_t>(src) + offset);
} }

View File

@ -17,97 +17,110 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <cstring> #include <cstring>
#include <dlfcn.h>
#include "memory.hpp" #include "memory.hpp"
#include "sharedlibrary.hpp" #include "sharedlibrary.hpp"
namespace neko::hack { namespace crackem {
struct Passthrough { SharedLibrary::SharedLibrary() : loaded(false) {}
std::string name; SharedLibrary::SharedLibrary(std::string_view _name) { this->Load(_name); }
fs::path path; SharedLibrary::SharedLibrary(const SharedLibrary& v) { *this = v; }
void* begin; SharedLibrary::SharedLibrary(SharedLibrary&& v) { *this = v; }
std::size_t size; SharedLibrary& SharedLibrary::operator=(SharedLibrary&& v) {
}; this->loaded = false;
this->name = v.name;
SharedLibrary::SharedLibrary(){} if (v.loaded) {
SharedLibrary::SharedLibrary(std::string_view _name) : name(_name) { v.loaded = false;
try { this->path = std::move(v.path);
this->Init(_name); this->_begin = v._begin;
} catch(...){} this->_end = v._end;
} this->_size = v._size;
this->lmap = v.lmap;
void SharedLibrary::Init(std::string_view _name){ this->loaded = true;
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<Passthrough*>(_pass);
if (!strstr(info->dlpi_name, pass->name.c_str()))
return 0;
pass->path = info->dlpi_name;
pass->begin = reinterpret_cast<void*>(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr);
pass->size = info->dlpi_phdr[0].p_memsz;
return 1;
}, reinterpret_cast<void*>(&pass)))
throw std::runtime_error("SharedLibrary: Unable to find loaded library");
this->path = pass.path;
this->_begin = pass.begin;
this->_end = mem::Offset<void*>(pass.begin, pass.size);
this->_size = pass.size;
this->lmap = static_cast<LMap>(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;
} }
return *this;
} }
void SharedLibrary::ForceInit(std::string_view _name) { SharedLibrary& SharedLibrary::operator=(const SharedLibrary& v) {
while(!this->init_flag) { this->loaded = false;
try { this->name = v.name;
this->Init(); if (v.loaded) {
} catch(...) {} 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() { void SharedLibrary::Load(std::string_view _name) {
return name; this->name = _name;
this->Load();
} }
void* SharedLibrary::begin() { void SharedLibrary::Load() {
this->Init(); 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<SharedLibrary*>(_pass);
_this->path = info->dlpi_name;
if (_this->path.stem() != _this->name/* || _this->path.extention() ==*/)
return 0;
_this->_begin = reinterpret_cast<void*>(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr);
_this->_size = info->dlpi_phdr[0].p_memsz;
return 1;
}, reinterpret_cast<void*>(this)))
return;
this->loaded = true;
this->_end = mem::Offset<void*>(this->_begin, this->_size);
this->lmap = static_cast<Handle>(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; return this->_begin;
} }
void* SharedLibrary::end() { void* SharedLibrary::end() const {
this->Init(); assert(this->loaded);
return this->_end; return this->_end;
} }
std::size_t SharedLibrary::size() { std::size_t SharedLibrary::size() const {
this->Init(); assert(this->loaded);
return this->_size; return this->_size;
} }
LMap SharedLibrary::GetLMap() { Handle SharedLibrary::GetHandle() const {
this->Init(); assert(this->loaded);
return this->lmap; return this->lmap;
} }
void SharedLibrary::Clear(){ void* SharedLibrary::GetSymInternal(SymStr name) const {
this->init_flag = false;
this->name = std::string_view();
}
void* SharedLibrary::GetSymInternal(SymStr name) {
#if defined(__linux__) #if defined(__linux__)
void* symbol = dlsym(this->GetLMap(), name); void* symbol = dlsym(this->GetHandle(), name);
if (const char* error = dlerror()) 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; return symbol;
#elif defined(_WIN32) #elif defined(_WIN32)
return GetProcAddress(this->GetLMap(), name); return GetProcAddress(this->GetLMap(), name);
#else
assert(false);
#endif #endif
} }

View File

@ -21,45 +21,53 @@
#if defined(__linux__) #if defined(__linux__)
#include <link.h> // link maps #include <link.h> // link maps
using LMap = link_map*; using Handle = link_map*;
using SymStr = const char*; using SymStr = const char*;
#elif defined(_WIN32) #elif defined(_WIN32)
#include <windows.h> // loadlibrary #include <windows.h> // loadlibrary
using LMap = HMODULE; using Handle = HMODULE;
using SymStr = LPCSTR; using SymStr = LPCSTR;
#endif #endif
#include <string_view> #include <string_view>
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem;
namespace neko::hack { namespace crackem {
namespace fs = std::filesystem;
class SharedLibrary { class SharedLibrary {
public: public:
SharedLibrary(); SharedLibrary();
SharedLibrary(std::string_view _name); SharedLibrary(std::string_view _name);
void Init() { this->Init(this->name); } SharedLibrary(const SharedLibrary&);
void ForceInit() { this->ForceInit(this->name); } SharedLibrary(SharedLibrary&&);
void Init(std::string_view _name); void Load();
void ForceInit(std::string_view _name); void Load(std::string_view _name);
std::string_view GetName();
void* begin(); SharedLibrary& operator=(SharedLibrary&& v);
void* end(); SharedLibrary& operator=(const SharedLibrary& v);
std::size_t size();
void Clear(); bool IsLoaded() const;
LMap GetLMap(); 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<typename T> template<typename T>
T GetSym(SymStr s) { return reinterpret_cast<T>(this->GetSymInternal(s)); } T GetSym(SymStr s) { return reinterpret_cast<T>(this->GetSymInternal(s)); }
private: private:
void* GetSymInternal(SymStr); void* GetSymInternal(SymStr) const;
bool init_flag = false;
bool loaded;
std::string_view name; std::string_view name;
fs::path path; fs::path path;
void* _begin; void* _begin;
void* _end; void* _end;
std::size_t _size; std::size_t _size;
LMap lmap; Handle lmap;
}; };
} }

View File

@ -24,58 +24,41 @@
#include "signature.hpp" #include "signature.hpp"
class Pattern { namespace crackem::sig {
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);
// act on delim // Original function by learn_more -- https://github.com/learn-more/findpattern-bench
// Wildcard static bool InRange(uint8_t x, uint8_t a, uint8_t b) { return x >= a && x <= b; }
if (input.front() == '?') { static uint8_t GetBits(uint8_t x) { return InRange((x & (~0x20)), 'A', 'F') ? ((x & (~0x20)) - 'A' + 0xA): (InRange(x, '0', '9') ? x - '0': 0); }
this->wilds.push_back(true); static uint8_t GetByte(const char* x) { return GetBits(x[0]) << 4 | GetBits(x[1]); }
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;
std::string hex = {input[0], input[1]}; uintptr_t FindPattern(uintptr_t start_address, uintptr_t end_address, const char* target_pattern) {
uint8_t nibble = std::stoul(hex, nullptr, 16); const char* pattern = target_pattern;
this->pat.push_back(*reinterpret_cast<std::byte*>(nibble));
input.remove_prefix(2); uintptr_t first_match = 0;
}
} for (uintptr_t position = start_address; position < end_address; position++) {
} if (!*pattern)
inline std::byte Get(std::size_t i) { return first_match;
assert(!this->IsWild(i));
return pat[i]; const uint8_t pattern_current = *reinterpret_cast<const uint8_t*>(pattern);
} const uint8_t memory_current = *reinterpret_cast<const uint8_t*>(position);
inline bool IsWild(std::size_t i) {
auto find = std::find(wilds.begin(), wilds.end(), i); if (pattern_current == '\?' || memory_current == GetByte(pattern)) {
return find != wilds.end(); if (!first_match)
} first_match = position;
inline std::size_t size(){
return this->pat.size(); if (!pattern[2])
} return first_match;
private:
std::vector<std::byte> pat; pattern += pattern_current != '\?' ? 3 : 2;
std::vector<bool> wilds; }
}; 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<std::byte*>(_begin);
std::byte* end = reinterpret_cast<std::byte*>(_end);
auto find = std::find_if(begin, end, [&](std::byte* s) {
// Pattern equality
for (int )
});*/
} }

View File

@ -21,8 +21,8 @@
#include <string_view> #include <string_view>
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);
} }

View File

@ -1,50 +1,74 @@
#include <cstring> #include <cstring>
#include "memory.hpp"
#include "vfthook.hpp" #include "vfthook.hpp"
namespace neko::hack { namespace crackem::vft::hook {
VFTHook::VFTHook(){} struct Ref::HookData {
VFTHook::~VFTHook() { bool Verify() {
try { return this->magic = kMagic;
this->Undo(); }
}catch(...){} static HookData* Get(VInst* v) {
} return mem::Offset<HookData>(*v, -int(sizeof(HookData)));
}
void VFTHook::IntInit(void* instance) { VInst* instance;
try { VFT* original;
this->Undo(); std::size_t size;
}catch(...){} constexpr static uint32_t kMagic = 0xCAFEBEEF;
uint32_t magic;
this->vtable = reinterpret_cast<void***>(instance); VF* replacement[];
this->original_data = *this->vtable;
this->size = 0;
while (this->original_data[this->size]) this->size++;
this->replacement = std::make_unique<VFT>(this->size);
memcpy(this->replacement.get(), this->original_data, sizeof(VF) * this->size);
}; };
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) { Ref::HookData* Ref::IntCreate(VInst* v) {
//if (this->original[idx] != this->replacement[idx]) if (HookData::Get(v)->Verify())
//throw std::logic_error("Already Hooked"); throw std::runtime_error("Hook already exists!");
this->replacement[idx] = func;
std::size_t size = 0;
while ((*v)[size])
size++;
HookData* hd = reinterpret_cast<HookData*>(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;
} }
} }

72
src/vfthook.hpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstdint>
#include <memory>
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<class T> Ref(T* inst) : Ref(Ref::Get(inst)) {
static_assert(!std::is_same_v<T, HookData>);
}
template<class T> void Replace(int idx, T replace_func) const {
this->IntReplace(idx, reinterpret_cast<void*>(&replace_func));
}
template<typename T>
T GetOriginal(std::size_t idx) const {
return reinterpret_cast<T>(this->IntGetOriginal(idx));
}
template<typename Ret, typename... T>
Ret CallOriginal(std::size_t idx, T... args) const {
using Func_t = Ret (*)(T...);
return this->GetOriginal<Func_t>(idx)(args...);
}
template<class T> static Ref Create(T* instance) {
return IntCreate(reinterpret_cast<VInst*>(instance));
}
template<class T> static Ref Get(T* instance) {
return IntGet(reinterpret_cast<VInst*>(instance));
}
template<class T> static bool Contains(T* instance) {
return IntContains(reinterpret_cast<VInst*>(instance));
}
};
}