Giter Club home page Giter Club logo

subhook's Introduction

Build Status Build Status - Windows

SubHook is a simple library for hooking arbitrary functions at run time. It's written in C and also provides an optional C++ wrapper API.

The library was originally developed to intercept a bunch of API calls in the SA-MP server, which is a Windows/Linux 32-bit app, in order to extend a plugin that I wrote for it. Since then, it has been adapted to better support x86_64, macOS, and more common use-cases, thanks to the contributors.

Installation

Easy method:

  1. Copy the source and header files to your project and include subhook.c in your build.
  2. On Windows only: Define SUBHOOK_STATIC before including subhook.h.

With CMake:

  1. Copy the subhook repo to your project tree.

  2. Call add_subdirectory(path/to/subhook) in your CMakeLists.txt.

  3. Optional: configure how the library is built by setting these varaible prior to add_subdirectory(...):

    • SUBHOOK_STATIC - Build as static library (OFF by default)
    • SUBHOOK_INSTALL - Enable installation and packaging of targets/files with CPack (OFF by default)
    • SUBHOOK_TESTS - Enable tests (ON by default)
    • SUBHOOK_FORCE_32BIT - Configure for compiling 32-bit binaries on 64-bit systems (default is OFF)

Use of CMake is not mandatory, the library can be built without it (no extra build configuration is required).

Examples

In the following examples foo is some function or a function pointer that takes a single argument of type int and uses the same calling convention as my_foo (depends on compiler).

Basic usage

#include <stdio.h>
#include <subhook.h>

subhook_t foo_hook;

void my_foo(int x) {
  /* Remove the hook so that you can call the original function. */
  subhook_remove(foo_hook);

  printf("foo(%d) called\n", x);
  foo(x);

  /* Install the hook back to intercept further calls. */
  subhook_install(foo_hook);
}

int main() {
  /* Create a hook that will redirect all foo() calls to to my_foo(). */
  foo_hook = subhook_new((void *)foo, (void *)my_foo, 0);

  /* Install it. */
  subhook_install(foo_hook);

  foo(123);

  /* Remove the hook and free memory when you're done. */
  subhook_remove(foo_hook);
  subhook_free(foo_hook);
}

Trampolines

Using trampolines allows you to jump to the original code without removing and re-installing hooks every time your function gets called.

typedef void (*foo_func)(int x);

void my_foo(int x) {
  printf("foo(%d) called\n", x);

  /* Call foo() via trampoline. */
  ((foo_func)subhook_get_trampoline(foo_hook))(x);
}

int main() {
   /* Same code as in the previous example. */
}

Please note that subhook has a very simple length disassmebler engine (LDE) that works only with most common prologue instructions like push, mov, call, etc. When it encounters an unknown instruction subhook_get_trampoline() will return NULL. You can delegate instruction decoding to a custom disassembler of your choice via subhook_set_disasm_handler().

C++

#include <iostream>
#include <subhook.h>

subhook::Hook foo_hook;
subhook::Hook foo_hook_tr;

typedef void (*foo_func)(int x);

void my_foo(int x) {
  // ScopedHookRemove removes the specified hook and automatically re-installs
  // it when the object goes out of scope (thanks to C++ destructors).
  subhook::ScopedHookRemove remove(&foo_hook);

  std::cout << "foo(" << x << ") called" << std::endl;
  foo(x + 1);
}

void my_foo_tr(int x) {
  std::cout << "foo(" << x << ") called" << std::endl;

  // Call the original function via trampoline.
  ((foo_func)foo_hook_tr.GetTrampoline())(x + 1);
}

int main() {
  foo_hook.Install((void *)foo, (void *)my_foo);
  foo_hook_tr.Install((void *)foo, (void *)my_foo_tr);
}

Known issues/limitations

  • subhook_get_trampoline() may return NULL because only a small subset of x86 instructions is supported by the disassembler in this library (just common prologue instructions). As a workaround you can plug in a more advanced instruction length decoder using subhook_set_disasm_handler().

  • 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 for jmp opcode + 4 bytes for offset. But if you enable the use of 64-bit offsets in 64-bit mode N becomes 14 (see the definition of subhook_jmp64).

    On x64_64, another cause could be that the function contains instructions referencing memory that is too far away from the trampline code buffer's address trampoline_addr, such as cmp dword ptr [some_32bit_addr], rax (i.e. RIP-relative addressing) where the offset between some_32bit_addr and trampoline_addr cannot fit into 32 bits, and therefore we cannot update the memory address referenced in the original code (we need to do that because because it's relative).

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

License

Licensed under the 2-clause BSD license.

subhook's People

Contributors

cookieplmonster avatar elduderinos avatar gocha avatar lukeusher avatar omgtehlion avatar patrickvl avatar r4nx avatar timgates42 avatar y-less avatar zeex avatar znixian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

subhook's Issues

subhook_make_jmp overwrites orignal code in trampoline

For the call to subhook_make_jump from subhook_make_trampoline, after the source bytes are copied in, the jmp bytes are copied over top of them instead of after them.

I was able to fix this by changing the first arg to (void *)(trampoline_addr + orig_size)

subhook_make_jmp(trampoline, (void *)(src_addr + orig_size), options);

(Only tested with 64-bit hooking).

8 bit relative jump tripping up my use case (instruction 0x79 0x07)

