- Notifications
You must be signed in to change notification settings - Fork162
kubo/plthook
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A utility library to hook library function calls issued byspecified object files (executable and libraries). This modifiesPLT (Procedure Linkage Table) entries inELF format used on most UnixesorIAT (Import Address Table) entries in PE format used on Windows.
Note: This isn't precise explanation. Some details are omitted.
When a function calls another function in another file, it is called via PLT (onUnix using ELF) or IAT (on Windows).
In order to callfoo_func()
inlibfoo.so
, the address of the callee must beknown. When callers are in the same file, the relative address to the callee isknown at compile time regardless of the absolute address at run time. Sosome_func()
callsfoo_func()
using relative addressing.
When callers are in other files, the address of the callee cannot be known atcompile time. To resolve it, each file has a mapping from external function namesto addresses. The callers directly look at the address in the PLT entry forfoo_func()
and jump to the address.
The addresses in PLT entries are resolved (1) at process startup or (2) at firstfunction call (lazy binding). It depends on OSes or on settings.
Plthook changes the address in PLT entries as above.Whenfoo_func()
is called fromprogram
,hook_foo_func()
is called instead.It doesn't change function calls fromlibfoo.so
andlibbar.so
.
When the hook functionhook_foo_func()
is inlibbar.so
, just call theoriginal functionfoo_func()
. It looks the PLT entry inlibbar.so
and jumpsto the original.
When the hook functionhook_foo_func()
is inprogram
, do not call theoriginal functionfoo_func()
because it jumps tohook_foo_func()
repeatedlyand crashes the process after memory for stack is exhausted. You need to get theaddress of the original function and set it to the function pointer variablefoo_func_addr
. Use the fourth argument ofplthook_replace()
to get theaddress on Windows. Use the return value ofdlsym(RTLD_DEFAULT, "foo_func")
onUnixes. The fourth argument ofplthook_replace()
isn't available on Unixesbecause it doesn't set the address of the original before the address in the PLTentry is resolved.
2024-09-02: Fix issues on macOS (#48)
2024-08-05: Addplthook_enum_with_prot()
to enumerate entries with memory protection information. (plthook_elf.c and plthook_osx.c)
2023-06-01: Add riscv support. (plthook_elf.c) (#45)
2022-09-19: Drop macOS 32-bit application support. Drop support for macOS 10.14 Mojave or before.
2022-08-12: Support LC_DYLD_CHAINED_FIXUPS on macOS intel
2020-03-30: Check _start also in plthook_open_by_handle() (plthook_elf.c) (#29)
2020-03-09: Add support for uClibc. (#28)
2019-11-14: Fix potential incorrect parsing of /proc/self/maps on linux. (#24)
2019-11-14: Fix possible double-close issue in plthook_elf.c (#23)
2019-09-27: Fix resource leaks when the format of /proc/self/maps is unexpected on Linux. (#20)
2019-09-26: Fix SEGV when plthook_open(..., "/usr/lib/libc.dylib") on macOS. (#19)
2019-02-17: Supportplthook_open_by_address()
and changeinternal logic ofplthook_open()
on Android.
2019-02-17: Stop checking RELRO and check memory protection atruntime instead.
2019-02-03: Fix crash when programs are compiled with compiler options-Wl,-z,relro
and-fno-plt
with the help ofJC Liang. (#10)
2018-02-06: Android support was contributed byDaniel Deptford.
2017-10-01:plthook_elf.c
was rewritten. Plthook had needed toread files on filesystem to get various information about targetobject files. It now do it only for full RELRO object files.Note that plthook before 2017-10-01 gets segmentation fault whilehooking aprelinked file on Linux.
2017-09-18: Fixed for processes onvalgrind on Linux.
If you have a librarylibfoo.so.1
and want to intercepta function callrecv()
without modifying the library,putplthook.h
andplthook_elf.c
,plthook_win32.c
orplthook_osx.c
in your source tree and add the following code.
#include"plthook.h"/* This function is called instead of recv() called by libfoo.so.1 */staticssize_tmy_recv(intsockfd,void*buf,size_tlen,intflags){ssize_trv; ...doyourtask:logging,etc. ...rv=recv(sockfd,buf,len,flags);/* call real recv(). */ ...doyourtask:logging,checkreceiveddata,etc. ...returnrv;}intinstall_hook_function(){plthook_t*plthook;if (plthook_open(&plthook,"libfoo.so.1")!=0) {printf("plthook_open error: %s\n",plthook_error());return-1; }if (plthook_replace(plthook,"recv", (void*)my_recv,NULL)!=0) {printf("plthook_replace error: %s\n",plthook_error());plthook_close(plthook);return-1; }plthook_close(plthook);return0;}
The above code doesn't work whenmy_recv()
is in the file opened byplthook_open()
as describedhere.Use the following code instead in the case.
staticssize_t (*recv_func)(intsockfd,void*buf,size_tlen,intflags);/* This function is called instead of recv() called by libfoo.so.1 */staticssize_tmy_recv(intsockfd,void*buf,size_tlen,intflags){ssize_trv; ...doyourtask:logging,etc. ...rv= (*recv_func)(sockfd,buf,len,flags);/* call real recv(). */ ...doyourtask:logging,checkreceiveddata,etc. ...returnrv;}intinstall_hook_function(){plthook_t*plthook;if (plthook_open_by_address(&plthook,&recv_func)!=0) {printf("plthook_open error: %s\n",plthook_error());return-1; }if (plthook_replace(plthook,"recv", (void*)my_recv, (void**)&recv_func)!=0) {printf("plthook_replace error: %s\n",plthook_error());plthook_close(plthook);return-1; }#ifndefWIN32// The address passed to the fourth argument of plthook_replace() is// available on Windows. But not on Unixes. Get the real address by dlsym().recv_func= (ssize_t (*)(int,void*,size_t,int))dlsym(RTLD_DEFAULT,"recv");#endifplthook_close(plthook);return0;}
Note that built-in functions cannot be hooked. For example the Ccompiler in macOS Sierra compilesceil()
as inline assembly code,not as function call ofceil
in the system library.
When a functions is imported byordinal on Windows,the function name is specified byexport_dll_name:@ordinal
.For exampleapi-ms-win-shcore-path-l1-1-0.dll:@170
.
PLTHook provides a function enumerating PLT/IAT entries.
voidprint_plt_entries(constchar*filename){plthook_t*plthook;unsignedintpos=0;/* This must be initialized with zero. */constchar*name;void**addr;if (plthook_open(&plthook,filename)!=0) {printf("plthook_open error: %s\n",plthook_error());return-1; }while (plthook_enum(plthook,&pos,&name,&addr)==0) {printf("%p(%p) %s\n",addr,*addr,name); }plthook_close(plthook);return0;}
Platform | source file | status |
---|---|---|
Linux i386 and x86_64 | plthook_elf.c | tested usinggithub actions |
Linux arm, aarch64, powerpc and powerpc64le | plthook_elf.c | tested onQEMU usinggithub actions |
Windows 32-bit and x64 (MSVC) | plthook_win32.c | tested usinggithub actions |
macOS (intel) (*4) | plthook_osx.c | tested usinggithub actions |
macOS (arm) | plthook_osx.c | tested usinggithub actions |
Windows 32-bit and x64 (Mingw32 and Cygwin) | plthook_win32.c | perhaps(*2) |
Solaris x86_64 | plthook_elf.c | perhaps(*1) |
FreeBSD i386 and x86_64 except i386 program on x86_64 OS | plthook_elf.c | perhaps(*1) |
Android(*3) | plthook_elf.c | perhaps(*2) |
*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed byDaniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.
2-clause BSD-style license.