zeex / subhook Goto Github PK
View Code? Open in Web Editor NEWSimple hooking library for C/C++ (x86 only, 32/64-bit, no dependencies)
License: BSD 2-Clause "Simplified" License
Simple hooking library for C/C++ (x86 only, 32/64-bit, no dependencies)
License: BSD 2-Clause "Simplified" License
NOP instruction (0x90) can not be recognized..
subhook_disasm return 0
I really hope this project can support ARM platform. Thank you
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).
Hi. May be it's same as x64 trampoline fail #15, but i'm not sure =)
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. =)
As i see subhook trampoline calls raises undefined undefined behaviour [always SIGSEGV? not sure] in two cases:
(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
(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
I published a test project on github to demonstrate this problem. Please check it.
Can you fix this bugs?
Thanks a lot!
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.
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!
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 ..
With SUBHOOK_OPTION_64BIT_OFFSET set, subhook_make_jmp64 correctly gets called, but then subhook_make_jmp32 gets called unconditionally, overwriting the 64-bit jmp.
Line 272 in d2a5756
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?
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:
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.
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.
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]
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?
Intel® Control-Flow Enforcement Technology (Intel CET)
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.
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
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>.
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
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 ;)
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.
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);
I'm pretty sure I'm getting some kind of exception and I'm wondering if __stdcall functions require something special?
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.
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;
}
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.
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
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).
I've looked at the previous thread on this and it has not solved it. It crashes the program I'm hooking.
As title, i thought it can only be used under win&linux until i glanced through the source file.
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?
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.
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
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)
Line 330 in d2a5756
(Only tested with 64-bit hooking).
Line 52 in c591a96
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?
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.
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 !
delete
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)...
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?
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
:
Line 506 in 1420130
in Trampoline, the code use re-locate need to be readdress, curious that have you coded about that?
and,
what disam program you recommend?
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.
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
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
The README example illustrated hooks for global functions. Do hooks work for instanced class methods (virtual and non-virtual), and method overloads?
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
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.
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?
I have generated vs 2013 project from cmake(cmake -G "Visual Studio 12 2013 Win64")
Hook works in debug subhook.dll build.
But it does not work if compile library in release mode
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.