2 byte instruction "jns 0x9" (bytes "0x79 0x07") (jump forward 7 bytes from updated IP) - using 8 bit offset), trips me up.

The function I am trying to subhook_make_trampoline for is as follows:

int fact(int n) {
  if (n < 0) return 0;
  if (n <= 1) return 1;
  int i, ans = 1;
  for (i = 2; i <= n; i++) {
    ans *= i;
  }
  return ans;
}

compiled w/ gcc 9.3 with -ggdb -mmanual-endbr I get:

(gdb) di fact
Dump of assembler code for function fact:
   0x00005555555551e9 <+0>:	push   %rbp
   0x00005555555551ea <+1>:	mov    %rsp,%rbp
   0x00005555555551ed <+4>:	mov    %edi,-0x14(%rbp)
   0x00005555555551f0 <+7>:	cmpl   $0x0,-0x14(%rbp)
   0x00005555555551f4 <+11>:	jns    0x5555555551fd <fact+20>     #  <<< ISSUE IS HERE
   0x00005555555551f6 <+13>:	mov    $0x0,%eax
   0x00005555555551fb <+18>:	jmp    0x555555555233 <fact+74>
   0x00005555555551fd <+20>:	cmpl   $0x1,-0x14(%rbp)
   0x0000555555555201 <+24>:	jg     0x55555555520a <fact+33>
   0x0000555555555203 <+26>:	mov    $0x1,%eax
   0x0000555555555208 <+31>:	jmp    0x555555555233 <fact+74>
   0x000055555555520a <+33>:	movl   $0x1,-0x4(%rbp)
   0x0000555555555211 <+40>:	movl   $0x2,-0x8(%rbp)
   0x0000555555555218 <+47>:	jmp    0x555555555228 <fact+63>
   0x000055555555521a <+49>:	mov    -0x4(%rbp),%eax
   0x000055555555521d <+52>:	imul   -0x8(%rbp),%eax
   0x0000555555555221 <+56>:	mov    %eax,-0x4(%rbp)
   0x0000555555555224 <+59>:	addl   $0x1,-0x8(%rbp)
   0x0000555555555228 <+63>:	mov    -0x8(%rbp),%eax
   0x000055555555522b <+66>:	cmp    -0x14(%rbp),%eax
   0x000055555555522e <+69>:	jle    0x55555555521a <fact+49>
   0x0000555555555230 <+71>:	mov    -0x4(%rbp),%eax
   0x0000555555555233 <+74>:	pop    %rbp
   0x0000555555555234 <+75>:	retq   

Instruction at fact+11 is "jns 0x09" or in raw bytes "0x79 0x07"
In subhook_x86.c it says:

 * Note: We don't support short (8-bit) offsets at the moment, so the
 * caller can assume the operand will be always 4 bytes.

What can I do to get around this limitation? It seems 2 byte relative jump instructions (meaning 8 bits of offset) would be used quite often in real world.

Linux x64 hook failed

If the distance between original code and the trampoline is bigger than 32 bytes, the hook will failed. That would be so nice if anyone could tell me how to fix it.

Instruction mov gets patched when relocated

I have this code (x86_64):
48 89 7D D8 mov [rbp-28h], rdi

subhook_disasm returns correct length=4 and reloc_op_offset=3 (which should be actually 0).
This corrupts the hooked routine.

Hooking fopen on Linux

I was hoping to intercept calls to fopen done by the server, like crashdetect does, but it seems not to do what I hoped for. I was able to hook CreateFileA on Windows, but calls to fopen that originate from the server are not intercepted; only mine are. Does crashdetect do something special for the hook to work?

fatal error LNK1120: 4 unresolved externals

error LNK2019: unresolved external symbol __imp__subhook_new referenced in function "public: bool __thiscall subhook::Hook::Install(void *,void *,enum subhook::HookFlags)" (?Install@Hook@subhook@@QAE_NPAX0W4HookFlags@2@@Z)
error LNK2019: unresolved external symbol __imp__subhook_free referenced in function "public: __thiscall subhook::Hook::~Hook(void)" (??1Hook@subhook@@QAE@XZ)
error LNK2019: unresolved external symbol __imp__subhook_install referenced in function "public: bool __thiscall subhook::Hook::Install(void)" (?Install@Hook@subhook@@QAE_NXZ)
error LNK2019: unresolved external symbol __imp__subhook_remove referenced in function "public: __thiscall subhook::Hook::~Hook(void)" (??1Hook@subhook@@QAE@XZ)
fatal error LNK1120: 4 unresolved externals

please some help

Question: Supports class methods?

The README example illustrated hooks for global functions. Do hooks work for instanced class methods (virtual and non-virtual), and method overloads?

readme script won't compile

you're missing a parenthesis on one of the lines in the c++ example:

foo_hook_tr.Install(void *)foo, (void *)my_foo_tr);

should be

foo_hook_tr.Install((void *)foo, (void *)my_foo_tr);

Fail to install hook with subhook::Hook.Install() in normal function call

Now we can use subhook to change the function implementation in a single-thread application. However, we found that if we create another thread to install the hook, the function object in the main thread will not change.

Here is the simple program to reproduce the issue.

#include <iostream>
#include <thread>
#include <subhook.h>

using namespace std;

int add_func(int a, int b) {
    cout << "Call simple add" << endl;
    return a + b;
}

