Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shared library compilation support #71

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ if(SAFETYHOOK_FETCH_ZYDIS) # fetch-zydis
)
FetchContent_MakeAvailable(Zydis)

add_library(Zydis::Zydis ALIAS Zydis)

endif()
# Packages
if(SAFETYHOOK_BUILD_DOCS) # build-docs
Expand All @@ -87,6 +89,10 @@ endif()
if(SAFETYHOOK_AMALGAMATE) # amalgamate
find_package(Python3 REQUIRED)

endif()
if((NOT SAFETYHOOK_FETCH_ZYDIS) AND NOT (TARGET Zydis)) # find-zydis
find_package(Zydis REQUIRED CONFIG)

endif()
# Target: safetyhook
set(safetyhook_SOURCES
Expand All @@ -101,12 +107,24 @@ set(safetyhook_SOURCES
cmake.toml
)

add_library(safetyhook STATIC)
add_library(safetyhook)

target_sources(safetyhook PRIVATE ${safetyhook_SOURCES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${safetyhook_SOURCES})

add_library(safetyhook::safetyhook ALIAS safetyhook)
if(BUILD_SHARED_LIBS) # shared
target_compile_definitions(safetyhook PUBLIC
SAFETYHOOK_SHARED_LIB
)
endif()

if(BUILD_SHARED_LIBS) # shared
target_compile_definitions(safetyhook PRIVATE
SAFETYHOOK_BUILDING
)
endif()

target_compile_features(safetyhook PUBLIC
cxx_std_23
)
Expand All @@ -116,6 +134,7 @@ if(MSVC) # msvc
"/permissive-"
"/W4"
"/w14640"
"/wd4251"
)
endif()

Expand Down Expand Up @@ -144,7 +163,7 @@ target_include_directories(safetyhook PUBLIC
)

target_link_libraries(safetyhook PUBLIC
Zydis
Zydis::Zydis
)

set(CMKR_TARGET safetyhook)
Expand Down Expand Up @@ -425,7 +444,7 @@ if(SAFETYHOOK_BUILD_TEST AND SAFETYHOOK_AMALGAMATE) # build-amalgamate-test
)

target_link_libraries(test-amalgamated PRIVATE
Zydis
Zydis::Zydis
Boost::ut
xbyak::xbyak
)
Expand Down
20 changes: 16 additions & 4 deletions cmake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ SAFETYHOOK_USE_CXXMODULES = false
build-docs = "SAFETYHOOK_BUILD_DOCS"
build-test = "SAFETYHOOK_BUILD_TEST"
build-examples = "SAFETYHOOK_BUILD_EXAMPLES"
shared = "BUILD_SHARED_LIBS"
windows-only-example = "SAFETYHOOK_BUILD_EXAMPLES AND WIN32"
amalgamate = "SAFETYHOOK_AMALGAMATE"
build-amalgamate-test = "SAFETYHOOK_BUILD_TEST AND SAFETYHOOK_AMALGAMATE"
fetch-zydis = "SAFETYHOOK_FETCH_ZYDIS"
find-zydis = "(NOT SAFETYHOOK_FETCH_ZYDIS) AND NOT (TARGET Zydis)"

