Skip to content

Commit

Permalink
Add shared library compilation support (#71)
Browse files Browse the repository at this point in the history
Co-authored-by: cursey <[email protected]>
Co-authored-by: aixxe <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2025
1 parent bb88575 commit 6bf6992
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 39 deletions.
31 changes: 25 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ option(SAFETYHOOK_BUILD_DOCS "" OFF)
option(SAFETYHOOK_BUILD_TEST "" OFF)
option(SAFETYHOOK_BUILD_EXAMPLES "" OFF)
option(SAFETYHOOK_AMALGAMATE "" OFF)
option(SAFETYHOOK_FETCH_ZYDIS "" OFF)
option(SAFETYHOOK_FETCH_ZYDIS "" ON)
option(SAFETYHOOK_USE_CXXMODULES "" OFF)

project(safetyhook)
Expand Down Expand Up @@ -67,17 +67,19 @@ if(SAFETYHOOK_FETCH_ZYDIS) # fetch-zydis
option(ZYDIS_BUILD_TOOLS "" OFF)
option(ZYDIS_BUILD_DOXYGEN "" OFF)

message(STATUS "Fetching Zydis (v4.0.0)...")
message(STATUS "Fetching Zydis (v4.1.0)...")
FetchContent_Declare(Zydis
GIT_REPOSITORY
"https://github.com/zyantific/zydis.git"
GIT_TAG
v4.0.0
v4.1.0
GIT_SHALLOW
ON
)
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
26 changes: 19 additions & 7 deletions cmake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ SAFETYHOOK_BUILD_DOCS = false
SAFETYHOOK_BUILD_TEST = false
SAFETYHOOK_BUILD_EXAMPLES = false
SAFETYHOOK_AMALGAMATE = false
SAFETYHOOK_FETCH_ZYDIS = false
SAFETYHOOK_FETCH_ZYDIS = true
SAFETYHOOK_USE_CXXMODULES = false

[conditions]
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 @@ -34,13 +36,16 @@ shallow = true
[fetch-content.Zydis]
condition = "fetch-zydis"
git = "https://github.com/zyantific/zydis.git"
tag = "v4.0.0"
tag = "v4.1.0"
shallow = true
cmake-before = """
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,9 +165,9 @@ 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 = """
add_dependencies(test-amalgamated Amalgamate)
"""
"""
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 SAFETYHOOK_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
9 changes: 5 additions & 4 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 @@ -14,7 +15,7 @@ namespace safetyhook {
/// @param destination The address of the destination function.
/// @param flags The flags to use.
/// @return The InlineHook object.
[[nodiscard]] InlineHook create_inline(void* target, void* destination, InlineHook::Flags flags = InlineHook::Default);
[[nodiscard]] InlineHook SAFETYHOOK_API create_inline(void* target, void* destination, InlineHook::Flags flags = InlineHook::Default);

/// @brief Easy to use API for creating an InlineHook.
/// @param target The address of the function to hook.
Expand All @@ -31,7 +32,7 @@ template <typename T, typename U>
/// @param destination The destination function.
/// @param flags The flags to use.
/// @return The MidHook object.
[[nodiscard]] MidHook create_mid(void* target, MidHookFn destination, MidHook::Flags flags = MidHook::Default);
[[nodiscard]] MidHook SAFETYHOOK_API create_mid(void* target, MidHookFn destination, MidHook::Flags flags = MidHook::Default);

/// @brief Easy to use API for creating a MidHook.
/// @param target the address of the function to hook.
Expand All @@ -46,7 +47,7 @@ template <typename T>
/// @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 All @@ -61,4 +62,4 @@ template <typename T> [[nodiscard]] VmHook create_vm(VmtHook& vmt, size_t index,
}
}

} // namespace safetyhook
} // namespace safetyhook
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
26 changes: 14 additions & 12 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,16 +63,16 @@ struct SystemInfo {
uint8_t* max_address;
};

SystemInfo system_info();
SystemInfo SAFETYHOOK_API system_info();

using ThreadContext = void*;

void trap_threads(uint8_t* from, uint8_t* to, size_t len, const std::function<void()>& run_fn);
void SAFETYHOOK_API trap_threads(uint8_t* from, uint8_t* to, size_t len, const std::function<void()>& run_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
} // 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 @@ -22,9 +24,9 @@ template <typename T, typename U> constexpr T address_cast(U address) {
}
}

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 @@ -34,7 +36,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 @@ -44,7 +46,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 = address_cast<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

0 comments on commit 6bf6992

Please sign in to comment.