int minus_func(int a, int b) {
    cout << "Call new add func" << endl;
    return a + b + b;
}

void change_add_func() {
    subhook::Hook foo_hook((void*)add_func,
                        (void*)minus_func,
                        subhook::HookFlag64BitOffset);

    // Output simple add
    add_func(1,2);

    if (!foo_hook.Install()) {
      std::cout << "Install failed" << std::endl;
      return;
    }

    // Output new add
    add_func(1,2);
}


int main() {
  std::thread thread1(change_add_func);
  thread1.join();

  // Still output simple add
  add_func(1,2);
}

Is there anyway to support this in multi-thread application?

Hook same tunction multiple times or share hook

YSF and SKY both use hook for function GetPacketID. Both of the plugins cant be loaded because latest will crash. Would be good if you create a method to double hook same runction or only hook it once and then just share the hook only.

Trampoline Method

What is the simplest way to trigger a call to the original function from within a sub-hooked function?

I have tried something like:

subhook_remove(hook);
DWORD address = 0x00471DA0;
__asm jmp [address]
subhook_install(hook);

But with no luck, as I'm awful at this stuff and the address isn't even correct for some reason, even though sub-hooking that address works perfectly - not to mention doing it that way wouldn't even push the args, Any suggestions?

Server crash on hooked GetPacketID

Hook did the GetPacketID and server gets Crash Release
(pastebin code http://pastebin.com/JwXxYcAB)

static BYTE HOOK_GetPacketID(Packet *p)
{
SubHook::ScopedRemove remove(&GetPacketID_hook);

BYTE pakId = p->data[0];
WORD playerid = p->playerIndex;

logprintf("packetid: %d, playeird: %d", packetId, playerid);

return packetId;

}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = (logprintf_t) ppData[PLUGIN_DATA_LOGPRINTF];

FUNC_GetPacketID = FindPatterns("\x8B\x44\x24\x04\x85\xC0\x75\x03\x0C\xFF\xC3", "xxxxxxx???x");

GetPacketID_hook.Install((void*)FUNC_GetPacketID, (void*)HOOK_GetPacketID);

logprintf(" * logpacket plugin was loaded.");
return true;

}

getpacket

Support Intel CET's instructions

Intel® Control-Flow Enforcement Technology (Intel CET)

Problem

Since 2018, compilers started placing ENDBR instructions at the beginning of functions/branch, making it impossible for subhook to make a trampoline.

At my environment, whole glibc is compiled with such instructions, making it impossible for me to hook & trampoline any standard function.

Moreover, with modern processors, absence of ENDBR prevents indirect jumping at arbitrary address, and thus, requires small redesign of subhook's trampoline construction.

Proposed fix

  • A quick fix (for old processors)
    Detect a single, 4-bytes long ENDBR32 or ENDBR64 instruction. As ENDBR are equal to NOP for processors with old ISA, prior to circa. 2019, we can carry on with copying / discarding the instruction.

  • A proper fix
    I presume that a proper fix would require you to change both JMPs to include No-track prefix, and put the jump to trampoline AFTER ENDBR instruction of original function.
    This can be, however, problematic, as I noticed that you use RET for AMD64 architecture, which will probably cause troubles with so-called Shadow stack, requiring you to work around with special Intel's instructions to for changing the stack.

In either case, with Indirect Branch Tracking enabled by both CPU and binary file, subhook will not work, because in such case, every function starts with ENDBR.

To get familiar, see Black Hat's PDF file

PVS-Studio errors

This is possible mistakes given by PVS-Studio static analyzer. It may help with something?

===============64 bit (64)===============
  (1): error V004: Diagnostics from the 64-bit rule set are not entirely accurate without the appropriate 64-bit compiler. Consider utilizing 64-bit compiler if possible.
