mirror of
https://github.com/Cxbx-Reloaded/subhook.git
synced 2024-12-02 08:08:32 +00:00
Rename "options" to "flags"
This commit is contained in:
parent
744057d486
commit
6cc020b2fd
18
README.md
18
README.md
@ -7,8 +7,8 @@ Linux and macOS. It supports x86 only (32-bit and 64-bit).
|
||||
Installation
|
||||
------------
|
||||
|
||||
Simply copy the files to your project and include subhook.c in your build.
|
||||
The other source files wil be `#included` by the main C file automatically
|
||||
Simply copy the files to your project and include subhook.c in your build.
|
||||
The other source files wil be `#included` by the main C file automatically
|
||||
depending on the OS and achitecture.
|
||||
|
||||
Use of CMake is not mandatory, the library can be built wihtout it (no extra
|
||||
@ -116,19 +116,19 @@ int main() {
|
||||
Known issues
|
||||
------------
|
||||
|
||||
* If a target function (the function you are hooking) is less than N bytes
|
||||
in length, for example if it's a short 2-byte jump to a nearby location
|
||||
* If a target function (the function you are hooking) is less than N bytes
|
||||
in length, for example if it's a short 2-byte jump to a nearby location
|
||||
(sometimes compilers generate code like this), then you will not be able
|
||||
to hook it.
|
||||
|
||||
N is 5 by default (1-byte jmp opcode + 32-bit offset), but it you enable
|
||||
the use of 64-bit offsets in 64-bit mode N becomes 14 (see the definition
|
||||
N is 5 by default (1-byte jmp opcode + 32-bit offset), but it you enable
|
||||
the use of 64-bit offsets in 64-bit mode N becomes 14 (see the definition
|
||||
of `subhook_jmp64`).
|
||||
|
||||
* Some systems protect executable code form being modified at runtime, which
|
||||
will not allow you to install hooks, or don't allow to mark heap-allocated
|
||||
* Some systems protect executable code form being modified at runtime, which
|
||||
will not allow you to install hooks, or don't allow to mark heap-allocated
|
||||
memory as executable, which prevents the use of trampolines.
|
||||
|
||||
|
||||
For example, on Fedora you can have such problems because of SELinux (though
|
||||
you can disable it or exclude your files).
|
||||
|
||||
|
46
subhook.h
46
subhook.h
@ -89,17 +89,17 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum subhook_options {
|
||||
/* Use 64-bit jump method on x86-64 (requires more space). */
|
||||
SUBHOOK_OPTION_64BIT_OFFSET = 1u << 1
|
||||
} subhook_options_t;
|
||||
typedef enum subhook_flags {
|
||||
/* Use the 64-bit jump method on x86-64 (requires more space). */
|
||||
SUBHOOK_64BIT_OFFSET = 1
|
||||
} subhook_flags_t;
|
||||
|
||||
struct subhook_struct;
|
||||
typedef struct subhook_struct *subhook_t;
|
||||
|
||||
SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
|
||||
void *dst,
|
||||
subhook_options_t options);
|
||||
subhook_flags_t flags);
|
||||
SUBHOOK_EXPORT void SUBHOOK_API subhook_free(subhook_t hook);
|
||||
|
||||
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_src(subhook_t hook);
|
||||
@ -121,37 +121,37 @@ SUBHOOK_EXPORT void *SUBHOOK_API subhook_read_dst(void *src);
|
||||
|
||||
namespace subhook {
|
||||
|
||||
enum HookOptions {
|
||||
HookOptionsNone = 0,
|
||||
HookOption64BitOffset = SUBHOOK_OPTION_64BIT_OFFSET
|
||||
enum HookFlags {
|
||||
HookNoFlags = 0,
|
||||
HookFlag64BitOffset = SUBHOOK_64BIT_OFFSET
|
||||
};
|
||||
|
||||
inline HookOptions operator|(HookOptions o1, HookOptions o2) {
|
||||
return static_cast<HookOptions>(
|
||||
inline HookFlags operator|(HookFlags o1, HookFlags o2) {
|
||||
return static_cast<HookFlags>(
|
||||
static_cast<unsigned int>(o1) | static_cast<unsigned int>(o2));
|
||||
}
|
||||
|
||||
inline HookOptions operator&(HookOptions o1, HookOptions o2) {
|
||||
return static_cast<HookOptions>(
|
||||
inline HookFlags operator&(HookFlags o1, HookFlags o2) {
|
||||
return static_cast<HookFlags>(
|
||||
static_cast<unsigned int>(o1) & static_cast<unsigned int>(o2));
|
||||
}
|
||||
|
||||
class Hook {
|
||||
public:
|
||||
Hook() : hook_(0) {}
|
||||
Hook(void *src,
|
||||
void *dst,
|
||||
HookOptions options = HookOptionsNone)
|
||||
: hook_(subhook_new(src, dst, (subhook_options_t)options)) {}
|
||||
Hook(void *src, void *dst, HookFlags flags = HookNoFlags)
|
||||
: hook_(subhook_new(src, dst, (subhook_flags_t)flags))
|
||||
{
|
||||
}
|
||||
|
||||
~Hook() {
|
||||
subhook_remove(hook_);
|
||||
subhook_free(hook_);
|
||||
}
|
||||
|
||||
void *GetSrc() { return subhook_get_src(hook_); }
|
||||
void *GetDst() { return subhook_get_dst(hook_); }
|
||||
void *GetTrampoline() { return subhook_get_trampoline(hook_); }
|
||||
void *GetSrc() const { return subhook_get_src(hook_); }
|
||||
void *GetDst() const { return subhook_get_dst(hook_); }
|
||||
void *GetTrampoline() const { return subhook_get_trampoline(hook_); }
|
||||
|
||||
bool Install() {
|
||||
return subhook_install(hook_) >= 0;
|
||||
@ -159,9 +159,9 @@ class Hook {
|
||||
|
||||
bool Install(void *src,
|
||||
void *dst,
|
||||
HookOptions options = HookOptionsNone) {
|
||||
HookFlags flags = HookNoFlags) {
|
||||
if (hook_ == 0) {
|
||||
hook_ = subhook_new(src, dst, (subhook_options_t)options);
|
||||
hook_ = subhook_new(src, dst, (subhook_flags_t)flags);
|
||||
}
|
||||
return Install();
|
||||
}
|
||||
@ -220,9 +220,9 @@ class ScopedHookInstall {
|
||||
ScopedHookInstall(Hook *hook,
|
||||
void *src,
|
||||
void *dst,
|
||||
HookOptions options = HookOptionsNone)
|
||||
HookFlags flags = HookNoFlags)
|
||||
: hook_(hook)
|
||||
, installed_(hook_->Install(src, dst, options))
|
||||
, installed_(hook_->Install(src, dst, flags))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ struct subhook_struct {
|
||||
int installed;
|
||||
void *src;
|
||||
void *dst;
|
||||
subhook_options_t options;
|
||||
subhook_flags_t flags;
|
||||
void *code;
|
||||
void *trampoline;
|
||||
size_t jmp_size;
|
||||
|
@ -266,13 +266,13 @@ static size_t subhook_disasm(void *src, int32_t *reloc_op_offset) {
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t subhook_get_jmp_size(subhook_options_t options) {
|
||||
static size_t subhook_get_jmp_size(subhook_flags_t flags) {
|
||||
#ifdef SUBHOOK_X86_64
|
||||
if ((options & SUBHOOK_OPTION_64BIT_OFFSET) != 0) {
|
||||
if ((flags & SUBHOOK_64BIT_OFFSET) != 0) {
|
||||
return sizeof(struct subhook_jmp64);
|
||||
}
|
||||
#else
|
||||
(void)options;
|
||||
(void)flags;
|
||||
#endif
|
||||
return sizeof(struct subhook_jmp32);
|
||||
}
|
||||
@ -318,13 +318,13 @@ static int subhook_make_jmp64(void *src, void *dst) {
|
||||
|
||||
static int subhook_make_jmp(void *src,
|
||||
void *dst,
|
||||
subhook_options_t options) {
|
||||
subhook_flags_t flags) {
|
||||
#ifdef SUBHOOK_X86_64
|
||||
if ((options & SUBHOOK_OPTION_64BIT_OFFSET) != 0) {
|
||||
if ((flags & SUBHOOK_64BIT_OFFSET) != 0) {
|
||||
return subhook_make_jmp64(src, dst);
|
||||
}
|
||||
#else
|
||||
(void)options;
|
||||
(void)flags;
|
||||
#endif
|
||||
return subhook_make_jmp32(src, dst);
|
||||
}
|
||||
@ -333,7 +333,7 @@ static int subhook_make_trampoline(void *trampoline,
|
||||
void *src,
|
||||
size_t jmp_size,
|
||||
size_t *trampoline_len,
|
||||
subhook_options_t options) {
|
||||
subhook_flags_t flags) {
|
||||
size_t orig_size = 0;
|
||||
size_t insn_len;
|
||||
intptr_t trampoline_addr = (intptr_t)trampoline;
|
||||
@ -383,12 +383,12 @@ static int subhook_make_trampoline(void *trampoline,
|
||||
*/
|
||||
return subhook_make_jmp((void *)(trampoline_addr + orig_size),
|
||||
(void *)(src_addr + orig_size),
|
||||
options);
|
||||
flags);
|
||||
}
|
||||
|
||||
SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
|
||||
void *dst,
|
||||
subhook_options_t options) {
|
||||
subhook_flags_t flags) {
|
||||
subhook_t hook;
|
||||
|
||||
if ((hook = malloc(sizeof(*hook))) == NULL) {
|
||||
@ -398,8 +398,8 @@ SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
|
||||
hook->installed = 0;
|
||||
hook->src = src;
|
||||
hook->dst = dst;
|
||||
hook->options = options;
|
||||
hook->jmp_size = subhook_get_jmp_size(hook->options);
|
||||
hook->flags = flags;
|
||||
hook->jmp_size = subhook_get_jmp_size(hook->flags);
|
||||
hook->trampoline_size = hook->jmp_size * 2 + MAX_INSN_LEN;
|
||||
hook->trampoline_len = 0;
|
||||
|
||||
@ -430,7 +430,7 @@ SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
|
||||
hook->src,
|
||||
hook->jmp_size,
|
||||
&hook->trampoline_len,
|
||||
hook->options);
|
||||
hook->flags);
|
||||
|
||||
if (hook->trampoline_len == 0) {
|
||||
free(hook->trampoline);
|
||||
@ -459,7 +459,7 @@ SUBHOOK_EXPORT int SUBHOOK_API subhook_install(subhook_t hook) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = subhook_make_jmp(hook->src, hook->dst, hook->options);
|
||||
error = subhook_make_jmp(hook->src, hook->dst, hook->flags);
|
||||
if (error >= 0) {
|
||||
hook->installed = true;
|
||||
return 0;
|
||||
|
@ -40,39 +40,46 @@ add_custom_command(
|
||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${asm_file}"
|
||||
)
|
||||
|
||||
add_executable(subhook_test
|
||||
add_executable(subhook_test_exe
|
||||
test.c
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${asm_file}.obj"
|
||||
)
|
||||
set_target_properties(subhook_test_exe PROPERTIES OUTPUT_NAME test)
|
||||
|
||||
set_target_properties(subhook_test PROPERTIES OUTPUT_NAME test)
|
||||
enable_language(CXX)
|
||||
add_executable(subhook_cxx_test_exe
|
||||
test.cpp
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${asm_file}.obj"
|
||||
)
|
||||
set_target_properties(subhook_cxx_test_exe PROPERTIES OUTPUT_NAME test++)
|
||||
|
||||
if(SUBHOOK_FORCE_32BIT)
|
||||
if(APPLE)
|
||||
set_target_properties(subhook_test PROPERTIES OSX_ARCHITECTURES i386)
|
||||
foreach(target subhook_test_exe subhook_cxx_test_exe)
|
||||
if(SUBHOOK_FORCE_32BIT)
|
||||
if(APPLE)
|
||||
set_target_properties(${target} PROPERTIES OSX_ARCHITECTURES i386)
|
||||
endif()
|
||||
if(UNIX)
|
||||
set_property(TARGET ${target} APPEND_STRING PROPERTY
|
||||
COMPILE_FLAGS " -m32")
|
||||
set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " -m32")
|
||||
endif()
|
||||
endif()
|
||||
if(UNIX)
|
||||
set_property(TARGET subhook_test APPEND_STRING PROPERTY
|
||||
COMPILE_FLAGS " -m32")
|
||||
set_property(TARGET subhook_test APPEND_STRING PROPERTY LINK_FLAGS " -m32")
|
||||
|
||||
target_link_libraries(${target} subhook)
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET ${target}
|
||||
APPEND_STRING PROPERTY LINK_FLAGS " /INCREMENTAL:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(subhook_test subhook)
|
||||
if(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT SUBHOOK_FORCE_32BIT)
|
||||
set_property(TARGET ${target} APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,-no_pie")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET subhook_test
|
||||
APPEND_STRING PROPERTY LINK_FLAGS " /INCREMENTAL:NO")
|
||||
endif()
|
||||
add_test(NAME ${target}_test COMMAND $<TARGET_FILE:${target}>)
|
||||
|
||||
if(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT SUBHOOK_FORCE_32BIT)
|
||||
set_property(TARGET subhook_test APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,-no_pie")
|
||||
endif()
|
||||
|
||||
add_test(NAME test COMMAND $<TARGET_FILE:subhook_test>)
|
||||
|
||||
set(expected_output "\
|
||||
set(expected_output "\
|
||||
Testing initial install[\r\n]+\
|
||||
foo_hooked\\(\\) called[\r\n]+\
|
||||
foo\\(\\) called[\r\n]+\
|
||||
@ -83,10 +90,11 @@ Testing trampoline[\r\n]+\
|
||||
foo_hooked_tr\\(\\) called[\r\n]+\
|
||||
foo\\(\\) called[\r\n]+\
|
||||
")
|
||||
set_tests_properties(test PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "${expected_output}")
|
||||
set_tests_properties(${target}_test PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "${expected_output}")
|
||||
|
||||
if(WIN32 AND NOT SUBHOOK_STATIC)
|
||||
set_tests_properties(test PROPERTIES
|
||||
ENVIRONMENT PATH=$<TARGET_FILE_DIR:subhook>)
|
||||
endif()
|
||||
if(WIN32 AND NOT SUBHOOK_STATIC)
|
||||
set_tests_properties(${target}_test PROPERTIES
|
||||
ENVIRONMENT PATH=$<TARGET_FILE_DIR:subhook>)
|
||||
endif()
|
||||
endforeach()
|
||||
|
@ -32,7 +32,7 @@ int main() {
|
||||
|
||||
subhook_t foo_hook = subhook_new((void *)foo,
|
||||
(void *)foo_hooked,
|
||||
SUBHOOK_OPTION_64BIT_OFFSET);
|
||||
SUBHOOK_64BIT_OFFSET);
|
||||
if (foo_hook == NULL || subhook_install(foo_hook) < 0) {
|
||||
puts("Install failed");
|
||||
return EXIT_FAILURE;
|
||||
@ -61,7 +61,7 @@ int main() {
|
||||
|
||||
subhook_t foo_hook_tr = subhook_new((void *)foo,
|
||||
(void *)foo_hooked_tr,
|
||||
SUBHOOK_OPTION_64BIT_OFFSET);
|
||||
SUBHOOK_64BIT_OFFSET);
|
||||
if (subhook_install(foo_hook_tr) < 0) {
|
||||
puts("Install failed");
|
||||
return EXIT_FAILURE;
|
||||
|
75
tests/test.cpp
Normal file
75
tests/test.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include <iostream>
|
||||
#include <subhook.h>
|
||||
|
||||
typedef void (*foo_func_t)();
|
||||
|
||||
#ifdef SUBHOOK_X86
|
||||
#if defined SUBHOOK_WINDOWS
|
||||
#define FOO_CALL __cdecl
|
||||
#elif defined SUBHOOK_UNIX
|
||||
#define FOO_CALL __attribute__((cdecl))
|
||||
#endif
|
||||
#else
|
||||
#define FOO_CALL
|
||||
#endif
|
||||
|
||||
extern "C" void FOO_CALL foo();
|
||||
foo_func_t foo_tr = nullptr;
|
||||
|
||||
void foo_hooked() {
|
||||
std::cout << "foo_hooked() called" << std::endl;;
|
||||
}
|
||||
|
||||
void foo_hooked_tr() {
|
||||
std::cout << "foo_hooked_tr() called" << std::endl;
|
||||
foo_tr();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Testing initial install" << std::endl;
|
||||
|
||||
subhook::Hook foo_hook((void *)foo,
|
||||
(void *)foo_hooked,
|
||||
subhook::HookFlag64BitOffset);
|
||||
if (!foo_hook.Install()) {
|
||||
std::cout << "Install failed" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo();
|
||||
if (!foo_hook.Remove()) {
|
||||
std::cout << "Remove failed" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo();
|
||||
|
||||
std::cout << "Testing re-install" << std::endl;
|
||||
|
||||
if (!foo_hook.Install()) {
|
||||
std::cout << "Install failed" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo();
|
||||
if (!foo_hook.Remove()) {
|
||||
std::cout << "Remove failed" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo();
|
||||
|
||||
std::cout << "Testing trampoline" << std::endl;
|
||||
|
||||
subhook::Hook foo_hook_tr((void *)foo,
|
||||
(void *)foo_hooked_tr,
|
||||
subhook::HookFlag64BitOffset);
|
||||
if (!foo_hook_tr.Install()) {
|
||||
std::cout << "Install failed" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo_tr = (foo_func_t)foo_hook_tr.GetTrampoline();
|
||||
if (foo_tr == nullptr) {
|
||||
std::cout << "Failed to build trampoline" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foo();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user