166 lines
6.3 KiB
C++
166 lines
6.3 KiB
C++
/// @file safetyhook/mid_hook.hpp
|
|
/// @brief Mid function hooking class.
|
|
|
|
#pragma once
|
|
|
|
#ifndef SAFETYHOOK_USE_CXXMODULES
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#else
|
|
import std.compat;
|
|
#endif
|
|
|
|
#include "safetyhook/allocator.hpp"
|
|
#include "safetyhook/common.hpp"
|
|
#include "safetyhook/context.hpp"
|
|
#include "safetyhook/inline_hook.hpp"
|
|
#include "safetyhook/utility.hpp"
|
|
|
|
namespace safetyhook {
|
|
|
|
/// @brief A MidHook destination function.
|
|
using MidHookFn = void (*)(Context& ctx);
|
|
|
|
/// @brief A mid function hook.
|
|
class SAFETYHOOK_API MidHook final {
|
|
public:
|
|
/// @brief Error type for MidHook.
|
|
struct Error {
|
|
/// @brief The type of error.
|
|
enum : uint8_t {
|
|
BAD_ALLOCATION,
|
|
BAD_INLINE_HOOK,
|
|
} type;
|
|
|
|
/// @brief Extra error information.
|
|
union {
|
|
Allocator::Error allocator_error; ///< Allocator error information.
|
|
InlineHook::Error inline_hook_error; ///< InlineHook error information.
|
|
};
|
|
|
|
/// @brief Create a BAD_ALLOCATION error.
|
|
/// @param err The Allocator::Error that failed.
|
|
/// @return The new BAD_ALLOCATION error.
|
|
[[nodiscard]] static Error bad_allocation(Allocator::Error err) {
|
|
Error error{};
|
|
error.type = BAD_ALLOCATION;
|
|
error.allocator_error = err;
|
|
return error;
|
|
}
|
|
|
|
/// @brief Create a BAD_INLINE_HOOK error.
|
|
/// @param err The InlineHook::Error that failed.
|
|
/// @return The new BAD_INLINE_HOOK error.
|
|
[[nodiscard]] static Error bad_inline_hook(InlineHook::Error err) {
|
|
Error error{};
|
|
error.type = BAD_INLINE_HOOK;
|
|
error.inline_hook_error = err;
|
|
return error;
|
|
}
|
|
};
|
|
|
|
/// @brief Flags for MidHook.
|
|
enum Flags : int {
|
|
Default = 0, ///< Default flags.
|
|
StartDisabled = 1, ///< Start the hook disabled.
|
|
};
|
|
|
|
/// @brief Creates a new MidHook object.
|
|
/// @param target The address of the function to hook.
|
|
/// @param destination_fn The destination function.
|
|
/// @param flags The flags to use.
|
|
/// @return The MidHook object or a MidHook::Error if an error occurred.
|
|
/// @note This will use the default global Allocator.
|
|
/// @note If you don't care about error handling, use the easy API (safetyhook::create_mid).
|
|
[[nodiscard]] static std::expected<MidHook, Error> create(
|
|
void* target, MidHookFn destination_fn, Flags flags = Default);
|
|
|
|
/// @brief Creates a new MidHook object.
|
|
/// @param target The address of the function to hook.
|
|
/// @param destination_fn The destination function.
|
|
/// @param flags The flags to use.
|
|
/// @return The MidHook object or a MidHook::Error if an error occurred.
|
|
/// @note This will use the default global Allocator.
|
|
/// @note If you don't care about error handling, use the easy API (safetyhook::create_mid).
|
|
template <typename T>
|
|
[[nodiscard]] static std::expected<MidHook, Error> create(
|
|
T target, MidHookFn destination_fn, Flags flags = Default) {
|
|
return create(reinterpret_cast<void*>(target), destination_fn, flags);
|
|
}
|
|
|
|
/// @brief Creates a new MidHook object with a given Allocator.
|
|
/// @param allocator The Allocator to use.
|
|
/// @param target The address of the function to hook.
|
|
/// @param destination_fn The destination function.
|
|
/// @param flags The flags to use.
|
|
/// @return The MidHook object or a MidHook::Error if an error occurred.
|
|
/// @note If you don't care about error handling, use the easy API (safetyhook::create_mid).
|
|
[[nodiscard]] static std::expected<MidHook, Error> create(
|
|
const std::shared_ptr<Allocator>& allocator, void* target, MidHookFn destination_fn, Flags flags = Default);
|
|
|
|
/// @brief Creates a new MidHook object with a given Allocator.
|
|
/// @tparam T The type of the function to hook.
|
|
/// @param allocator The Allocator to use.
|
|
/// @param target The address of the function to hook.
|
|
/// @param destination_fn The destination function.
|
|
/// @param flags The flags to use.
|
|
/// @return The MidHook object or a MidHook::Error if an error occurred.
|
|
/// @note If you don't care about error handling, use the easy API (safetyhook::create_mid).
|
|
template <typename T>
|
|
[[nodiscard]] static std::expected<MidHook, Error> create(
|
|
const std::shared_ptr<Allocator>& allocator, T target, MidHookFn destination_fn, Flags flags = Default) {
|
|
return create(allocator, reinterpret_cast<void*>(target), destination_fn, flags);
|
|
}
|
|
|
|
MidHook() = default;
|
|
MidHook(const MidHook&) = delete;
|
|
MidHook(MidHook&& other) noexcept;
|
|
MidHook& operator=(const MidHook&) = delete;
|
|
MidHook& operator=(MidHook&& other) noexcept;
|
|
~MidHook() = default;
|
|
|
|
/// @brief Reset the hook.
|
|
/// @details This will remove the hook and free the stub.
|
|
/// @note This is called automatically in the destructor.
|
|
void reset();
|
|
|
|
/// @brief Get a pointer to the target.
|
|
/// @return A pointer to the target.
|
|
[[nodiscard]] uint8_t* target() const { return m_target; }
|
|
|
|
/// @brief Get the address of the target.
|
|
/// @return The address of the target.
|
|
[[nodiscard]] uintptr_t target_address() const { return reinterpret_cast<uintptr_t>(m_target); }
|
|
|
|
/// @brief Get the destination function.
|
|
/// @return The destination function.
|
|
[[nodiscard]] MidHookFn destination() const { return m_destination; }
|
|
|
|
/// @brief Returns a vector containing the original bytes of the target function.
|
|
/// @return A vector of the original bytes of the target function.
|
|
[[nodiscard]] const auto& original_bytes() const { return m_hook.m_original_bytes; }
|
|
|
|
/// @brief Tests if the hook is valid.
|
|
/// @return true if the hook is valid, false otherwise.
|
|
explicit operator bool() const { return static_cast<bool>(m_stub); }
|
|
|
|
/// @brief Enable the hook.
|
|
[[nodiscard]] std::expected<void, Error> enable();
|
|
|
|
/// @brief Disable the hook.
|
|
[[nodiscard]] std::expected<void, Error> disable();
|
|
|
|
/// @brief Check if the hook is enabled.
|
|
[[nodiscard]] bool enabled() const { return m_hook.enabled(); }
|
|
|
|
private:
|
|
InlineHook m_hook{};
|
|
uint8_t* m_target{};
|
|
Allocation m_stub{};
|
|
MidHookFn m_destination{};
|
|
|
|
std::expected<void, Error> setup(
|
|
const std::shared_ptr<Allocator>& allocator, uint8_t* target, MidHookFn destination);
|
|
};
|
|
} // namespace safetyhook
|