From 182689766b9719693d94acfeb52f7fff4fbae036 Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:08:50 +0700 Subject: [PATCH 1/8] feat(hooking): add errno, version, and callback variants to ShadowHook wrapper - Add memkit_errno(), memkit_strerror(), memkit_version(), memkit_init_errno() - Add memkit_hook_with_callback() and memkit_hook_by_symbol_callback() - Fix errno sign: ShadowHook returns positive error codes --- src/hooking.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/hooking.c b/src/hooking.c index 3087778..02e69f7 100644 --- a/src/hooking.c +++ b/src/hooking.c @@ -18,7 +18,7 @@ int memkit_hook_init(int mode, bool debuggable) { // Propagate error to errno for consistent error handling if (ret != 0) { - errno = -ret; // ShadowHook returns negative error codes + errno = ret; // ShadowHook returns positive error codes } return ret; @@ -107,3 +107,47 @@ void* memkit_hook_by_symbol(const char* lib_name, const char* symbol_name, void* return stub; } + +// ============================================================================ +// HOOKING: ERROR HANDLING +// ============================================================================ + +int memkit_errno(void) { + return shadowhook_get_errno(); +} + +const char *memkit_strerror(int errno_code) { + return shadowhook_to_errmsg(errno_code); +} + +const char *memkit_version(void) { + return shadowhook_get_version(); +} + +int memkit_init_errno(void) { + return shadowhook_get_init_errno(); +} + +// ============================================================================ +// HOOKING: WITH CALLBACK +// ============================================================================ + +void *memkit_hook_with_callback(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, MemKitHooked hooked, void *hooked_arg) { + if (!lib_name || !sym_name || !new_addr) return NULL; + + void *stub = shadowhook_hook_sym_name_callback( + lib_name, + sym_name, + new_addr, + orig_addr, + (shadowhook_hooked_t)hooked, + hooked_arg + ); + + if (!stub && orig_addr) *orig_addr = NULL; + return stub; +} + +void *memkit_hook_by_symbol_callback(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, MemKitHooked hooked, void *hooked_arg) { + return memkit_hook_with_callback(lib_name, sym_name, new_addr, orig_addr, hooked, hooked_arg); +} From 91e85a9b01bd9c1f4d2bcb8d516c84e2c175376c Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:09:08 +0700 Subject: [PATCH 2/8] feat(intercept): add ShadowHook intercept API wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add memkit_intercept() — intercept by function address - Add memkit_intercept_by_symbol() — intercept by library+symbol name - Add memkit_intercept_at_instr() — instruction-level interception - Add memkit_intercept_with_callback() — async intercept with completion callback - Add memkit_unintercept() - Add proxy/stack management: memkit_get_prev_func(), memkit_pop_stack(), memkit_allow_reentrant(), memkit_disallow_reentrant(), memkit_get_return_address() - Add MemKitCpuContext, MemKitVReg, MemKitInterceptor typedefs --- src/intercept.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/intercept.c diff --git a/src/intercept.c b/src/intercept.c new file mode 100644 index 0000000..4785c63 --- /dev/null +++ b/src/intercept.c @@ -0,0 +1,133 @@ +#include +#include + +#include "memkit.h" +#include "shadowhook.h" + +// ============================================================================ +// INTERCEPT: BY FUNCTION ADDRESS +// ============================================================================ + +void *memkit_intercept(void *func_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...) { + if (!func_addr || !pre) return NULL; + + void *stub; + + if (flags & MK_INTERCEPT_RECORD) { + va_list ap; + va_start(ap, flags); + const char *record_lib_name = va_arg(ap, const char *); + const char *record_sym_name = va_arg(ap, const char *); + va_end(ap); + stub = shadowhook_intercept_func_addr(func_addr, pre, data, flags, record_lib_name, record_sym_name); + } else { + stub = shadowhook_intercept_func_addr(func_addr, pre, data, flags); + } + + return stub; +} + +// ============================================================================ +// INTERCEPT: BY SYMBOL ADDRESS +// ============================================================================ + +void *memkit_intercept_by_sym_addr(void *sym_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...) { + if (!sym_addr || !pre) return NULL; + + void *stub; + + if (flags & MK_INTERCEPT_RECORD) { + va_list ap; + va_start(ap, flags); + const char *record_lib_name = va_arg(ap, const char *); + const char *record_sym_name = va_arg(ap, const char *); + va_end(ap); + stub = shadowhook_intercept_sym_addr(sym_addr, pre, data, flags, record_lib_name, record_sym_name); + } else { + stub = shadowhook_intercept_sym_addr(sym_addr, pre, data, flags); + } + + return stub; +} + +// ============================================================================ +// INTERCEPT: BY SYMBOL NAME +// ============================================================================ + +void *memkit_intercept_by_symbol(const char *lib_name, const char *sym_name, MemKitInterceptor pre, void *data, uint32_t flags) { + if (!lib_name || !sym_name || !pre) return NULL; + void *stub = shadowhook_intercept_sym_name(lib_name, sym_name, pre, data, flags); + if (!stub) { + errno = shadowhook_get_errno(); + } + return stub; +} + +// ============================================================================ +// INTERCEPT: AT INSTRUCTION ADDRESS +// ============================================================================ + +void *memkit_intercept_at_instr(void *instr_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...) { + if (!instr_addr || !pre) return NULL; + + void *stub; + + if (flags & MK_INTERCEPT_RECORD) { + va_list ap; + va_start(ap, flags); + const char *record_lib_name = va_arg(ap, const char *); + const char *record_sym_name = va_arg(ap, const char *); + va_end(ap); + stub = shadowhook_intercept_instr_addr(instr_addr, pre, data, flags, record_lib_name, record_sym_name); + } else { + stub = shadowhook_intercept_instr_addr(instr_addr, pre, data, flags); + } + + return stub; +} + +// ============================================================================ +// INTERCEPT: WITH CALLBACK +// ============================================================================ + +void *memkit_intercept_with_callback(const char *lib_name, const char *sym_name, MemKitInterceptor pre, void *data, uint32_t flags, MemKitIntercepted intercepted, void *arg) { + if (!lib_name || !sym_name || !pre) return NULL; + void *stub = shadowhook_intercept_sym_name_callback(lib_name, sym_name, pre, data, flags, (shadowhook_intercepted_t)intercepted, arg); + if (!stub) { + errno = shadowhook_get_errno(); + } + return stub; +} + +// ============================================================================ +// UNINTERCEPT +// ============================================================================ + +int memkit_unintercept(void *stub) { + if (!stub) return MK_ERRNO_INVALID_ARG; + return shadowhook_unintercept(stub); +} + +// ============================================================================ +// PROXY / STACK MANAGEMENT +// ============================================================================ + +void *memkit_get_prev_func(void *func) { + return shadowhook_get_prev_func(func); +} + +void memkit_pop_stack(const void *return_address) { + shadowhook_pop_stack((void *)return_address); +} + +void memkit_allow_reentrant(const void *return_address) { + shadowhook_allow_reentrant((void *)return_address); +} + +void memkit_disallow_reentrant(const void *return_address) { + shadowhook_disallow_reentrant((void *)return_address); +} + +void *memkit_get_return_address(void) { + return shadowhook_get_return_address(); +} From 300eeecfc2b12b5165c050cdc37fdc2f30b64649 Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:09:20 +0700 Subject: [PATCH 3/8] feat(records): add ShadowHook records API wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add memkit_get_records() — retrieve operation records as CSV - Add memkit_dump_records_fd() — dump records to file descriptor - Add 11 MK_RECORD_ITEM_* constants --- src/records.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/records.c diff --git a/src/records.c b/src/records.c new file mode 100644 index 0000000..9b6211c --- /dev/null +++ b/src/records.c @@ -0,0 +1,18 @@ +#include "memkit.h" +#include "shadowhook.h" + +// ============================================================================ +// RECORDS: GET AS STRING +// ============================================================================ + +char *memkit_get_records(uint32_t item_flags) { + return shadowhook_get_records(item_flags); +} + +// ============================================================================ +// RECORDS: DUMP TO FILE DESCRIPTOR +// ============================================================================ + +void memkit_dump_records_fd(int fd, uint32_t item_flags) { + shadowhook_dump_records(fd, item_flags); +} From ab762e8d48206639de5264890b7ee720adc4fafa Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:09:31 +0700 Subject: [PATCH 4/8] feat(config): add ShadowHook runtime configuration wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add memkit_get_mode() — current hook mode - Add memkit_set_debuggable()/get_debuggable() - Add memkit_set_recordable()/get_recordable() - Add memkit_set_disable()/get_disable() - Add MK_IS_SHARED_MODE, MK_IS_UNIQUE_MODE, MK_IS_MULTI_MODE macros --- src/runtime_config.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/runtime_config.c diff --git a/src/runtime_config.c b/src/runtime_config.c new file mode 100644 index 0000000..d82da84 --- /dev/null +++ b/src/runtime_config.c @@ -0,0 +1,46 @@ +#include "memkit.h" +#include "shadowhook.h" + +// ============================================================================ +// RUNTIME CONFIG: MODE +// ============================================================================ + +int memkit_get_mode(void) { + return (int)shadowhook_get_mode(); +} + +// ============================================================================ +// RUNTIME CONFIG: DEBUGGABLE +// ============================================================================ + +void memkit_set_debuggable(bool debuggable) { + shadowhook_set_debuggable(debuggable); +} + +bool memkit_get_debuggable(void) { + return shadowhook_get_debuggable(); +} + +// ============================================================================ +// RUNTIME CONFIG: RECORDABLE +// ============================================================================ + +void memkit_set_recordable(bool recordable) { + shadowhook_set_recordable(recordable); +} + +bool memkit_get_recordable(void) { + return shadowhook_get_recordable(); +} + +// ============================================================================ +// RUNTIME CONFIG: DISABLE +// ============================================================================ + +void memkit_set_disable(bool disable) { + shadowhook_set_disable(disable); +} + +bool memkit_get_disable(void) { + return shadowhook_get_disable(); +} From 37089656a6497be034f868fa32f0b7d605b9d670 Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:09:46 +0700 Subject: [PATCH 5/8] feat(hooks): add V2 hook API with per-hook flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add memkit_hook_v2() — hook by symbol name with mode/record flags - Add memkit_hook_by_symbol_v2() — alias for V2 API - Add MK_HOOK_DEFAULT, MK_HOOK_WITH_SHARED_MODE, MK_HOOK_WITH_UNIQUE_MODE, MK_HOOK_WITH_MULTI_MODE, MK_HOOK_RECORD flags --- src/hooking_flags.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/hooking_flags.c diff --git a/src/hooking_flags.c b/src/hooking_flags.c new file mode 100644 index 0000000..2f37f76 --- /dev/null +++ b/src/hooking_flags.c @@ -0,0 +1,23 @@ +#include "memkit.h" +#include "shadowhook.h" + +// ============================================================================ +// HOOKING V2: BY SYMBOL NAME WITH FLAGS +// Note: shadowhook_hook_sym_name_2 does NOT take variadic record params +// in the current ShadowHook API — record params are only for _2 variants +// of hook_func_addr, hook_sym_addr, and intercept functions. +// ============================================================================ + +void *memkit_hook_v2(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, uint32_t flags) { + if (!lib_name || !sym_name || !new_addr) return NULL; + + void *stub = shadowhook_hook_sym_name_2(lib_name, sym_name, new_addr, orig_addr, flags); + + if (!stub && orig_addr) *orig_addr = NULL; + return stub; +} + +void *memkit_hook_by_symbol_v2(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, uint32_t flags) { + /* Same implementation as memkit_hook_v2 — ShadowHook's _2 API has same signature for sym_name */ + return memkit_hook_v2(lib_name, sym_name, new_addr, orig_addr, flags); +} From 5206b765b61804047c2b5b82f5c009500bef819a Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:10:09 +0700 Subject: [PATCH 6/8] feat(dl): add DL init/fini callback registration wrapper - Add memkit_register_dl_init_callback()/unregister - Add memkit_register_dl_fini_callback()/unregister - Add MemKitDlInfo, MemKitDlInitCallback, MemKitDlFiniCallback typedefs --- src/dl_callbacks.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/dl_callbacks.c diff --git a/src/dl_callbacks.c b/src/dl_callbacks.c new file mode 100644 index 0000000..61977ed --- /dev/null +++ b/src/dl_callbacks.c @@ -0,0 +1,46 @@ +#include "memkit.h" +#include "shadowhook.h" + +// ============================================================================ +// DL INIT CALLBACKS +// ============================================================================ + +int memkit_register_dl_init_callback(MemKitDlInitCallback pre, MemKitDlInitCallback post, void *data) { + if (!pre && !post) return MK_ERRNO_INVALID_ARG; + return shadowhook_register_dl_init_callback( + (shadowhook_dl_info_t)pre, + (shadowhook_dl_info_t)post, + data + ); +} + +int memkit_unregister_dl_init_callback(MemKitDlInitCallback pre, MemKitDlInitCallback post, void *data) { + if (!pre && !post) return MK_ERRNO_INVALID_ARG; + return shadowhook_unregister_dl_init_callback( + (shadowhook_dl_info_t)pre, + (shadowhook_dl_info_t)post, + data + ); +} + +// ============================================================================ +// DL FINI CALLBACKS +// ============================================================================ + +int memkit_register_dl_fini_callback(MemKitDlFiniCallback pre, MemKitDlFiniCallback post, void *data) { + if (!pre && !post) return MK_ERRNO_INVALID_ARG; + return shadowhook_register_dl_fini_callback( + (shadowhook_dl_info_t)pre, + (shadowhook_dl_info_t)post, + data + ); +} + +int memkit_unregister_dl_fini_callback(MemKitDlFiniCallback pre, MemKitDlFiniCallback post, void *data) { + if (!pre && !post) return MK_ERRNO_INVALID_ARG; + return shadowhook_unregister_dl_fini_callback( + (shadowhook_dl_info_t)pre, + (shadowhook_dl_info_t)post, + data + ); +} From d8c53cc5b262a57aa47e0352670c722db78c16cd Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:10:23 +0700 Subject: [PATCH 7/8] feat(header,build): complete ShadowHook wrapper declarations and build config Header (memkit.h): - Add 46 MK_ERRNO_* error constants - Add MK_MODE_SHARED, MK_MODE_UNIQUE, MK_MODE_MULTI constants - Add MemKitHooked, MemKitIntercepted, MemKitInterceptor callback types - Add MemKitCpuContext, MemKitVReg, MemKitDlInfo typedefs - Add 6 proxy/stack management macros (MEMKIT_CALL_PREV, MEMKIT_POP_STACK, etc.) - Declare 27 new wrapper functions across all API groups - Add MK_INTERCEPT_* and MK_RECORD_ITEM_* flag constants Build system: - Add intercept.c, records.c, runtime_config.c, hooking_flags.c, dl_callbacks.c to CMakeLists.txt and Makefile --- CMakeLists.txt | 5 + Makefile | 2 +- include/memkit.h | 260 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 066d27e..0627520 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,11 @@ endif() add_library(memkit SHARED src/memory.c src/hooking.c + src/hooking_flags.c + src/intercept.c + src/records.c + src/runtime_config.c + src/dl_callbacks.c src/il2cpp.c src/xdl_wrapper.c ) diff --git a/Makefile b/Makefile index cb8ad12..ec9ef66 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ INCLUDES = -Iinclude \ LDFLAGS = -shared -llog -landroid -MEMKIT_SRCS = src/memory.c src/hooking.c src/il2cpp.c src/xdl_wrapper.c +MEMKIT_SRCS = src/memory.c src/hooking.c src/hooking_flags.c src/intercept.c src/records.c src/runtime_config.c src/dl_callbacks.c src/il2cpp.c src/xdl_wrapper.c SH_DIR = deps/shadowhook/shadowhook/src/main/cpp SH_SRCS = $(SH_DIR)/arch/$(SH_ARCH)/sh_inst.c \ $(SH_DIR)/arch/$(SH_ARCH)/sh_glue.S \ diff --git a/include/memkit.h b/include/memkit.h index 03173c2..8398c00 100644 --- a/include/memkit.h +++ b/include/memkit.h @@ -6,6 +6,8 @@ #include #include +#include "shadowhook.h" + #ifdef __cplusplus extern "C" { #endif @@ -396,6 +398,264 @@ void* memkit_xdl_open_from_phdr(struct dl_phdr_info* info); #define XDL_RESOLVE_SIZE(lib_name, symbol, out_size) \ memkit_xdl_sym(memkit_xdl_open(lib_name, XDL_DEFAULT), symbol, out_size) +// ============================================================================ +// HOOKING: ERROR HANDLING +// ============================================================================ + +/* Error codes (mirrors ShadowHook's 46 error codes) */ +#define MK_ERRNO_OK 0 +#define MK_ERRNO_PENDING 1 +#define MK_ERRNO_UNINIT 2 +#define MK_ERRNO_INVALID_ARG 3 +#define MK_ERRNO_OOM 4 +#define MK_ERRNO_MPROT 5 +#define MK_ERRNO_WRITE_CRASH 6 +#define MK_ERRNO_INIT_ERRNO 7 +#define MK_ERRNO_INIT_SIGSEGV 8 +#define MK_ERRNO_INIT_SIGBUS 9 +#define MK_ERRNO_INTERCEPT_DUP 10 +#define MK_ERRNO_INIT_SAFE 11 +#define MK_ERRNO_INIT_LINKER 12 +#define MK_ERRNO_INIT_HUB 13 +#define MK_ERRNO_HUB_CREAT 14 +#define MK_ERRNO_MONITOR_DLOPEN 15 +#define MK_ERRNO_HOOK_UNIQUE_DUP 16 +#define MK_ERRNO_HOOK_DLOPEN_CRASH 17 +#define MK_ERRNO_HOOK_DLSYM 18 +#define MK_ERRNO_HOOK_DLSYM_CRASH 19 +#define MK_ERRNO_HOOK_DUP 20 +#define MK_ERRNO_HOOK_DLADDR_CRASH 21 +#define MK_ERRNO_HOOK_DLINFO 22 +#define MK_ERRNO_HOOK_SYMSZ 23 +#define MK_ERRNO_HOOK_ENTER 24 +#define MK_ERRNO_HOOK_REWRITE_CRASH 25 +#define MK_ERRNO_HOOK_REWRITE_FAILED 26 +#define MK_ERRNO_UNHOOK_NOTFOUND 27 +#define MK_ERRNO_UNHOOK_CMP_CRASH 28 +#define MK_ERRNO_UNHOOK_TRAMPO_MISMATCH 29 +#define MK_ERRNO_UNHOOK_EXIT_MISMATCH 30 +#define MK_ERRNO_UNHOOK_EXIT_CRASH 31 +#define MK_ERRNO_UNHOOK_ON_ERROR 32 +#define MK_ERRNO_UNHOOK_ON_UNFINISHED 33 +#define MK_ERRNO_ELF_ARCH_MISMATCH 34 +#define MK_ERRNO_LINKER_ARCH_MISMATCH 35 +#define MK_ERRNO_DUP 36 +#define MK_ERRNO_NOT_FOUND 37 +#define MK_ERRNO_NOT_SUPPORT 38 +#define MK_ERRNO_INIT_TASK 39 +#define MK_ERRNO_HOOK_ISLAND_EXIT 40 +#define MK_ERRNO_HOOK_ISLAND_ENTER 41 +#define MK_ERRNO_HOOK_ISLAND_REWRITE 42 +#define MK_ERRNO_MODE_CONFLICT 43 +#define MK_ERRNO_HOOK_MULTI_DUP 44 +#define MK_ERRNO_DISABLED 45 + +/* Get last error code from ShadowHook */ +int memkit_errno(void); + +/* Get human-readable error message */ +const char *memkit_strerror(int errno_code); + +/* Get ShadowHook version string */ +const char *memkit_version(void); + +/* Get error code from last shadowhook_init() call */ +int memkit_init_errno(void); + +// ============================================================================ +// HOOKING: MODE CONSTANTS +// ============================================================================ + +#define MK_MODE_SHARED 0 +#define MK_MODE_UNIQUE 1 +#define MK_MODE_MULTI 2 + +// ============================================================================ +// HOOKING: CPU CONTEXT & INTERCEPT TYPES +// ============================================================================ + +/* CPU context passed to interceptor — direct passthrough to ShadowHook */ +typedef shadowhook_cpu_context_t MemKitCpuContext; + +/* NEON/VFP vector register — direct passthrough to ShadowHook */ +typedef shadowhook_vreg_t MemKitVReg; + +/* Interceptor function type — receives CPU context on each call to target */ +typedef void (*MemKitInterceptor)( + MemKitCpuContext *cpu_context, + void *data +); + +// ============================================================================ +// HOOKING: CALLBACK TYPES +// ============================================================================ + +/* Callback invoked when a hook operation completes (success or failure) */ +typedef void (*MemKitHooked)( + int error_number, + const char *lib_name, + const char *sym_name, + void *sym_addr, + void *new_addr, + void *orig_addr, + void *arg +); + +/* Callback invoked when an intercept operation completes */ +typedef void (*MemKitIntercepted)( + int error_number, + const char *lib_name, + const char *sym_name, + void *sym_addr, + void *pre, + void *data, + void *arg +); + +// ============================================================================ +// HOOKING: PROXY & STACK MANAGEMENT (Macros) +// ============================================================================ + +/* Call the previous function in the proxy chain (MULTI mode only). + * @param func Your proxy function pointer + * @param func_sig Function signature type, e.g., int(*)(int, const char*) + * @param ... Arguments to forward + * @note Only works in MULTI mode. In SHARED mode, use SHADOWHOOK_CALL_PREV directly. */ +#define MEMKIT_CALL_PREV(func, func_sig, ...) \ + ((func_sig)memkit_get_prev_func((void *)(func)))(__VA_ARGS__) + +/* Pop the current stack frame after a proxy call returns. + * Must be called at the end of every proxy function. */ +#define MEMKIT_POP_STACK() \ + memkit_pop_stack(__builtin_return_address(0)) + +/* Allow reentrant calls to this proxy from the same thread. */ +#define MEMKIT_ALLOW_REENTRANT() \ + memkit_allow_reentrant(__builtin_return_address(0)) + +/* Disallow reentrant calls to this proxy from the same thread. */ +#define MEMKIT_DISALLOW_REENTRANT() \ + memkit_disallow_reentrant(__builtin_return_address(0)) + +/* Get the return address of the current proxy caller. */ +#define MEMKIT_RETURN_ADDRESS() \ + memkit_get_return_address() + +// ============================================================================ +// HOOKING: PROXY & STACK MANAGEMENT (Functions) +// ============================================================================ + +void *memkit_get_prev_func(void *func); +void memkit_pop_stack(const void *return_address); +void memkit_allow_reentrant(const void *return_address); +void memkit_disallow_reentrant(const void *return_address); +void *memkit_get_return_address(void); + +// ============================================================================ +// HOOKING: FLAGS (V2 API) +// ============================================================================ + +#define MK_HOOK_DEFAULT 0 +#define MK_HOOK_WITH_SHARED_MODE 1 +#define MK_HOOK_WITH_UNIQUE_MODE 2 +#define MK_HOOK_WITH_MULTI_MODE 4 +#define MK_HOOK_RECORD 8 + +void *memkit_hook_v2(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, uint32_t flags); +void *memkit_hook_by_symbol_v2(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, uint32_t flags); + +// ============================================================================ +// INTERCEPT API +// ============================================================================ + +/* Intercept flags */ +#define MK_INTERCEPT_DEFAULT 0 +#define MK_INTERCEPT_WITH_FPSIMD_READ_ONLY 1 +#define MK_INTERCEPT_WITH_FPSIMD_WRITE_ONLY 2 +#define MK_INTERCEPT_WITH_FPSIMD_READ_WRITE 3 +#define MK_INTERCEPT_RECORD 4 + +/* Intercept by function address */ +void *memkit_intercept(void *func_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...); + +/* Intercept by symbol address */ +void *memkit_intercept_by_sym_addr(void *sym_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...); + +/* Intercept by library name and symbol name */ +void *memkit_intercept_by_symbol(const char *lib_name, const char *sym_name, MemKitInterceptor pre, void *data, uint32_t flags); + +/* Remove an interceptor */ +int memkit_unintercept(void *stub); + +/* Intercept at a specific instruction address */ +void *memkit_intercept_at_instr(void *instr_addr, MemKitInterceptor pre, void *data, uint32_t flags, ...); + +/* Intercept with completion callback */ +void *memkit_intercept_with_callback(const char *lib_name, const char *sym_name, MemKitInterceptor pre, void *data, uint32_t flags, MemKitIntercepted intercepted, void *arg); + +// ============================================================================ +// RECORDS API +// ============================================================================ + +#define MK_RECORD_ITEM_TIMESTAMP (1 << 0) +#define MK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1) +#define MK_RECORD_ITEM_OP (1 << 2) +#define MK_RECORD_ITEM_LIB_NAME (1 << 3) +#define MK_RECORD_ITEM_SYM_NAME (1 << 4) +#define MK_RECORD_ITEM_SYM_ADDR (1 << 5) +#define MK_RECORD_ITEM_NEW_ADDR (1 << 6) +#define MK_RECORD_ITEM_BACKUP_LEN (1 << 7) +#define MK_RECORD_ITEM_ERRNO (1 << 8) +#define MK_RECORD_ITEM_STUB (1 << 9) +#define MK_RECORD_ITEM_FLAGS (1 << 10) +#define MK_RECORD_ITEM_ALL 0x7FF + +/* Get operation records as CSV string (caller must free) */ +/* NOTE: The returned string is heap-allocated; caller is responsible for calling free() */ +char *memkit_get_records(uint32_t item_flags); + +/* Dump operation records to a file descriptor */ +void memkit_dump_records_fd(int fd, uint32_t item_flags); + +// ============================================================================ +// RUNTIME CONFIGURATION +// ============================================================================ + +#define MK_IS_SHARED_MODE (MK_MODE_SHARED == memkit_get_mode()) +#define MK_IS_UNIQUE_MODE (MK_MODE_UNIQUE == memkit_get_mode()) +#define MK_IS_MULTI_MODE (MK_MODE_MULTI == memkit_get_mode()) + +int memkit_get_mode(void); +void memkit_set_debuggable(bool debuggable); +bool memkit_get_debuggable(void); +void memkit_set_recordable(bool recordable); +bool memkit_get_recordable(void); +void memkit_set_disable(bool disable); +bool memkit_get_disable(void); + +// ============================================================================ +// DL INIT/FINI CALLBACKS +// ============================================================================ + +typedef shadowhook_dl_info_t MemKitDlInfo; +typedef void (*MemKitDlInitCallback)(struct dl_phdr_info *info, size_t size, void *data); +typedef void (*MemKitDlFiniCallback)(struct dl_phdr_info *info, size_t size, void *data); + +int memkit_register_dl_init_callback(MemKitDlInitCallback pre, MemKitDlInitCallback post, void *data); +int memkit_unregister_dl_init_callback(MemKitDlInitCallback pre, MemKitDlInitCallback post, void *data); +int memkit_register_dl_fini_callback(MemKitDlFiniCallback pre, MemKitDlFiniCallback post, void *data); +int memkit_unregister_dl_fini_callback(MemKitDlFiniCallback pre, MemKitDlFiniCallback post, void *data); + +// ============================================================================ +// HOOKING: CALLBACK VARIANTS +// ============================================================================ + +/* Hook with completion callback */ +void *memkit_hook_with_callback(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, MemKitHooked hooked, void *hooked_arg); + +/* Hook by symbol name with completion callback (alias) */ +void *memkit_hook_by_symbol_callback(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, MemKitHooked hooked, void *hooked_arg); + #ifdef __cplusplus } #endif From a451fe13df0862cf3c7da96a24719270b2777bea Mon Sep 17 00:00:00 2001 From: HanSoBored Date: Thu, 9 Apr 2026 11:35:37 +0700 Subject: [PATCH 8/8] docs: update docs for ShadowHook wrapper ~90% API coverage - Expand Hooking Functions section with all new API groups (V2, Intercept, Records, Runtime Config, DL Callbacks, Proxy/Stack) - Add V2 Hook API, Intercept API, Records API, Runtime Configuration, and DL Callbacks sections with practical examples - Update Error Handling to use MK_ERRNO_* constants and memkit_strerror() - Expand ShadowHook Macros with MEMKIT_CALL_PREV, MEMKIT_POP_STACK, etc. - Add new source files to Project Structure (intercept.c, records.c, runtime_config.c, hooking_flags.c, dl_callbacks.c) - Expand ShadowHook Modes table with V2 flags, intercept flags, runtime config, and DL callbacks --- README.md | 105 +++++++++- docs/USAGE.md | 550 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 629 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c9988bf..5d14c31 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,12 @@ Android-Mem-Kit/ │ └── memkit.h # Public API header ├── src/ │ ├── memory.c # Memory patching (mprotect-based) -│ ├── hooking.c # ShadowHook wrapper +│ ├── hooking.c # ShadowHook wrapper (basic hook/unhook) +│ ├── hooking_flags.c # V2 hook API with mode flags +│ ├── intercept.c # Intercept API (pre-call inspection) +│ ├── records.c # Records API (operation logging) +│ ├── runtime_config.c # Runtime configuration (debuggable, recordable) +│ ├── dl_callbacks.c # DL init/fini callbacks │ ├── il2cpp.c # IL2CPP symbol resolution (uses memkit_xdl_* wrapper) │ └── xdl_wrapper.c # Generic xDL wrapper layer ├── examples/ @@ -208,6 +213,7 @@ memkit_patch_free(patch); // Initialize (call once) memkit_hook_init(SHADOWHOOK_MODE_UNIQUE, false); +// --- Basic Hook API --- // Hook by symbol void* stub = memkit_hook_by_symbol("lib.so", "func_name", my_func, (void**)&orig); @@ -216,6 +222,59 @@ void* stub = memkit_hook(address, my_func, (void**)&orig); // Unhook memkit_unhook(stub); + +// --- V2 Hook API (with flags) --- +void* stub = memkit_hook_v2("lib.so", "func_name", my_func, (void**)&orig, MK_HOOK_DEFAULT); +void* stub = memkit_hook_by_symbol_v2("lib.so", "func_name", my_func, (void**)&orig, MK_HOOK_RECORD); + +// --- Hook with Callback --- +void* stub = memkit_hook_with_callback("lib.so", "func", my_func, (void**)&orig, my_hooked_cb, NULL); + +// --- Intercept API (pre-call inspection) --- +void* stub = memkit_intercept_by_symbol("lib.so", "func", my_interceptor, NULL, MK_INTERCEPT_DEFAULT); +memkit_unintercept(stub); + +// --- Records API --- +char* csv = memkit_get_records(MK_RECORD_ITEM_ALL); // caller must free() +memkit_dump_records_fd(STDOUT_FILENO, MK_RECORD_ITEM_ALL); + +// --- Runtime Configuration --- +memkit_set_debuggable(true); +memkit_set_recordable(true); +memkit_set_disable(true); // global disable switch + +// --- DL Callbacks --- +memkit_register_dl_init_callback(my_dl_init_pre, my_dl_init_post, NULL); +memkit_register_dl_fini_callback(my_dl_fini_pre, my_dl_fini_post, NULL); +``` + +### Proxy/Stack Macros + +```c +// In MULTI mode: call previous hook in chain +int my_proxy(int a, const char* b) { + int ret = MEMKIT_CALL_PREV(my_proxy, int(*)(int, const char*), a, b); + MEMKIT_POP_STACK(); + return ret; +} + +// Control reentrancy +MEMKIT_ALLOW_REENTRANT(); +MEMKIT_DISALLOW_REENTRANT(); + +// Get return address of caller +void* ret_addr = MEMKIT_RETURN_ADDRESS(); +``` + +### Error Handling + +```c +// Get error code and message +int err = memkit_errno(); +const char* msg = memkit_strerror(err); +LOGE("Hook failed: %d - %s", err, msg); + +// 46 MK_ERRNO_* constants available (MK_ERRNO_OK through MK_ERRNO_DISABLED) ``` ### IL2CPP Functions (Unity Apps) @@ -281,13 +340,53 @@ memkit_hook_by_symbol("libtarget.so", "targetFunc", --- -## ShadowHook Modes +## ShadowHook Modes & Capabilities + +### Hooking Modes | Mode | Description | Use Case | | :--- | :--- | :--- | | `SHADOWHOOK_MODE_UNIQUE` | Same address can only be hooked once | Most research scenarios | | `SHADOWHOOK_MODE_SHARED` | Multiple hooks allowed (recursion prevention) | When using multiple SDKs | -| `SHADOWHOOK_MODE_MULTI` | Multiple hooks allowed (no prevention) | Advanced use cases | +| `SHADOWHOOK_MODE_MULTI` | Multiple hooks allowed (no prevention) | Advanced chaining use cases | + +### V2 Hook Flags + +| Flag | Description | +| :--- | :--- | +| `MK_HOOK_DEFAULT` | Default behavior (respects init mode) | +| `MK_HOOK_WITH_SHARED_MODE` | Force SHARED mode for this hook | +| `MK_HOOK_WITH_UNIQUE_MODE` | Force UNIQUE mode for this hook | +| `MK_HOOK_WITH_MULTI_MODE` | Force MULTI mode for this hook | +| `MK_HOOK_RECORD` | Enable recording for this hook operation | + +### Intercept Flags + +| Flag | Description | +| :--- | :--- | +| `MK_INTERCEPT_DEFAULT` | Standard intercept (no FP/SIMD context) | +| `MK_INTERCEPT_WITH_FPSIMD_READ_ONLY` | Include FP/SIMD registers (read-only) | +| `MK_INTERCEPT_WITH_FPSIMD_WRITE_ONLY` | Include FP/SIMD registers (write-only) | +| `MK_INTERCEPT_WITH_FPSIMD_READ_WRITE` | Include FP/SIMD registers (read-write) | +| `MK_INTERCEPT_RECORD` | Enable recording for this intercept | + +### Runtime Configuration + +| Feature | Description | +| :--- | :--- | +| `memkit_set_debuggable()` | Toggle debug logging at runtime | +| `memkit_set_recordable()` | Enable/disable operation recording | +| `memkit_set_disable()` | Global enable/disable switch | +| `MK_IS_SHARED_MODE` | Macro to check current mode | +| `MK_IS_UNIQUE_MODE` | Macro to check current mode | +| `MK_IS_MULTI_MODE` | Macro to check current mode | + +### DL Callbacks + +| Callback | Description | +| :--- | :--- | +| `memkit_register_dl_init_callback()` | Called when a library is loaded (dlopen) | +| `memkit_register_dl_fini_callback()` | Called when a library is unloaded (dlclose) | --- diff --git a/docs/USAGE.md b/docs/USAGE.md index 509b313..07ba155 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -72,6 +72,8 @@ void init() { ### Hooking Functions +#### Basic Hook API + | Function | Description | Returns | |----------|-------------|---------| | `memkit_hook_init(mode, debuggable)` | Initialize ShadowHook | `int` (0 = success) | @@ -79,6 +81,147 @@ void init() { | `memkit_hook_by_symbol(lib, sym, func, &orig)` | Hook by symbol name | `stub` or NULL | | `memkit_unhook(stub)` | Unhook function | `void` | +#### V2 Hook API + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_hook_v2(lib, sym, new, &orig, flags)` | Hook with mode flags | `stub` or NULL | +| `memkit_hook_by_symbol_v2(lib, sym, new, &orig, flags)` | Hook by symbol with flags | `stub` or NULL | + +**V2 Hook Flags:** + +| Flag | Description | +|------|-------------| +| `MK_HOOK_DEFAULT` | Default behavior (respects init mode) | +| `MK_HOOK_WITH_SHARED_MODE` | Force SHARED mode for this hook | +| `MK_HOOK_WITH_UNIQUE_MODE` | Force UNIQUE mode for this hook | +| `MK_HOOK_WITH_MULTI_MODE` | Force MULTI mode for this hook | +| `MK_HOOK_RECORD` | Enable recording for this hook operation | + +#### Hook with Callback + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_hook_with_callback(lib, sym, new, &orig, cb, arg)` | Hook with completion callback | `stub` or NULL | +| `memkit_hook_by_symbol_callback(lib, sym, new, &orig, cb, arg)` | Alias for above | `stub` or NULL | + +**Callback Types:** + +| Type | Description | +|------|-------------| +| `MemKitHooked` | Called when hook operation completes (success or failure) | +| `MemKitIntercepted` | Called when intercept operation completes | +| `MemKitInterceptor` | Receives CPU context on each call to target | + +#### Intercept API + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_intercept(addr, pre, data, flags, ...)` | Intercept by address | `stub` or NULL | +| `memkit_intercept_by_symbol(lib, sym, pre, data, flags)` | Intercept by symbol | `stub` or NULL | +| `memkit_intercept_by_sym_addr(addr, pre, data, flags, ...)` | Intercept by symbol address | `stub` or NULL | +| `memkit_intercept_at_instr(addr, pre, data, flags, ...)` | Intercept at specific instruction | `stub` or NULL | +| `memkit_intercept_with_callback(lib, sym, pre, data, flags, cb, arg)` | Intercept with callback | `stub` or NULL | +| `memkit_unintercept(stub)` | Remove interceptor | `int` | + +**Intercept Flags:** + +| Flag | Description | +|------|-------------| +| `MK_INTERCEPT_DEFAULT` | Standard intercept (no FP/SIMD context) | +| `MK_INTERCEPT_WITH_FPSIMD_READ_ONLY` | Include FP/SIMD registers (read-only) | +| `MK_INTERCEPT_WITH_FPSIMD_WRITE_ONLY` | Include FP/SIMD registers (write-only) | +| `MK_INTERCEPT_WITH_FPSIMD_READ_WRITE` | Include FP/SIMD registers (read-write) | +| `MK_INTERCEPT_RECORD` | Enable recording for this intercept | + +**Context Types:** + +| Type | Description | +|------|-------------| +| `MemKitCpuContext` | CPU context passed to interceptor | +| `MemKitVReg` | NEON/VFP vector register | + +#### Records API + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_get_records(item_flags)` | Get records as CSV string | `char*` (caller frees) | +| `memkit_dump_records_fd(fd, item_flags)` | Dump records to file descriptor | `void` | + +**Record Item Flags:** + +| Flag | Description | +|------|-------------| +| `MK_RECORD_ITEM_TIMESTAMP` | Include timestamp | +| `MK_RECORD_ITEM_CALLER_LIB_NAME` | Include caller library name | +| `MK_RECORD_ITEM_OP` | Include operation type | +| `MK_RECORD_ITEM_LIB_NAME` | Include target library name | +| `MK_RECORD_ITEM_SYM_NAME` | Include symbol name | +| `MK_RECORD_ITEM_SYM_ADDR` | Include symbol address | +| `MK_RECORD_ITEM_NEW_ADDR` | Include new function address | +| `MK_RECORD_ITEM_BACKUP_LEN` | Include backup length | +| `MK_RECORD_ITEM_ERRNO` | Include error code | +| `MK_RECORD_ITEM_STUB` | Include stub pointer | +| `MK_RECORD_ITEM_FLAGS` | Include flags | +| `MK_RECORD_ITEM_ALL` | Include all fields (0x7FF) | + +#### Runtime Configuration + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_get_mode()` | Get current hooking mode | `int` | +| `memkit_set_debuggable(val)` | Enable/disable debug logging | `void` | +| `memkit_get_debuggable()` | Check debug mode status | `bool` | +| `memkit_set_recordable(val)` | Enable/disable recording | `void` | +| `memkit_get_recordable()` | Check recording status | `bool` | +| `memkit_set_disable(val)` | Global enable/disable switch | `void` | +| `memkit_get_disable()` | Check disabled status | `bool` | + +**Mode Check Macros:** + +| Macro | Description | +|-------|-------------| +| `MK_IS_SHARED_MODE` | Evaluates to true if in SHARED mode | +| `MK_IS_UNIQUE_MODE` | Evaluates to true if in UNIQUE mode | +| `MK_IS_MULTI_MODE` | Evaluates to true if in MULTI mode | + +#### DL Callbacks + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_register_dl_init_callback(pre, post, data)` | Register dlopen callback | `int` | +| `memkit_unregister_dl_init_callback(pre, post, data)` | Unregister dlopen callback | `int` | +| `memkit_register_dl_fini_callback(pre, post, data)` | Register dlclose callback | `int` | +| `memkit_unregister_dl_fini_callback(pre, post, data)` | Unregister dlclose callback | `int` | + +**DL Callback Types:** + +| Type | Description | +|------|-------------| +| `MemKitDlInfo` | Library info for DL callbacks | +| `MemKitDlInitCallback` | Called when library is loaded (dlopen) | +| `MemKitDlFiniCallback` | Called when library is unloaded (dlclose) | + +#### Proxy/Stack Macros + +| Macro | Description | +|-------|-------------| +| `MEMKIT_CALL_PREV(func, func_sig, ...)` | Call previous function in proxy chain (MULTI mode) | +| `MEMKIT_POP_STACK()` | Pop current stack frame after proxy call | +| `MEMKIT_ALLOW_REENTRANT()` | Allow reentrant calls from same thread | +| `MEMKIT_DISALLOW_REENTRANT()` | Disallow reentrant calls from same thread | +| `MEMKIT_RETURN_ADDRESS()` | Get return address of current proxy caller | + +#### Proxy/Stack Functions + +| Function | Description | Returns | +|----------|-------------|---------| +| `memkit_get_prev_func(func)` | Get previous function pointer | `void*` | +| `memkit_pop_stack(return_addr)` | Pop stack frame | `void` | +| `memkit_allow_reentrant(return_addr)` | Allow reentrancy | `void` | +| `memkit_disallow_reentrant(return_addr)` | Disallow reentrancy | `void` | +| `memkit_get_return_address()` | Get caller return address | `void*` | + ### IL2CPP Functions | Function | Description | Returns | @@ -235,17 +378,324 @@ memkit_unhook(hook_stub); ### ShadowHook Macros -ShadowHook provides convenient macros: +MemKit provides convenient macros for proxy/stack management: ```c -// In your hook function, call original using macro -void my_hook(void* instance) { - // Option 1: Use stored original pointer - orig_function(instance); +// In MULTI mode: call the previous function in the proxy chain +int my_proxy(int a, const char* b) { + int ret = MEMKIT_CALL_PREV(my_proxy, int(*)(int, const char*), a, b); + MEMKIT_POP_STACK(); // Must call at end of every proxy + return ret; +} + +// Control reentrancy within a proxy +MEMKIT_ALLOW_REENTRANT(); // Allow recursive calls from same thread +MEMKIT_DISALLOW_REENTRANT(); // Block recursive calls + +// Get the return address of the current proxy caller +void* ret_addr = MEMKIT_RETURN_ADDRESS(); +``` + +### V2 Hook API - // Option 2: Use SHADOWHOOK_CALL_PREV macro - SHADOWHOOK_CALL_PREV(my_hook, void (*)(void*), instance); +The V2 API allows per-hook mode control with flags: + +```c +// Hook with specific mode (overrides global init mode) +void* stub = memkit_hook_v2( + "libtarget.so", + "target_function", + (void*)my_function, + (void**)&orig_function, + MK_HOOK_WITH_UNIQUE_MODE // Force UNIQUE for this hook only +); + +// Hook with recording enabled +void* recorded_stub = memkit_hook_by_symbol_v2( + "libssl.so", + "SSL_read", + (void*)my_SSL_read, + (void**)&orig_SSL_read, + MK_HOOK_RECORD // Log this hook operation +); + +// Combine flags +void* stub = memkit_hook_v2( + "libtarget.so", + "check_integrity", + (void*)my_check, + (void**)&orig_check, + MK_HOOK_WITH_SHARED_MODE | MK_HOOK_RECORD +); +``` + +### Hook with Callback + +Get notified when a hook operation completes: + +```c +// Completion callback — called after hook is installed (or fails) +void on_hook_complete(int error_number, const char* lib_name, + const char* sym_name, void* sym_addr, + void* new_addr, void* orig_addr, void* arg) { + if (error_number == 0) { + LOGI("Hook installed: %s!%s at %p", lib_name, sym_name, sym_addr); + } else { + LOGE("Hook failed: %d — %s", error_number, memkit_strerror(error_number)); + } } + +// Hook with callback +void* stub = memkit_hook_with_callback( + "libtarget.so", + "target_function", + (void*)my_function, + (void**)&orig_function, + on_hook_complete, + NULL // user arg passed to callback +); + +// Same as above (alias) +void* stub2 = memkit_hook_by_symbol_callback( + "libssl.so", "SSL_read", + (void*)my_SSL_read, (void**)&orig_SSL_read, + on_hook_complete, NULL +); +``` + +### Intercept API + +The Intercept API allows you to inspect and modify CPU registers **before** the target function executes. Unlike hooks, interceptors receive the full CPU context and can modify arguments in-place. + +```c +// Interceptor function — receives CPU context on each call +static void my_interceptor(MemKitCpuContext* cpu_context, void* data) { + // Read arguments from registers (ARM64: x0-x7 hold first 8 args) + uint64_t arg0 = cpu_context->regs[0]; + uint64_t arg1 = cpu_context->regs[1]; + + LOGI("Intercepted! arg0=0x%lx, arg1=0x%lx", arg0, arg1); + + // Modify arguments before the target function sees them + cpu_context->regs[0] = 0; // Zero out first argument + + // Optionally skip calling the original function entirely + // by setting the PC to the return address +} + +// Basic intercept by symbol +void* stub = memkit_intercept_by_symbol( + "libtarget.so", + "target_function", + my_interceptor, + NULL, // user data + MK_INTERCEPT_DEFAULT +); + +// Intercept with FP/SIMD context (for functions using NEON) +void* stub = memkit_intercept_by_symbol( + "libtarget.so", + "simd_function", + my_interceptor, + NULL, + MK_INTERCEPT_WITH_FPSIMD_READ_WRITE // Include vfp/regs +); + +// Remove interceptor +memkit_unintercept(stub); +``` + +#### Intercept with Completion Callback + +```c +void on_intercept_complete(int error_number, const char* lib_name, + const char* sym_name, void* sym_addr, + void* pre, void* data, void* arg) { + if (error_number == 0) { + LOGI("Intercept installed: %s!%s", lib_name, sym_name); + } +} + +void* stub = memkit_intercept_with_callback( + "libtarget.so", + "check_signature", + my_interceptor, + NULL, + MK_INTERCEPT_DEFAULT, + on_intercept_complete, + NULL +); +``` + +#### Intercept at Specific Instruction + +For advanced use cases where you need to intercept at a specific instruction offset: + +```c +uintptr_t base = memkit_get_lib_base("libtarget.so"); +void* instr_addr = (void*)(base + 0x1234); // Specific instruction + +void* stub = memkit_intercept_at_instr( + instr_addr, + my_interceptor, + NULL, + MK_INTERCEPT_DEFAULT +); +``` + +#### Real-World Example: SSL Pinning Bypass via Intercept + +```c +// Intercept SSL_CTX_set_verify to force VERIFY_NONE +static void intercept_SSL_CTX_set_verify(MemKitCpuContext* ctx, void* data) { + // ARM64 calling convention: x0=ctx, x1=mode, x2=callback + uint64_t mode = ctx->regs[1]; + LOGI("SSL_CTX_set_verify called with mode=0x%lx", mode); + + // Force mode to VERIFY_NONE (0) + ctx->regs[1] = 0; +} + +void* stub = memkit_intercept_by_symbol( + "libssl.so", + "SSL_CTX_set_verify", + intercept_SSL_CTX_set_verify, + NULL, + MK_INTERCEPT_DEFAULT +); +``` + +#### Real-World Example: Integrity Check Bypass via Intercept + +```c +// Intercept a signature verification function +static void intercept_verify_signature(MemKitCpuContext* ctx, void* data) { + // Force return value to 1 (valid) by modifying x0 before return + // We can't modify return value directly in interceptor, + // but we can use a hook instead for post-call modification. + // Interceptors are best for argument inspection/modification. + LOGI("verify_signature called — arguments logged"); +} +``` + +### Records API + +MemKit can log all hook/intercept operations to a CSV format for analysis: + +```c +// Enable recording globally +memkit_set_recordable(true); + +// Get records as CSV string (caller must free) +char* csv = memkit_get_records(MK_RECORD_ITEM_ALL); +if (csv) { + LOGI("Operation records:\n%s", csv); + free(csv); +} + +// Dump records directly to a file descriptor +int fd = open("/data/local/tmp/memkit_records.csv", O_WRONLY | O_CREAT, 0644); +if (fd >= 0) { + memkit_dump_records_fd(fd, MK_RECORD_ITEM_ALL); + close(fd); +} + +// Selective recording — only include specific fields +uint32_t flags = MK_RECORD_ITEM_TIMESTAMP + | MK_RECORD_ITEM_LIB_NAME + | MK_RECORD_ITEM_SYM_NAME + | MK_RECORD_ITEM_ERRNO; +char* csv = memkit_get_records(flags); +``` + +#### Record Item Flags + +| Flag | Field | +|------|-------| +| `MK_RECORD_ITEM_TIMESTAMP` | Timestamp of operation | +| `MK_RECORD_ITEM_CALLER_LIB_NAME` | Library that initiated the operation | +| `MK_RECORD_ITEM_OP` | Operation type (hook, intercept, unhook) | +| `MK_RECORD_ITEM_LIB_NAME` | Target library name | +| `MK_RECORD_ITEM_SYM_NAME` | Target symbol name | +| `MK_RECORD_ITEM_SYM_ADDR` | Symbol address | +| `MK_RECORD_ITEM_NEW_ADDR` | Replacement function address | +| `MK_RECORD_ITEM_BACKUP_LEN` | Backup length of trampoline | +| `MK_RECORD_ITEM_ERRNO` | Error code (0 = success) | +| `MK_RECORD_ITEM_STUB` | Stub pointer | +| `MK_RECORD_ITEM_FLAGS` | Flags used | +| `MK_RECORD_ITEM_ALL` | All fields (0x7FF) | + +### Runtime Configuration + +Control MemKit behavior at runtime: + +```c +// Check current mode +int mode = memkit_get_mode(); +if (MK_IS_UNIQUE_MODE) { + LOGI("Running in UNIQUE mode"); +} else if (MK_IS_SHARED_MODE) { + LOGI("Running in SHARED mode"); +} + +// Toggle debug logging +memkit_set_debuggable(true); +bool is_debug = memkit_get_debuggable(); + +// Toggle recording at runtime +memkit_set_recordable(true); +bool is_recordable = memkit_get_recordable(); + +// Global disable — suspends all hooking/intercepting +memkit_set_disable(true); // Disable all operations +bool is_disabled = memkit_get_disable(); +memkit_set_disable(false); // Re-enable +``` + +### DL Callbacks + +Register callbacks to be notified when libraries are loaded or unloaded: + +```c +// Called before and after a library is loaded (dlopen) +void dl_init_pre(struct dl_phdr_info* info, size_t size, void* data) { + LOGI("Library loading: %s", info->dlpi_name); +} + +void dl_init_post(struct dl_phdr_info* info, size_t size, void* data) { + LOGI("Library loaded: %s at base 0x%lx", info->dlpi_name, info->dlpi_addr); +} + +// Called before and after a library is unloaded (dlclose) +void dl_fini_pre(struct dl_phdr_info* info, size_t size, void* data) { + LOGI("Library unloading: %s", info->dlpi_name); +} + +void dl_fini_post(struct dl_phdr_info* info, size_t size, void* data) { + LOGI("Library unloaded: %s", info->dlpi_name); +} + +// Register callbacks +memkit_register_dl_init_callback(dl_init_pre, dl_init_post, NULL); +memkit_register_dl_fini_callback(dl_fini_pre, dl_fini_post, NULL); + +// Unregister when done +memkit_unregister_dl_init_callback(dl_init_pre, dl_init_post, NULL); +memkit_unregister_dl_fini_callback(dl_fini_pre, dl_fini_post, NULL); +``` + +#### Real-World Example: Auto-Hook When Target Library Loads + +```c +// Auto-hook SSL functions when libssl.so is loaded +static void on_ssl_loaded(struct dl_phdr_info* info, size_t size, void* data) { + if (strstr(info->dlpi_name, "libssl.so")) { + LOGI("libssl.so detected — installing hooks..."); + // Install hooks here + } +} + +memkit_register_dl_init_callback(NULL, on_ssl_loaded, NULL); ``` --- @@ -315,27 +765,87 @@ thread_hook_stub = memkit_hook_by_symbol( ## Error Handling -### Using errno +### MemKit Error Functions -All functions set `errno` on failure: +MemKit provides its own error handling layer wrapping ShadowHook: ```c #include #include +// Get last error code from ShadowHook +int err = memkit_errno(); + +// Get human-readable error message +const char* msg = memkit_strerror(err); +LOGE("Operation failed: %d - %s", err, msg); + +// Get version string +const char* version = memkit_version(); +LOGI("ShadowHook version: %s", version); + +// Get error from last shadowhook_init() call +int init_err = memkit_init_errno(); +``` + +### MK_ERRNO_* Constants + +MemKit exposes all 46 ShadowHook error codes: + +```c +// Common error codes +MK_ERRNO_OK // 0: Success +MK_ERRNO_UNINIT // 2: Not initialized +MK_ERRNO_INVALID_ARG // 3: Invalid argument +MK_ERRNO_OOM // 4: Out of memory +MK_ERRNO_MPROT // 5: mprotect failed +MK_ERRNO_HOOK_DLSYM // 18: Symbol not found +MK_ERRNO_HOOK_ENTER // 24: Failed to enter hook +MK_ERRNO_HOOK_DUP // 20: Duplicate hook +MK_ERRNO_UNHOOK_NOTFOUND // 27: Unhook target not found +MK_ERRNO_DISABLED // 45: Operations disabled + +// Full list available in memkit.h: +// MK_ERRNO_PENDING, MK_ERRNO_WRITE_CRASH, MK_ERRNO_INIT_ERRNO, +// MK_ERRNO_INIT_SIGSEGV, MK_ERRNO_INIT_SIGBUS, MK_ERRNO_INTERCEPT_DUP, +// MK_ERRNO_INIT_SAFE, MK_ERRNO_INIT_LINKER, MK_ERRNO_INIT_HUB, +// MK_ERRNO_HUB_CREAT, MK_ERRNO_MONITOR_DLOPEN, MK_ERRNO_HOOK_UNIQUE_DUP, +// MK_ERRNO_HOOK_DLOPEN_CRASH, MK_ERRNO_HOOK_DLSYM_CRASH, +// MK_ERRNO_HOOK_DLADDR_CRASH, MK_ERRNO_HOOK_DLINFO, MK_ERRNO_HOOK_SYMSZ, +// MK_ERRNO_HOOK_REWRITE_CRASH, MK_ERRNO_HOOK_REWRITE_FAILED, +// MK_ERRNO_UNHOOK_CMP_CRASH, MK_ERRNO_UNHOOK_TRAMPO_MISMATCH, +// MK_ERRNO_UNHOOK_EXIT_MISMATCH, MK_ERRNO_UNHOOK_EXIT_CRASH, +// MK_ERRNO_UNHOOK_ON_ERROR, MK_ERRNO_UNHOOK_ON_UNFINISHED, +// MK_ERRNO_ELF_ARCH_MISMATCH, MK_ERRNO_LINKER_ARCH_MISMATCH, +// MK_ERRNO_DUP, MK_ERRNO_NOT_FOUND, MK_ERRNO_NOT_SUPPORT, +// MK_ERRNO_INIT_TASK, MK_ERRNO_HOOK_ISLAND_EXIT, +// MK_ERRNO_HOOK_ISLAND_ENTER, MK_ERRNO_HOOK_ISLAND_REWRITE, +// MK_ERRNO_MODE_CONFLICT, MK_ERRNO_HOOK_MULTI_DUP +``` + +### Using memkit_strerror() + +```c void* stub = memkit_hook_by_symbol("lib.so", "func", my_func, (void**)&orig); if (stub == NULL) { - LOGE("Hook failed: %s", strerror(errno)); - - // Common errors: - // EINVAL - Invalid argument - // ENOENT - Symbol not found - // EACCES - Permission denied (mprotect failed) - // ENOMEM - Out of memory + int err = memkit_errno(); + const char* msg = memkit_strerror(err); + LOGE("Hook failed: %d - %s", err, msg); + + // Common troubleshooting: + if (err == MK_ERRNO_HOOK_DLSYM) { + LOGE("Symbol not found — check library name and symbol spelling"); + } else if (err == MK_ERRNO_UNINIT) { + LOGE("ShadowHook not initialized — call memkit_hook_init() first"); + } else if (err == MK_ERRNO_INVALID_ARG) { + LOGE("Invalid argument — check function pointers and addresses"); + } } ``` -### ShadowHook Error Codes +### ShadowHook Error Codes (Legacy) + +For compatibility, you can still use ShadowHook's native error functions: ```c if (stub == NULL) { @@ -345,12 +855,6 @@ if (stub == NULL) { } ``` -Common ShadowHook errors: -- `SHADOWHOOK_ERRNO_HOOK_DLSYM` - Symbol not found -- `SHADOWHOOK_ERRNO_HOOK_ENTER` - Failed to enter hook -- `SHADOWHOOK_ERRNO_INVALID_ARG` - Invalid argument -- `SHADOWHOOK_ERRNO_UNINIT` - Not initialized - --- ## Best Practices