subhook\subhook_x86.c (372): error V104: Implicit conversion of 'reloc_op_offset' to memsize type in an arithmetic expression.
subhook\subhook_x86.c (251): error V112: Dangerous magic number 4 used: len += 4;...
subhook\subhook_x86.c (236): error V112: Dangerous magic number 4 used: len += 4;...
subhook\subhook_x86.c (225): error V112: Dangerous magic number 4 used: ...3 && rm == 4) {.
subhook\subhook_x86.c (493): error V104: Implicit conversion of 'maybe_jmp32->offset' to memsize type in an arithmetic expression: maybe_jmp32->offset + (uintptr_t) src
subhook\subhook_x86.c (164): error V112: Dangerous magic number 4 used: ...rand_size = 4;.
subhook\subhook_private.h (40): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (47): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (46): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (45): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (44): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (43): error V122: Memsize type is used in the struct/class.
subhook\subhook_private.h (41): error V122: Memsize type is used in the struct/class.
subhook\subhook_x86.c (295): error V202: Explicit conversion from memsize type to 32-bit integer type.
subhook\subhook_x86.c (371): error V202: Explicit conversion from memsize type to 32-bit integer type: (int32_t)(trampoline_addr - src_addr)
subhook\subhook_x86.c (217): error V202: Explicit conversion from memsize type to 32-bit integer type: (int32_t) len

===============General Analysis (GA)===============
subhook\subhook_x86.c (47): error V677: Custom declaration of a standard 'uintptr_t' type. The system header file should be used: #include <STDDEF.H>.
subhook\subhook_x86.c (290): error V560: A part of conditional expression is always false: distance < (- 0x7fffffff - 1).
subhook\subhook_x86.c (290): error V560: A part of conditional expression is always false: distance > 0x7fffffff.
subhook\subhook_x86.c (46): error V677: Custom declaration of a standard 'intptr_t' type. The system header file should be used: #include <STDDEF.H>.

CMake cache variables

After these changes: 69ed7f0 we need to add CACHE INTERNAL "" into all of our subhook parameters for working after first cmake calling (after second and future calls everything is ok):

set (SUBHOOK_STATIC true CACHE INTERNAL "")
set (SUBHOOK_TESTS false CACHE INTERNAL "")

This is by design?

Hook install segfault

Upgraded from 0.7 to 0.8 because I thought 64-bit trampolines might be fixed, installing some hooks now causes a segmentation fault.

I've narrowed it down to this call to free in subhook_new:

free(hook->trampoline);

GetTrampoline() Returns 0 If Hooking WINAPI

It works well with normal functions but when hooking something from WIN32 API it return 0 .
Lets say we hooked MessageBox the only to call the original function would be using a ScopedRemove or a Remove.

x64 trampolines rarely working

This is with v0.8.1, same issue as #15 and #43.

I'm hooking 42 functions in an x64 Linux binary. If I look at the output of GetTrampoline() on every hook after it's installed, 12 of them have trampolines, and the other 30 do not (nullptr).

I'm using subhook::HookFlags::HookFlag64BitOffset, I tried it without as well and the hooks didn't install properly.

The hooks of course work fine without using trampolines, but some of the functions are called very frequently so it would be nice to eliminate all the copying if possible.

Trampoline failures

Hi. May be it's same as x64 trampoline fail #15, but i'm not sure =)

Background

I tried to install hook on x86_64.elf shared object function and was disappointed with error #75 (EOVERFLOW). Next i tried to use SUBHOOK_64BIT_OFFSET flag and it became another grief for me. =)

Point of my problems

As i see subhook trampoline calls raises undefined undefined behaviour [always SIGSEGV? not sure] in two cases:

1. under x86_64 with SUBHOOK_64BIT_OFFSET

(gdb) run
Starting program: /tmp/build/subhook-trampoline-test/x86_64-desktop/debug/test 
main: hook = 0x555555570e70
main: executing foo...
foo: value = 1
main: hook installed
main: executing foo...
foo_replacement: value = 1, trampoline = 0
main: trampoline = 0x555555570ee0
main: executing foo...
foo_replacement: value = 1, trampoline = 0x555555570ee0
foo_replacement: executing trampoline [0x555555570ee0]...

Program received signal SIGSEGV, Segmentation fault.
0x0000555555570eec in ?? ()
(gdb) bt
#0  0x0000555555570eec in ?? ()
#1  0x0000000000000000 in ?? ()
(gdb) generate-core-file 
Saved corefile core.15694

core.15694.gz

2. under x86 with size-optimized foo function build (gcc "-Os" flag)

(gdb) run
Starting program: /tmp/build/subhook-trampoline-test/x86-desktop/release+min_size/test 
main: hook = 0x56560b70
main: executing foo...
foo: value = 1
main: hook installed
main: executing foo...
foo_replacement: value = 1, trampoline = 0
main: trampoline = 0x56560bb0
main: executing foo...
foo_replacement: value = 1, trampoline = 0x56560bb0
foo_replacement: executing trampoline [0x56560bb0]...

Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0  0x00000000 in ?? ()
#1  0xf7fc5273 in foo () from /tmp/build/subhook-trampoline-test/x86-desktop/release+min_size/libfoo.so
#2  0x5655784a in foo_replacement(int) ()
#3  0x56557ff2 in routine() ()
#4  0x56557294 in main ()
(gdb) generate-core-file 
Saved corefile core.15628

core.15628.gz

Test project on github

I published a test project on github to demonstrate this problem. Please check it.

Can you fix this bugs?
Thanks a lot!

readdressing?

in Trampoline, the code use re-locate need to be readdress, curious that have you coded about that?
and,
what disam program you recommend?

Problems with updating to 3.0

After updating subhook to 3.0 I got these errors:

$ make
[  6%] Built target subhook
[ 10%] Building CXX object CMakeFiles/FCNPC.dir/src/Hooks.cpp.o
In file included from /home/ziggi/devgit/FCNPC/src/Main.hpp:39:0,
                 from /home/ziggi/devgit/FCNPC/src/Hooks.cpp:11:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h: In constructor ‘SubHook::SubHook(void*, void*)’:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:122:61: error: too few arguments to function ‘subhook* subhook_new(void*, void*, subhook_options_t)’
   SubHook(void *src, void *dst) : hook_(subhook_new(src, dst)) {}
                                                             ^
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:96:38: note: declared here
 SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
                                      ^~~~~~~~~~~
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h: In member function ‘bool SubHook::Install(void*, void*)’:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:139:35: error: too few arguments to function ‘subhook* subhook_new(void*, void*, subhook_options_t)’
       hook_ = subhook_new(src, dst);
                                   ^
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:96:38: note: declared here
 SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
                                      ^~~~~~~~~~~
