Rename "options" to "flags"

This commit is contained in:
Zeex 2018-09-06 23:52:15 +06:00
parent 744057d486
commit 6cc020b2fd
7 changed files with 160 additions and 77 deletions

View File

@ -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).

View File

@ -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))
{
}

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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
View 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;
}