[fetch-content.ut]
condition = "build-test"
Expand All @@ -41,6 +43,9 @@ option(ZYDIS_BUILD_EXAMPLES "" OFF)
option(ZYDIS_BUILD_TOOLS "" OFF)
option(ZYDIS_BUILD_DOXYGEN "" OFF)
"""
cmake-after = """
add_library(Zydis::Zydis ALIAS Zydis)
"""

[find-package.Doxygen]
condition = "build-docs"
Expand All @@ -50,16 +55,23 @@ required = true
condition = "amalgamate"
required = true

[find-package.Zydis]
condition = "find-zydis"
config = true
required = true

[target.safetyhook]
type = "static"
type = "library"
sources = ["src/*.cpp"]
include-directories = ["include/"]
compile-features = ["cxx_std_23"]
alias = "safetyhook::safetyhook"
link-libraries = ["Zydis"]
msvc.private-compile-options = ["/permissive-", "/W4", "/w14640"]
link-libraries = ["Zydis::Zydis"]
msvc.private-compile-options = ["/permissive-", "/W4", "/w14640", "/wd4251"]
clang.private-compile-options = ["-Wall", "-Wextra", "-Wshadow", "-Wnon-virtual-dtor", "-pedantic"]
gcc.private-compile-options = ["-Wall", "-Wextra", "-Wshadow", "-Wnon-virtual-dtor", "-pedantic"]
shared.compile-definitions = ["SAFETYHOOK_SHARED_LIB"]
shared.private-compile-definitions = ["SAFETYHOOK_BUILDING"]
cmake-after = """
if(SAFETYHOOK_USE_CXXMODULES)
target_compile_definitions(safetyhook INTERFACE SAFETYHOOK_USE_CXXMODULES)
Expand Down Expand Up @@ -153,7 +165,7 @@ condition = "build-amalgamate-test"
type = "executable"
sources = ["test/*.cpp", "amalgamated-dist/safetyhook.cpp"]
include-directories = ["amalgamated-dist/"]
link-libraries = ["Zydis", "Boost::ut", "xbyak::xbyak"]
link-libraries = ["Zydis::Zydis", "Boost::ut", "xbyak::xbyak"]
compile-definitions = ["BOOST_UT_DISABLE_MODULE"]
compile-features = ["cxx_std_23"]
cmake-after = """
Expand Down
6 changes: 4 additions & 2 deletions include/safetyhook/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import std.compat;
#endif

#include "safetyhook/common.hpp"

namespace safetyhook {
class Allocator;

/// @brief A memory allocation.
class Allocation final {
class SAFETYHOOK_API Allocation final {
public:
Allocation() = default;
Allocation(const Allocation&) = delete;
Expand Down Expand Up @@ -58,7 +60,7 @@ class Allocation final {
};

/// @brief Allocates memory near target addresses.
class Allocator final : public std::enable_shared_from_this<Allocator> {
class SAFETYHOOK_API Allocator final : public std::enable_shared_from_this<Allocator> {
public:
/// @brief Returns the global Allocator.
/// @return The global Allocator.
Expand Down
16 changes: 16 additions & 0 deletions include/safetyhook/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@
#elif SAFETYHOOK_COMPILER_GCC || SAFETYHOOK_COMPILER_CLANG
#define SAFETYHOOK_NOINLINE __attribute__((noinline))
#endif

#if SAFETYHOOK_COMPILER_MSVC
#define SAFETYHOOK_DLLEXPORT __declspec(dllexport)
#define SAFETYHOOK_DLLIMPORT __declspec(dllimport)
#elif SAFETYHOOK_COMPILER_GCC || SAFETYHOOK_COMPILER_CLANG
#define SAFETYHOOK_DLLEXPORT __attribute__((visibility("default")))
#define SATEFYHOOK_DLLIMPORT
#endif

#if SAFETYHOOK_SHARED_LIB && SAFETYHOOK_BUILDING
#define SAFETYHOOK_API SAFETYHOOK_DLLEXPORT
#elif SAFETYHOOK_SHARED_LIB
#define SAFETYHOOK_API SAFETYHOOK_DLLIMPORT
#else
#define SAFETYHOOK_API
#endif
7 changes: 4 additions & 3 deletions include/safetyhook/easy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include "safetyhook/common.hpp"
#include "safetyhook/inline_hook.hpp"
#include "safetyhook/mid_hook.hpp"
#include "safetyhook/utility.hpp"
Expand All @@ -13,7 +14,7 @@ namespace safetyhook {
/// @param target The address of the function to hook.
/// @param destination The address of the destination function.
/// @return The InlineHook object.
[[nodiscard]] InlineHook create_inline(void* target, void* destination);
[[nodiscard]] InlineHook SAFETYHOOK_API create_inline(void* target, void* destination);

/// @brief Easy to use API for creating an InlineHook.
/// @param target The address of the function to hook.
Expand All @@ -27,7 +28,7 @@ namespace safetyhook {
/// @param target the address of the function to hook.
/// @param destination The destination function.
/// @return The MidHook object.
[[nodiscard]] MidHook create_mid(void* target, MidHookFn destination);
[[nodiscard]] MidHook SAFETYHOOK_API create_mid(void* target, MidHookFn destination);

/// @brief Easy to use API for creating a MidHook.
/// @param target the address of the function to hook.
Expand All @@ -40,7 +41,7 @@ namespace safetyhook {
/// @brief Easy to use API for creating a VmtHook.
/// @param object The object to hook.
/// @return The VmtHook object.
[[nodiscard]] VmtHook create_vmt(void* object);
[[nodiscard]] VmtHook SAFETYHOOK_API create_vmt(void* object);

/// @brief Easy to use API for creating a VmHook.
/// @param vmt The VmtHook to use to create the VmHook.
Expand Down
2 changes: 1 addition & 1 deletion include/safetyhook/inline_hook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import std.compat;

namespace safetyhook {
/// @brief An inline hook.
class InlineHook final {
class SAFETYHOOK_API InlineHook final {
public:
/// @brief Error type for InlineHook.
struct Error {
Expand Down
3 changes: 2 additions & 1 deletion include/safetyhook/mid_hook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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"
Expand All @@ -21,7 +22,7 @@ namespace safetyhook {
using MidHookFn = void (*)(Context& ctx);

/// @brief A mid function hook.
class MidHook final {
class SAFETYHOOK_API MidHook final {
public:
/// @brief Error type for MidHook.
struct Error {
Expand Down
24 changes: 13 additions & 11 deletions include/safetyhook/os.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import std.compat;
#endif

#include "safetyhook/common.hpp"

namespace safetyhook {

enum class OsError {
Expand Down Expand Up @@ -45,14 +47,14 @@ struct VmBasicInfo {
bool is_free;
};

std::expected<uint8_t*, OsError> vm_allocate(uint8_t* address, size_t size, VmAccess access);
void vm_free(uint8_t* address);
std::expected<uint32_t, OsError> vm_protect(uint8_t* address, size_t size, VmAccess access);
std::expected<uint32_t, OsError> vm_protect(uint8_t* address, size_t size, uint32_t access);
std::expected<VmBasicInfo, OsError> vm_query(uint8_t* address);
bool vm_is_readable(uint8_t* address, size_t size);
bool vm_is_writable(uint8_t* address, size_t size);
bool vm_is_executable(uint8_t* address);
std::expected<uint8_t*, OsError> SAFETYHOOK_API vm_allocate(uint8_t* address, size_t size, VmAccess access);
void SAFETYHOOK_API vm_free(uint8_t* address);
std::expected<uint32_t, OsError> SAFETYHOOK_API vm_protect(uint8_t* address, size_t size, VmAccess access);
std::expected<uint32_t, OsError> SAFETYHOOK_API vm_protect(uint8_t* address, size_t size, uint32_t access);
std::expected<VmBasicInfo, OsError> SAFETYHOOK_API vm_query(uint8_t* address);
bool SAFETYHOOK_API vm_is_readable(uint8_t* address, size_t size);
bool SAFETYHOOK_API vm_is_writable(uint8_t* address, size_t size);
bool SAFETYHOOK_API vm_is_executable(uint8_t* address);

struct SystemInfo {
uint32_t page_size;
Expand All @@ -61,7 +63,7 @@ struct SystemInfo {
uint8_t* max_address;
};

SystemInfo system_info();
SystemInfo SAFETYHOOK_API system_info();

using ThreadId = uint32_t;
using ThreadHandle = void*;
Expand All @@ -74,13 +76,13 @@ using ThreadContext = void*;
/// @note The visit function will be called in the order that the threads were frozen.
/// @note The visit function will be called before the run function.
/// @note Keep the logic inside run_fn and visit_fn as simple as possible to avoid deadlocks.
void execute_while_frozen(const std::function<void()>& run_fn,
void SAFETYHOOK_API execute_while_frozen(const std::function<void()>& run_fn,
const std::function<void(ThreadId, ThreadHandle, ThreadContext)>& visit_fn = {});

/// @brief Will modify the context of a thread's IP to point to a new address if its IP is at the old address.
/// @param ctx The thread context to modify.
/// @param old_ip The old IP address.
/// @param new_ip The new IP address.
void fix_ip(ThreadContext ctx, uint8_t* old_ip, uint8_t* new_ip);
void SAFETYHOOK_API fix_ip(ThreadContext ctx, uint8_t* old_ip, uint8_t* new_ip);

} // namespace safetyhook
10 changes: 6 additions & 4 deletions include/safetyhook/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import std.compat;
#endif

#include "safetyhook/common.hpp"

namespace safetyhook {
template <typename T> constexpr void store(uint8_t* address, const T& value) {
std::copy_n(reinterpret_cast<const uint8_t*>(&value), sizeof(T), address);
Expand All @@ -17,9 +19,9 @@ template <typename T> constexpr void store(uint8_t* address, const T& value) {
template <typename T>
concept FnPtr = requires(T f) { std::is_pointer_v<T>&& std::is_function_v<std::remove_pointer_t<T>>; };

bool is_executable(uint8_t* address);
bool SAFETYHOOK_API is_executable(uint8_t* address);

class UnprotectMemory {
class SAFETYHOOK_API UnprotectMemory {
public:
UnprotectMemory() = delete;
~UnprotectMemory();
Expand All @@ -29,7 +31,7 @@ class UnprotectMemory {
UnprotectMemory& operator=(UnprotectMemory&& other) noexcept;

private:
friend std::optional<UnprotectMemory> unprotect(uint8_t*, size_t);
friend std::optional<UnprotectMemory> SAFETYHOOK_API unprotect(uint8_t*, size_t);

UnprotectMemory(uint8_t* address, size_t size, uint32_t original_protection)
: m_address{address}, m_size{size}, m_original_protection{original_protection} {}
Expand All @@ -39,7 +41,7 @@ class UnprotectMemory {
uint32_t m_original_protection{};
};

[[nodiscard]] std::optional<UnprotectMemory> unprotect(uint8_t* address, size_t size);
[[nodiscard]] std::optional<UnprotectMemory> SAFETYHOOK_API unprotect(uint8_t* address, size_t size);

template <typename T> constexpr T align_up(T address, size_t align) {
const auto unaligned_address = (uintptr_t)address;
Expand Down
4 changes: 2 additions & 2 deletions include/safetyhook/vmt_hook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import std.compat;

namespace safetyhook {
/// @brief A hook class that allows for hooking a single method in a VMT.
class VmHook final {
class SAFETYHOOK_API VmHook final {
public:
VmHook() = default;
VmHook(const VmHook&) = delete;
Expand Down Expand Up @@ -92,7 +92,7 @@ class VmHook final {
};

/// @brief A hook class that copies an entire VMT for a given object and replaces it.
class VmtHook final {
class SAFETYHOOK_API VmtHook final {
public:
/// @brief Error type for VmtHook.
struct Error {
Expand Down
Loading