/home/ziggi/devgit/FCNPC/src/Hooks.cpp: In static member function ‘static void CHooks::InstallHooks()’:
/home/ziggi/devgit/FCNPC/src/Hooks.cpp:325:72: error: too few arguments to function  subhook* subhook_new(void*, void*, subhook_options_t)’
  hookFindPublic = subhook_new(pFindPublic, (BYTE *)&amx_FindPublic_Hook);
                                                                        ^
In file included from /home/ziggi/devgit/FCNPC/src/Main.hpp:39:0,
                 from /home/ziggi/devgit/FCNPC/src/Hooks.cpp:11:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:96:38: note: declared here
 SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
                                      ^~~~~~~~~~~
/home/ziggi/devgit/FCNPC/src/Hooks.cpp:329:54: error: too few arguments to function  subhook* subhook_new(void*, void*, subhook_options_t)’
  hookPush = subhook_new(pPush, (BYTE *)&amx_Push_Hook);
                                                      ^
In file included from /home/ziggi/devgit/FCNPC/src/Main.hpp:39:0,
                 from /home/ziggi/devgit/FCNPC/src/Hooks.cpp:11:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:96:38: note: declared here
 SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
                                      ^~~~~~~~~~~
/home/ziggi/devgit/FCNPC/src/Hooks.cpp:333:54: error: too few arguments to function  subhook* subhook_new(void*, void*, subhook_options_t)’
  hookExec = subhook_new(pExec, (BYTE *)&amx_Exec_Hook);
                                                      ^
In file included from /home/ziggi/devgit/FCNPC/src/Main.hpp:39:0,
                 from /home/ziggi/devgit/FCNPC/src/Hooks.cpp:11:
