Git Product home page Git Product logo

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

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

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!

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.

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!

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

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?

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.

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.

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.

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]

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?

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

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

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

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.

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

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.

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

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.

/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

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

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?

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.

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

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

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?

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.

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 !

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

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

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?

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.

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

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?

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.

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?

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

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.