Add features, RV64 still doesn't work 🤷
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18..3.28)
|
||||||
project(FunctionHook)
|
project(FunctionHook)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
@@ -4,33 +4,46 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace FunctionHooking {
|
namespace FunctionHooking {
|
||||||
class Detour {
|
class Detour;
|
||||||
private:
|
|
||||||
void* source = nullptr; //The function to be hooked.
|
|
||||||
void* destination = nullptr; //The function we are redirecting execution to.
|
|
||||||
|
|
||||||
std::vector<uint8_t> overwritten_bytes{};
|
|
||||||
std::vector<uint8_t> hook_bytes{};
|
|
||||||
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 <typename T, typename... Args>
|
|
||||||
inline typename std::enable_if<!std::is_void<T>::value, T>::type callOriginal(Args... args) {
|
|
||||||
removeHook();
|
|
||||||
const T result = reinterpret_cast<T(*)(Args...)>(source)(args...);
|
|
||||||
createHook(source, destination);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
inline typename std::enable_if<std::is_void<T>::value, void>::type callOriginal(Args... args) {
|
|
||||||
removeHook();
|
|
||||||
reinterpret_cast<void(*)(Args...)>(source)(args...);
|
|
||||||
createHook(source, destination);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FunctionHooking::Detour {
|
||||||
|
private:
|
||||||
|
void* source = nullptr;
|
||||||
|
void* destination = nullptr;
|
||||||
|
std::vector<uint8_t> overwritten_bytes{};
|
||||||
|
std::vector<uint8_t> hook_bytes{};
|
||||||
|
private:
|
||||||
|
void CreateHook(void* source_address, void* destination_address);
|
||||||
|
void RemoveHook();
|
||||||
|
public:
|
||||||
|
/// @returns True if our hook is present.
|
||||||
|
/// @note This allows you to know if something has overwritten our hook.
|
||||||
|
[[nodiscard]] bool Valid();
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
inline typename std::enable_if<!std::is_void<T>::value, T>::type CallOriginal(Args... args) {
|
||||||
|
RemoveHook();
|
||||||
|
const T result = reinterpret_cast<T(*)(Args...)>(source)(args...);
|
||||||
|
CreateHook(source, destination);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
inline typename std::enable_if<std::is_void<T>::value, void>::type CallOriginal(Args... args) {
|
||||||
|
RemoveHook();
|
||||||
|
reinterpret_cast<void(*)(Args...)>(source)(args...);
|
||||||
|
CreateHook(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// @returns True if the function is detour hooked.
|
||||||
|
/// @param function_address The function to check.
|
||||||
|
static bool Hooked(void* function_address);
|
||||||
|
public:
|
||||||
|
/// Create a new detour hook.
|
||||||
|
/// @param source_address The function to be hooked.
|
||||||
|
/// @param destination_address The function we are redirecting execution to.
|
||||||
|
Detour(void* source_address, void* destination_address);
|
||||||
|
~Detour();
|
||||||
|
};
|
13
main.cpp
13
main.cpp
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
using namespace FunctionHooking;
|
using namespace FunctionHooking;
|
||||||
|
|
||||||
Detour voidDetour;
|
Detour* voidDetour;
|
||||||
Detour stringDetour;
|
Detour* stringDetour;
|
||||||
|
|
||||||
void original_void() {
|
void original_void() {
|
||||||
std::cout << "Original void function." << std::endl;
|
std::cout << "Original void function." << std::endl;
|
||||||
@@ -12,7 +12,7 @@ void original_void() {
|
|||||||
|
|
||||||
void hook_void() {
|
void hook_void() {
|
||||||
std::cout << "Void hook function." << std::endl;
|
std::cout << "Void hook function." << std::endl;
|
||||||
voidDetour.callOriginal<void>();
|
voidDetour->CallOriginal<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string original_string(const std::string& string) {
|
std::string original_string(const std::string& string) {
|
||||||
@@ -23,16 +23,15 @@ std::string original_string(const std::string& string) {
|
|||||||
std::string hook_string(const std::string& string) {
|
std::string hook_string(const std::string& string) {
|
||||||
std::cout << string + " hook function." << std::endl;
|
std::cout << string + " hook function." << std::endl;
|
||||||
|
|
||||||
return stringDetour.callOriginal<std::string>(string);
|
return stringDetour->CallOriginal<std::string>(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
//Set up hooks.
|
//Set up hooks.
|
||||||
voidDetour = FunctionHooking::Detour((void*) original_void, (void*) hook_void);
|
voidDetour = new Detour((void*) original_void, (void*) hook_void);
|
||||||
stringDetour = FunctionHooking::Detour((void*) original_string, (void*) hook_string);
|
stringDetour = new Detour((void*) original_string, (void*) hook_string);
|
||||||
|
|
||||||
original_void();
|
original_void();
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
original_string("String");
|
original_string("String");
|
||||||
return 0;
|
|
||||||
}
|
}
|
@@ -2,12 +2,15 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
void FunctionHooking::Detour::createHook(void* source_address, void* destination_address) {
|
using namespace FunctionHooking;
|
||||||
source = source_address;
|
constexpr std::array<uint8_t, 13> empty_hook = { 0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xE2 };
|
||||||
destination = destination_address;
|
|
||||||
|
void Detour::CreateHook(void* source_address, void* destination_address) {
|
||||||
|
hook_bytes.resize(empty_hook.size());
|
||||||
|
memcpy(hook_bytes.data(), empty_hook.data(), hook_bytes.size());
|
||||||
|
|
||||||
hook_bytes.insert(hook_bytes.begin(), {0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x41, 0xFF, 0xE2});
|
|
||||||
overwritten_bytes.resize(hook_bytes.size());
|
overwritten_bytes.resize(hook_bytes.size());
|
||||||
|
|
||||||
//Save the bytes to be overwritten from the function prelude.
|
//Save the bytes to be overwritten from the function prelude.
|
||||||
@@ -25,3 +28,13 @@ void FunctionHooking::Detour::createHook(void* source_address, void* destination
|
|||||||
//Make the memory page non-writable again.
|
//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);
|
mprotect((void*)((uintptr_t)source_address & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Detour::Hooked(void* function_address) {
|
||||||
|
std::vector<uint8_t> current_data(empty_hook.size());
|
||||||
|
memcpy(current_data.data(), function_address, current_data.size());
|
||||||
|
|
||||||
|
if (current_data[0] == empty_hook[0] && current_data[1] == empty_hook[1] && current_data[10] == empty_hook[10]
|
||||||
|
&& current_data[11] == empty_hook[11] && current_data[12] == empty_hook[12])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
@@ -3,7 +3,8 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
void FunctionHooking::Detour::removeHook() {
|
using namespace FunctionHooking;
|
||||||
|
void Detour::RemoveHook() {
|
||||||
|
|
||||||
//Make the target memory page writable.
|
//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);
|
mprotect((void*)((uintptr_t)source & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_WRITE|PROT_EXEC);
|
||||||
@@ -15,6 +16,24 @@ void FunctionHooking::Detour::removeHook() {
|
|||||||
mprotect((void*)((uintptr_t)source & ~(sysconf(_SC_PAGE_SIZE)-1)), sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_EXEC);
|
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) {
|
Detour::Detour(void* source_address, void* destination_address) : source(source_address), destination(destination_address) {
|
||||||
createHook(source_address, destination_address);
|
CreateHook(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FunctionHooking::Detour::Valid() {
|
||||||
|
if (source == nullptr || destination == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hook_bytes.empty() || overwritten_bytes.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<uint8_t> current_data(hook_bytes.size());
|
||||||
|
memcpy(current_data.data(), source, current_data.size());
|
||||||
|
|
||||||
|
return current_data == hook_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Detour::~Detour() {
|
||||||
|
if (Valid())
|
||||||
|
RemoveHook();
|
||||||
}
|
}
|
@@ -3,38 +3,39 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
void FunctionHooking::Detour::createHook(void* source_address, void* destination_address) {
|
constexpr uint32_t JAL_OPCODE = 0b1101111;
|
||||||
source = source_address;
|
constexpr int JAL_OFFSET_SHIFT = 12;
|
||||||
destination = destination_address;
|
constexpr int JAL_MASK = 0xfff;
|
||||||
|
|
||||||
hook_bytes.insert(hook_bytes.begin(), { 0x17, 0x05, 0x00, 0x00, 0x03, 0x25, 0x05, 0x00, 0x67, 0x00, 0x0a, 0x00} );
|
void FunctionHooking::Detour::CreateHook(void* source_address, void* destination_address) {
|
||||||
|
intptr_t target_addr = reinterpret_cast<intptr_t>(source_address);
|
||||||
|
intptr_t hook_addr = reinterpret_cast<intptr_t>(destination_address);
|
||||||
|
int32_t offset = (hook_addr - target_addr) >> 1;
|
||||||
|
|
||||||
overwritten_bytes.resize(hook_bytes.size() + 8);
|
// Encode the offset for the JAL instruction
|
||||||
|
uint32_t instruction = (JAL_OPCODE & JAL_MASK) |
|
||||||
|
((offset & 0x7fe) << 20) | // bits 1 to 10 of offset
|
||||||
|
((offset & 0x800) << 9) | // bit 11 of offset
|
||||||
|
((offset & 0xff000) << 9); // bits 12 to 19 of offset
|
||||||
|
|
||||||
//Save the bytes to be overwritten from the function prelude
|
hook_bytes.resize(sizeof(instruction));
|
||||||
memcpy(overwritten_bytes.data(), source_address, hook_bytes.size());
|
std::memcpy(hook_bytes.data(), &instruction, sizeof(instruction));
|
||||||
|
|
||||||
//Calculate the offset.
|
overwritten_bytes.resize(sizeof(instruction));
|
||||||
intptr_t offset = (intptr_t)destination_address - (intptr_t)source_address;
|
std::memcpy(overwritten_bytes.data(), source_address, sizeof(instruction));
|
||||||
|
|
||||||
//Place the offset into the auipc instruction
|
// Determine the page size and page containing the target function
|
||||||
memcpy(&hook_bytes[1], &offset, sizeof(uint32_t));
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
void* page_start = reinterpret_cast<void*>(target_addr & ~(page_size - 1));
|
||||||
|
|
||||||
//Append the destination address after the instructions
|
// Change memory protection to allow writing
|
||||||
hook_bytes.insert(hook_bytes.end(), (uint8_t*)&destination_address, (uint8_t*)&destination_address + sizeof(void*));
|
if (mprotect(page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
//Save the bytes to be overwritten from the function prelude.
|
// Write the instruction to the target function
|
||||||
memcpy(overwritten_bytes.data(), source_address, hook_bytes.size());
|
std::memcpy(source_address, &instruction, sizeof(instruction));
|
||||||
|
|
||||||
//Put the destination address into the jump.
|
// Restore original memory protection
|
||||||
memcpy(&hook_bytes[2], &destination_address, sizeof(void*));
|
if (mprotect(page_start, page_size, PROT_READ | PROT_EXEC) == -1)
|
||||||
|
return;
|
||||||
//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.data(), hook_bytes.size());
|
|
||||||
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user