/home/ziggi/devgit/FCNPC/src/subhook/subhook.h:96:38: note: declared here
 SUBHOOK_EXPORT subhook_t SUBHOOK_API subhook_new(void *src,
                                      ^~~~~~~~~~~
make[2]: *** [CMakeFiles/FCNPC.dir/build.make:255: CMakeFiles/FCNPC.dir/src/Hooks.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:68: CMakeFiles/FCNPC.dir/all] Error 2
make: *** [Makefile:150: all] Error 2

Can you fix it please?

System information:

$ g++ --version
g++ (GCC) 6.1.1 20160802
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ uname -mosr
Linux 4.6.4-1-ARCH x86_64 GNU/Linux

unresolved external symbols

When I want to use this as a static library and redefine SUBHOOK_EXPORT to (nothing) this happens:

What do I need to do to make it compile correctly? In my project I included "subhook.c" and "subhook.h" (for the compiler list) on Visual Studio 2013. But I'm getting these linker errors:

main.obj : error LNK2019: unresolved external symbol "struct subhook * __cdecl subhook_new(void *,void *)" (?subhook_new@@YAPAUsubhook@@pax0@Z) referenced in function "public: bool __thiscall SubHook::Install(void *,void *)" (?Install@SubHook@@QAE_NPAX0@Z)
...(and a few more)...

There maybe some bugs in rex prefix judge of 64-bits

image
when I use subhook to hook this function, the function 'subhook_disasm' return the len of line which in red box to be 11-byte, but actually it is 7-byte.
I check the intel document to find out why, but I can't understand it.
The picture is I compile a C++ code with

  • g++: 4.8.5 20150623
  • 64-bits red-hat machine
  • kernal info "3.10.0-327.13.1.el7.x86_64"

And I haven't find a machine code with prefix 0x48 has length larger than 7.

Would you mind to check this question? thanks a lot.

Additional hook indirection

When hooking logprintf, it is impossible to call the original function via the trampoline, passing the original arguments, since it is not possible to pass the parameter pack this way.

Currently, the hook function does something like this:

jmp handler

I suggest an option that would generate this code instead:

call handler
jmp eax

This way, I could first decide in the handler whether to handle the call or not (returning my custom handler or the trampoline function pointer) and the hook will call the function. I don't know if it is possible to expose the arguments to this new handler as well, but it would be cool.

YSF for example uses this handler (with custom assembly as the actual hook):

void *logprintf_trampoline()
{
	// If server messages aren't handled, the hook will jump to the trampoline
	if(CPlugin::Get()->IsOnServerMessageEnabled() && CPlugin::Get()->IsMainThread())
	{
		return reinterpret_cast<void*>(&custom_logprintf);
	}else{
		return subhook_get_trampoline(logprintf_hook);
	}
}

This guarantees that when the user doesn't want to handle the call, the original function will be called every time, without modifications to the arguments. This way, other plugins could freely hook logprintf as well.

Another use case is making logprintf thread safe by first checking in the handler if it is the main thread or not, and then return either the trampoline or some thread-safe implementation (which uses a custom log, pushes the message to a queue or doesn't do anything).

Subhook causing server to crash?

Hey, I have been trying to fix this for days however I failed to. As a note: server has no network access so packages cannot be updated.
Details:
CentOS 6.6 x86_64
Server crashed while it tried to load YSF, kurta version(I also tried SKY, both are making the server crash). The server_log provides some info I have tried to debug and understand however I couldn't:

[19/07/2015 01:42:01] [debug] Server crashed due to an unknown error
[19/07/2015 01:42:02] [debug] Native backtrace:
[19/07/2015 01:42:02] [debug] #0 0043950b in _ZN10StackTraceC1EPv () from plugins/crashdetect.so
[19/07/2015 01:42:02] [debug] #1 0043220f in _ZN11CrashDetect20PrintNativeBacktraceERSoPv () from plugins/crashdetect.so
[19/07/2015 01:42:02] [debug] #2 004333fc in _ZN11CrashDetect20PrintNativeBacktraceEPv () from plugins/crashdetect.so
[19/07/2015 01:42:02] [debug] #3 00433866 in _ZN11CrashDetect11OnExceptionEPv () from plugins/crashdetect.so
[19/07/2015 01:42:02] [debug] #4 0043915c in ?? () from plugins/crashdetect.so
[19/07/2015 01:42:02] [debug] #5 00d22410 in ?? ()
[19/07/2015 01:42:02] [debug] #6 004a2a85 in subhook_install () from plugins/YSF.so
[19/07/2015 01:42:02] [debug] #7 004743d1 in _Z15InstallPreHooksv () from plugins/YSF.so
[19/07/2015 01:42:02] [debug] #8 00488bf7 in Load () from plugins/YSF.so
[19/07/2015 01:42:02] [debug] #9 080d2742 in ?? () from ./samp03svr
[19/07/2015 01:42:02] [debug] #10 080d2afa in ?? () from ./samp03svr
[19/07/2015 01:42:02] [debug] #11 080aa0d0 in ?? () from ./samp03svr
[19/07/2015 01:42:02] [debug] #12 0029bd36 in __libc_start_main () from /lib/libc.so.6
[19/07/2015 01:42:02] [debug] #13 0804b4e1 in ?? () from ./samp03svr

Some extra info: as I tried to understand stuff a little bit by myself, I figured out the plugin crashes after this line:
Namecheck_hook.Install((void *)CAddress::FUNC_ContainsInvalidChars, (void *)HOOK_ContainsInvalidChars);
SA-MP server is 0.3.7 R2, plugin is the lastest one by kurta(R14 I believe).
Thank you for your help!

subhook_make_jmp64 might SIGSEGV if a function is located across a page boundary

Hello,

I am successfully using subhook in loadlibrary so, first of all, thank you for your effort to put this project together!

Recently I might have discovered a bug in subhook:
I noticed that when mprotect is executed, even if size == sizeof(struct subhook_jmp64), the permission are changed on the entire page instead.

For example, let's say we are creating an x86_64 hook on the following function:

(gdb) x/1i src
   0x55de0714b9aa <DeleteCriticalSection>:	push   %rbp

After we executed mprotect (in subhook_unprotect()):
return mprotect(address, size, SUBHOOK_CODE_PROTECT_FLAGS); (with size == 0x14)

the following output is returned by cat /proc/$PID/maps:

55de0714b000-55de0714c000 rwxp 00014000 08:01 787782

So, as you can notice, it looks like the permissions are changed on 0x1000 bytes instead of 0x14.

This said, we could face a scenario where the first 0x14 of a function are located across a page boundary, so not all of these 0x14 will become RWX.

For example, let's consider this function:

(gdb) x/1i address
   0x55de0714bff8 <CertStrToNameW>:	push   %rbp

whose the page address range is 0x55de0714b000 - 0x55de0714c000 and the first 0x14 bytes of the function ends between 0x55de0714bff8 and 0x55de0714c00c. It's easy to spot how the privileges of the very first 0x8 bytes of the function are affected from the call to mprotect, while the other 0xc remains unchanged.

This will inevitably lead to a SIGSEGV in subhook_make_jmp64.

Here, I implemented a quick fix which works. Feel free to cherry-pick it, if you want.

I hope the explanation was exhausting and I made the entire point clear.

Test not working on AppVeyor

The test program is not working on AppVeyor for some reason.

It doesn't crash or anything, just none of the hooks seem to install. However, subhook_install() returns success in all cases.

It works fine on my Windows 10 machine though (built with Visual Studio 2015, x86 and x64).

License

Not a a bug In the program but still an Issues , why ?
without a license the project is all rights reserved for you and we are not allowed to use it or run it .
and please dont select GPL or LGPL :(, use MIT if you want to people to include your copyright in the program or Public Domain If Its Just Something your giving to people without any restrictions ..

trampoline fails on X86_64 due to "endbr64" instruction not handled?

I am trying to create a trampoline to a function that in C is:

void foo(void) {
  int a, b, c;
  puts("foo() called");
  a = random();
  b = (random() + a) % (random() & 0xff);
  c = a + b;
  printf("foo: value of c = %d\n", c);
}

I am using gcc 9.3 on ubuntu 20.04 64 bit w/ Linux kernel 5.8.0-53-generic

Compiled code is:

(gdb) x/20iw foo
   0x5555555552c3 <foo>:	endbr64 
   0x5555555552c7 <foo+4>:	push   %rbp
   0x5555555552c8 <foo+5>:	mov    %rsp,%rbp
   0x5555555552cb <foo+8>:	push   %rbx
   0x5555555552cc <foo+9>:	sub    $0x18,%rsp
   0x5555555552d0 <foo+13>:	lea    0xe09(%rip),%rdi        # 0x5555555560e0
   0x5555555552d7 <foo+20>:	callq  0x5555555550d0 <puts@plt>
   0x5555555552dc <foo+25>:	callq  0x555555555110 <random@plt>
   0x5555555552e1 <foo+30>:	mov    %eax,-0x1c(%rbp)
   0x5555555552e4 <foo+33>:	callq  0x555555555110 <random@plt>
   0x5555555552e9 <foo+38>:	mov    -0x1c(%rbp),%edx
   0x5555555552ec <foo+41>:	movslq %edx,%rdx
   0x5555555552ef <foo+44>:	lea    (%rax,%rdx,1),%rbx
   0x5555555552f3 <foo+48>:	callq  0x555555555110 <random@plt>
   0x5555555552f8 <foo+53>:	movzbl %al,%ecx
   0x5555555552fb <foo+56>:	mov    %rbx,%rax
   0x5555555552fe <foo+59>:	cqto   
   0x555555555300 <foo+61>:	idiv   %rcx
   0x555555555303 <foo+64>:	mov    %rdx,%rax
   0x555555555306 <foo+67>:	mov    %eax,-0x18(%rbp)

And in bytes it is:

(gdb) x/20xw foo
0x5555555552c3 <foo>:	         0xfa1e0ff3	0xe5894855	0xec834853	0x3d8d4818
0x5555555552d3 <foo+16>:	0x00000e09	0xfffdf4e8	0xfe2fe8ff	0x4589ffff
0x5555555552e3 <foo+32>:	0xfe27e8e4	0x558bffff	0xd26348e4	0x101c8d48
0x5555555552f3 <foo+48>:	0xfffe18e8	0xc8b60fff	0x48d88948	0xf9f74899
0x555555555303 <foo+64>:	0x89d08948	0x558be845	0xe8458be4	0x4589d001

subhook_disasm() fails to decode this function. I have tried to understand subhook_diasm() but I can't tell just what the issue (or the fix) might me. Any hints welcome, including if problem is something else.

On older 64 bit linux system w/ GCC 4.4.7 this function is:

gdb) x/20iw foo
   0x400ac9 <foo>:	push   %rbp
   0x400aca <foo+1>:	mov    %rsp,%rbp
   0x400acd <foo+4>:	push   %rbx
   0x400ace <foo+5>:	sub    $0x28,%rsp
   0x400ad2 <foo+9>:	mov    $0x400df0,%edi
   0x400ad7 <foo+14>:	callq  0x4008e0 <puts@plt>
   0x400adc <foo+19>:	callq  0x400950 <random@plt>
   0x400ae1 <foo+24>:	mov    %eax,-0x1c(%rbp)
   0x400ae4 <foo+27>:	callq  0x400950 <random@plt>
   0x400ae9 <foo+32>:	mov    -0x1c(%rbp),%edx
   0x400aec <foo+35>:	movslq %edx,%rdx
   0x400aef <foo+38>:	lea    (%rax,%rdx,1),%rbx
   0x400af3 <foo+42>:	callq  0x400950 <random@plt>
   0x400af8 <foo+47>:	and    $0xff,%eax
   0x400afd <foo+52>:	mov    %rax,-0x28(%rbp)
   0x400b01 <foo+56>:	mov    %rbx,%rdx
   0x400b04 <foo+59>:	mov    %rdx,%rax
   0x400b07 <foo+62>:	sar    $0x3f,%rdx
   0x400b0b <foo+66>:	idivq  -0x28(%rbp)
   0x400b0f <foo+70>:	mov    %rdx,%rax

Here trampoline works fine (the difference that is important I believe is the "endbr64" at start when using GCC 9.3

Is there any plan to continue supporting macos?

When working on #58 I noticed subhook is broken for macos. The mprotect() call in subhook_unprotect() will keep failing with EACCES, because in latest macos versions it's not supported to make process code writable. With this restriction subhook won't be able to work for macos.

Is there a plan to workaround such restrictions and keep supporting macos? With Apple emphasizing on running only signed code, it seems hard to do so?

warning: variable ‘address_size’ set but not used

Scanning dependencies of target subhook
[  3%] Building C object lib/subhook/CMakeFiles/subhook.dir/subhook.c.o
In file included from /home/ziggi/devgit/FCNPC/lib/subhook/subhook.c:52:0:
/home/ziggi/devgit/FCNPC/lib/subhook/subhook_x86.c: In function ‘subhook_disasm’:
/home/ziggi/devgit/FCNPC/lib/subhook/subhook_x86.c:171:10: warning: variable ‘address_size’ set but not used [-Wunused-but-set-variable]
   size_t address_size = 4;
          ^~~~~~~~~~~~
[  6%] Linking C static library libsubhook.a
[  6%] Built target subhook

/O2 crash

Hi,

I think your hooking library does not like /O2 because it crash.

https://github.com/kurta999/YSF/blob/YSF_/Hooks.cpp

static void HOOK_amx_Register(AMX *amx, AMX_NATIVE_INFO *nativelist, int number)

[2014/10/03 15:29:28] [debug] Server crashed due to an unknown error
[2014/10/03 15:29:28] [debug] Native backtrace:
[debug] #0 02147b47 in HOOK_amx_Register () from plugins\YSF.dll
[debug] #1 00404b51 in ?? () from samp-server.exe

0.3z R4

subhook_disasm can't be "subclassed"

I want to create a disassembler that first tries the default disassembler and only takes over if that fails. This way I can hardcode support for just a few extra opcodes without needing a heavy disassembler. However it feels like it was especially designed to make this impossible. A few things that would make this a lot easier:

  • subhook_disasm should be exported. Then I can reference it in my custom disassembler.
  • subhook_disasm_handler should be initialized with subhook_disasm. Then I could get the address of subhook_disasm this way as an alternative to exporting it.

Segfault in tests on FreeBSD

Subhook looks really cool! It would be awesome if it could support FreeBSD. I tried building and testing locally, and it builds fine (with the addition of a trivial #if defined(__FreeBSD__)), but it segfaults while running the tests. I'll look more into this later to see what I can find out.

If you're interested, once things are working on FreeBSD, you could add it to your CI using Cirrus CI or sr.ht.

x64 trampoline fail

Hello, it is impossible to make trampoline in x64, because of disasm: it have no needed flags(imm64, etc), and does not recognize standard x64 operands: push r12, sub rsp, 0x20 etc. Will be nice to see ready to x64 disasm here. Cheers ;)

Fail to run in MacOS Catalina 10.15.4

Here is the error log.

$ make test
Running tests...
Test project /Users/tobe/code/subhook/buid
    Start 1: subhook_test_exe_test
1/2 Test #1: subhook_test_exe_test ............***Failed  Required regular expression not found. Regex=[Testing initial install
foo_hooked\(\) called
foo\(\) called
Testing re-install
foo_hooked\(\) called
foo\(\) called
Testing trampoline
foo_hooked_tr\(\) called
foo\(\) called

]  0.00 sec
    Start 2: subhook_cxx_test_exe_test
2/2 Test #2: subhook_cxx_test_exe_test ........***Failed  Required regular expression not found. Regex=[Testing initial install
foo_hooked\(\) called
foo\(\) called
Testing re-install
foo_hooked\(\) called
foo\(\) called
Testing trampoline
foo_hooked_tr\(\) called
foo\(\) called

]  0.00 sec

0% tests passed, 2 tests failed out of 2

Total Test time (real) =   0.01 sec

The following tests FAILED:
          1 - subhook_test_exe_test (Failed)
          2 - subhook_cxx_test_exe_test (Failed)
Errors while running CTest
make: *** [test] Error 8

Method installation failed ?

Hi,
First, great project ! Thank u for doing it !
I've tried to use subhook in order to replace Oracle Java 10 libjvm.so methods.
While everything worked for me for Java 8, certain hook failed for Java 10.
I have tried to install a hook instead of the DumperWriter::write_internal() method
but my method (the replacing one) is never being called although I know it supposed to.

I have printed the content of the relevant addresses in order to try to provide the info.

The following is the output of the implementation of the method without any hooking:

Found symbol address for symbol _ZN10DumpWriter14write_internalEPvm
Symbol address 0x7f83cc903970
0x7f83cc903970 : 0x56415741e5894855
0x7f83cc903978 : 0xec83485354415541
0x7f83cc903980 : 0xc87d8948278b4418
0x7f83cc903988 : 0x8f880fe48545
0x7f83cc903990 : 0x49f38948d2854800

The following is the output of my program after installing the hook:

// Look for the hook and print its content (first 5 8 bytes):
Found symbol address for symbol _ZN10DumpWriter14write_internalEPvm
Symbol address 0x7f68c7c67970 // same one
0x7f68c7c67970 : 0x564157018bfd2be9
0x7f68c7c67978 : 0xec83485354415541
0x7f68c7c67980 : 0xc87d8948278b4418
0x7f68c7c67988 : 0x8f880fe48545
0x7f68c7c67990 : 0x49f38948d2854800

When I'm getting the hook for this method I'm getting the following:

hookGetSrc() 0x7f68c7c67970 // expected
hook GetDst() 0x7f68c95276a0
hook GetTrampoline() 0x7f68c0026730 // expected ?

I'm using subhook version 0.4.2.
Was there such a known issue in this version ?
Thanks in advance !

Subhook 0.2 crash with YSF

I added SAMPGDK into YSF, and seems that subhook 0.2 doesn't like it. With 0.1 everything fine.

I'm anyway hook amx_Register from YSF, I think this caused this crash.

[code]
[debug] #0 01e8250a in SubHook::Install () from plugins\YSF.dll
[debug] #1 01e8b3ee in InstallPreHooks () from plugins\YSF.dll
[debug] #2 01e8c4d5 in Load () from plugins\YSF.dll
[debug] #3 00469e5b in ?? () from samp-server.exe
[debug] #4 00469fac in ?? () from samp-server.exe
[debug] #5 0018fd24 in ?? ()
[debug] #6 4ae32468 in ?? () from samp-server.exe
[debug] #7 6805eb00 in ?? () from samp-server.exe
[debug] #8 004ae330 in ?? () from samp-server.exe
[debug] #9 01d62fe8 in ?? ()
[debug] #10 64656c69 in ?? () from samp-server.exe
[/code]

Linker errors

Pretty sure this is a problem on my end, my heads just hurting from this
error LNK2005: subhook_alloc_code already defined in subhook.obj
I cant find any reason why this would be defined more than once, any ideas?

MAX_INSN_LEN can be too small for 64-bit jmps

With the newly-added 64-bit jmps, 14 bytes are required. MAX_INSN_LEN is defined as only 15, with trampoline size using the jmp size plus MAX_INSN_LEN - 1 (14).

With needing to end on an instruction boundary, that leaves no extra room for all of the src and jmp bytes in the trampoline. For example, in the example function I posted in #15, 15 bytes are needed for the src instructions that get copied.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.