2018-11-22 05:29:35 +06:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2012-2018 Zeex
|
2013-01-13 22:15:49 +07:00
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef SUBHOOK_H
|
|
|
|
|
#define SUBHOOK_H
|
|
|
|
|
|
2020-05-30 20:21:30 +06:00
|
|
|
#include <stddef.h>
|
|
|
|
|
|
2013-01-13 22:15:49 +07:00
|
|
|
#if defined _M_IX86 || defined __i386__
|
2016-07-24 05:17:25 +06:00
|
|
|
#define SUBHOOK_X86
|
|
|
|
|
#define SUBHOOK_BITS 32
|
2023-02-10 00:00:32 +06:00
|
|
|
#elif defined _M_AMD64 || defined __amd64__
|
2016-07-24 05:17:25 +06:00
|
|
|
#define SUBHOOK_X86_64
|
|
|
|
|
#define SUBHOOK_BITS 64
|
2013-01-13 22:15:49 +07:00
|
|
|
#else
|
2016-07-24 05:17:25 +06:00
|
|
|
#error Unsupported architecture
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
2016-08-28 15:10:11 +06:00
|
|
|
#if defined _WIN32 || defined __CYGWIN__
|
2016-07-24 05:17:25 +06:00
|
|
|
#define SUBHOOK_WINDOWS
|
2021-11-11 13:27:23 +02:00
|
|
|
#elif defined __linux__ \
|
2020-10-31 17:18:48 +06:00
|
|
|
|| defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
|
2016-08-28 15:10:11 +06:00
|
|
|
#define SUBHOOK_UNIX
|
2023-02-10 00:00:32 +06:00
|
|
|
#elif defined __APPLE__
|
2021-11-11 13:27:23 +02:00
|
|
|
#define SUBHOOK_APPLE
|
2021-11-20 22:12:51 +02:00
|
|
|
#define SUBHOOK_UNIX
|
2013-01-13 22:15:49 +07:00
|
|
|
#else
|
2016-07-24 05:17:25 +06:00
|
|
|
#error Unsupported operating system
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
2019-02-07 06:46:09 +06:00
|
|
|
#if !defined SUBHOOK_EXTERN
|
2016-07-24 05:17:25 +06:00
|
|
|
#if defined __cplusplus
|
|
|
|
|
#define SUBHOOK_EXTERN extern "C"
|
|
|
|
|
#else
|
|
|
|
|
#define SUBHOOK_EXTERN extern
|
|
|
|
|
#endif
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined SUBHOOK_STATIC
|
2016-07-24 05:17:25 +06:00
|
|
|
#define SUBHOOK_API
|
|
|
|
|
#define SUBHOOK_EXPORT SUBHOOK_EXTERN
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined SUBHOOK_API
|
2016-07-24 05:17:25 +06:00
|
|
|
#if defined SUBHOOK_X86
|
|
|
|
|
#if defined SUBHOOK_WINDOWS
|
|
|
|
|
#define SUBHOOK_API __cdecl
|
2021-11-20 22:12:51 +02:00
|
|
|
#elif defined SUBHOOK_UNIX
|
2016-07-24 05:17:25 +06:00
|
|
|
#define SUBHOOK_API __attribute__((cdecl))
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
#define SUBHOOK_API
|
|
|
|
|
#endif
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined SUBHOOK_EXPORT
|
2016-07-24 05:17:25 +06:00
|
|
|
#if defined SUBHOOK_WINDOWS
|
|
|
|
|
#if defined SUBHOOK_IMPLEMENTATION
|
|
|
|
|
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __declspec(dllexport)
|
|
|
|
|
#else
|
|
|
|
|
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __declspec(dllimport)
|
|
|
|
|
#endif
|
2021-11-20 22:12:51 +02:00
|
|
|
#elif defined SUBHOOK_UNIX
|
2016-07-24 05:17:25 +06:00
|
|
|
#if defined SUBHOOK_IMPLEMENTATION
|
|
|
|
|
#define SUBHOOK_EXPORT SUBHOOK_EXTERN __attribute__((visibility("default")))
|
|
|
|
|
#else
|
|
|
|
|
#define SUBHOOK_EXPORT SUBHOOK_EXTERN
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
2013-01-13 22:15:49 +07:00
|
|
|
#endif
|
|
|
|
|
|
2018-09-06 23:52:15 +06:00
|
|
|
typedef enum subhook_flags {
|
2023-02-10 00:00:32 +06:00
|
|
|
/*
|
|
|
|
|
* Use the 64-bit jump method on x86-64. Unlike the classical 32-bit JMP,
|
|
|
|
|
* this approach ensures that the destination code can be reached from any
|
|
|
|
|
* point in the 64-bit address space, even if the source and destination are
|
|
|
|
|
* more than 4GB away from each other (meaning we are not limited to using
|
|
|
|
|
* JMP 32-bit offsets).
|
|
|
|
|
*
|
|
|
|
|
* Keep in mind that it requires overwriting a few more leading instructions
|
|
|
|
|
* inside the target code, thus it may not work with extremely short
|
|
|
|
|
* functions (14 bytes vs 5 bytes).
|
|
|
|
|
*
|
|
|
|
|
* Credits to @Ozymandias117 and @RomanHargrave on GitHub for implementing
|
|
|
|
|
* this in subhook.
|
|
|
|
|
*/
|
|
|
|
|
SUBHOOK_64BIT_OFFSET = 0x01,
|
|
|
|
|
/*
|
|
|
|
|
* Generate a trampoline for jumping back to the original code faster (without
|
|
|
|
|
* removing the hook each time).
|
|
|
|
|
*
|
|
|
|
|
* In some scenarios, trampolines cannot be created. See "Known limitations"
|
|
|
|
|
* in the README file.
|
|
|
|
|
*/
|
|
|
|
|
SUBHOOK_TRAMPOLINE = 0x02,
|
|
|
|
|
/*
|
|
|
|
|
* Windows x64 only: Try to allocate a trampoline buffer within +/- 2GB range
|
|
|
|
|
* of the original function to overcome a possible issue with relocating memory
|
|
|
|
|
* referencing instructions, particularly those which use RIP-relative
|
|
|
|
|
* addresses (i.e. with 32-bit offsets).
|
|
|
|
|
*
|
|
|
|
|
* Caution: this feature may slow down your code.
|
|
|
|
|
*/
|
|
|
|
|
SUBHOOK_TRAMPOLINE_ALLOC_NEARBY = 0x04
|
2018-09-06 23:52:15 +06:00
|
|
|
} subhook_flags_t;
|
2016-07-24 04:56:04 +06:00
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
struct subhook_struct;
|
|
|
|
|
typedef struct subhook_struct *subhook_t;
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2018-11-22 04:22:15 +06:00
|
|
|
typedef int (SUBHOOK_API *subhook_disasm_handler_t)(
|
|
|
|
|
void *src,
|
|
|
|
|
int *reloc_op_offset);
|
|
|
|
|
|
2018-11-22 05:29:35 +06:00
|
|
|
SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(
|
|
|
|
|
void *src,
|
|
|
|
|
void *dst,
|
|
|
|
|
subhook_flags_t flags);
|
2013-12-05 02:04:59 +07:00
|
|
|
SUBHOOK_EXPORT void SUBHOOK_API subhook_free(subhook_t hook);
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2014-07-29 17:42:17 +07:00
|
|
|
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_src(subhook_t hook);
|
2013-12-05 02:04:59 +07:00
|
|
|
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_dst(subhook_t hook);
|
2014-11-27 00:07:51 +06:00
|
|
|
SUBHOOK_EXPORT void *SUBHOOK_API subhook_get_trampoline(subhook_t hook);
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2013-12-05 02:04:59 +07:00
|
|
|
SUBHOOK_EXPORT int SUBHOOK_API subhook_install(subhook_t hook);
|
|
|
|
|
SUBHOOK_EXPORT int SUBHOOK_API subhook_is_installed(subhook_t hook);
|
2014-11-27 00:07:51 +06:00
|
|
|
SUBHOOK_EXPORT int SUBHOOK_API subhook_remove(subhook_t hook);
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2018-11-22 05:29:35 +06:00
|
|
|
/*
|
|
|
|
|
* Reads hook destination address from code.
|
2013-01-13 22:15:49 +07:00
|
|
|
*
|
2018-11-22 05:29:35 +06:00
|
|
|
* This function may be useful when you don't know the address or want to
|
|
|
|
|
* check whether src is already hooked.
|
2013-01-13 22:15:49 +07:00
|
|
|
*/
|
2013-01-15 04:02:06 +07:00
|
|
|
SUBHOOK_EXPORT void *SUBHOOK_API subhook_read_dst(void *src);
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2020-05-30 20:17:19 +06:00
|
|
|
/*
|
|
|
|
|
* Returns the length of the first instruction in src. You can replace it with
|
|
|
|
|
* a custom function via subhook_set_disasm_handler.
|
|
|
|
|
*/
|
|
|
|
|
SUBHOOK_EXPORT int SUBHOOK_API subhook_disasm(void *src, int *reloc_op_offset);
|
|
|
|
|
|
2018-11-22 05:29:35 +06:00
|
|
|
/*
|
|
|
|
|
* Sets a custom disassmbler function to use in place of the default one
|
2018-11-22 04:22:15 +06:00
|
|
|
* (subhook_disasm).
|
|
|
|
|
*
|
2020-05-30 20:04:46 +06:00
|
|
|
* The default function can recognize only a small subset of x86 instructions
|
|
|
|
|
* commonly used in prologues. If it fails in your situation, you might want
|
|
|
|
|
* to use a more advanced disassembler library.
|
2018-11-22 04:22:15 +06:00
|
|
|
*/
|
|
|
|
|
SUBHOOK_EXPORT void SUBHOOK_API subhook_set_disasm_handler(
|
|
|
|
|
subhook_disasm_handler_t handler);
|
|
|
|
|
|
2013-01-13 22:15:49 +07:00
|
|
|
#ifdef __cplusplus
|
|
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
namespace subhook {
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2018-09-06 23:52:15 +06:00
|
|
|
enum HookFlags {
|
|
|
|
|
HookNoFlags = 0,
|
2023-02-10 00:00:32 +06:00
|
|
|
HookFlag64BitOffset = SUBHOOK_64BIT_OFFSET,
|
|
|
|
|
HookFlagTrampoline = SUBHOOK_TRAMPOLINE,
|
|
|
|
|
HookFlagTrampolineAllocNearby = SUBHOOK_TRAMPOLINE_ALLOC_NEARBY
|
2016-08-13 02:17:42 +06:00
|
|
|
};
|
|
|
|
|
|
2018-09-06 23:52:15 +06:00
|
|
|
inline HookFlags operator|(HookFlags o1, HookFlags o2) {
|
|
|
|
|
return static_cast<HookFlags>(
|
2016-08-13 02:17:42 +06:00
|
|
|
static_cast<unsigned int>(o1) | static_cast<unsigned int>(o2));
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-06 23:52:15 +06:00
|
|
|
inline HookFlags operator&(HookFlags o1, HookFlags o2) {
|
|
|
|
|
return static_cast<HookFlags>(
|
2016-08-13 02:17:42 +06:00
|
|
|
static_cast<unsigned int>(o1) & static_cast<unsigned int>(o2));
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 04:22:15 +06:00
|
|
|
inline void *ReadHookDst(void *src) {
|
|
|
|
|
return subhook_read_dst(src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void SetDisasmHandler(subhook_disasm_handler_t handler) {
|
|
|
|
|
subhook_set_disasm_handler(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
class Hook {
|
|
|
|
|
public:
|
2020-05-30 20:21:30 +06:00
|
|
|
Hook() : hook_(NULL) {}
|
2018-09-06 23:52:15 +06:00
|
|
|
Hook(void *src, void *dst, HookFlags flags = HookNoFlags)
|
|
|
|
|
: hook_(subhook_new(src, dst, (subhook_flags_t)flags))
|
|
|
|
|
{
|
|
|
|
|
}
|
2016-08-13 02:17:42 +06:00
|
|
|
|
|
|
|
|
~Hook() {
|
2017-07-28 01:23:55 +03:00
|
|
|
subhook_remove(hook_);
|
|
|
|
|
subhook_free(hook_);
|
2016-07-24 05:17:25 +06:00
|
|
|
}
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2018-09-06 23:52:15 +06:00
|
|
|
void *GetSrc() const { return subhook_get_src(hook_); }
|
|
|
|
|
void *GetDst() const { return subhook_get_dst(hook_); }
|
|
|
|
|
void *GetTrampoline() const { return subhook_get_trampoline(hook_); }
|
2013-01-17 06:07:06 +07:00
|
|
|
|
2016-07-24 05:17:25 +06:00
|
|
|
bool Install() {
|
2020-05-30 19:48:32 +06:00
|
|
|
return subhook_install(hook_) == 0;
|
2016-07-24 05:17:25 +06:00
|
|
|
}
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2016-08-13 01:58:55 +06:00
|
|
|
bool Install(void *src,
|
|
|
|
|
void *dst,
|
2018-09-06 23:52:15 +06:00
|
|
|
HookFlags flags = HookNoFlags) {
|
2020-05-30 20:21:30 +06:00
|
|
|
if (hook_ != NULL) {
|
2020-05-30 19:59:20 +06:00
|
|
|
subhook_remove(hook_);
|
|
|
|
|
subhook_free(hook_);
|
|
|
|
|
}
|
|
|
|
|
hook_ = subhook_new(src, dst, (subhook_flags_t)flags);
|
2020-05-30 20:21:30 +06:00
|
|
|
if (hook_ == NULL) {
|
2020-05-30 19:59:20 +06:00
|
|
|
return false;
|
2016-07-24 05:17:25 +06:00
|
|
|
}
|
|
|
|
|
return Install();
|
|
|
|
|
}
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2016-07-24 05:17:25 +06:00
|
|
|
bool Remove() {
|
2020-05-30 19:48:32 +06:00
|
|
|
return subhook_remove(hook_) == 0;
|
2016-07-24 05:17:25 +06:00
|
|
|
}
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2016-07-24 05:17:25 +06:00
|
|
|
bool IsInstalled() const {
|
|
|
|
|
return !!subhook_is_installed(hook_);
|
|
|
|
|
}
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
private:
|
|
|
|
|
Hook(const Hook &);
|
|
|
|
|
void operator=(const Hook &);
|
2013-01-13 22:15:49 +07:00
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
private:
|
2016-07-24 05:17:25 +06:00
|
|
|
subhook_t hook_;
|
2013-01-13 22:15:49 +07:00
|
|
|
};
|
|
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
class ScopedHookRemove {
|
|
|
|
|
public:
|
|
|
|
|
ScopedHookRemove(Hook *hook)
|
2020-05-30 19:59:20 +06:00
|
|
|
: hook_(hook),
|
|
|
|
|
removed_(hook_->Remove())
|
2016-08-13 02:17:42 +06:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ScopedHookRemove() {
|
|
|
|
|
if (removed_) {
|
|
|
|
|
hook_->Install();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ScopedHookRemove(const ScopedHookRemove &);
|
|
|
|
|
void operator=(const ScopedHookRemove &);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Hook *hook_;
|
|
|
|
|
bool removed_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ScopedHookInstall {
|
|
|
|
|
public:
|
|
|
|
|
ScopedHookInstall(Hook *hook)
|
2020-05-30 19:59:20 +06:00
|
|
|
: hook_(hook),
|
|
|
|
|
installed_(hook_->Install())
|
2016-08-13 02:17:42 +06:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-31 15:42:52 +09:00
|
|
|
ScopedHookInstall(Hook *hook,
|
|
|
|
|
void *src,
|
|
|
|
|
void *dst,
|
2018-09-06 23:52:15 +06:00
|
|
|
HookFlags flags = HookNoFlags)
|
2020-05-30 19:59:20 +06:00
|
|
|
: hook_(hook),
|
|
|
|
|
installed_(hook_->Install(src, dst, flags))
|
2017-03-31 15:42:52 +09:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-13 02:17:42 +06:00
|
|
|
~ScopedHookInstall() {
|
|
|
|
|
if (installed_) {
|
|
|
|
|
hook_->Remove();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ScopedHookInstall(const ScopedHookInstall &);
|
|
|
|
|
void operator=(const ScopedHookInstall &);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Hook *hook_;
|
|
|
|
|
bool installed_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace subhook
|
|
|
|
|
|
2013-01-19 00:25:39 +07:00
|
|
|
#endif /* __cplusplus */
|
2013-01-13 22:15:49 +07:00
|
|
|
|
|
|
|
|
#endif /* SUBHOOK_H */
|