Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - manderrow/plthook: Hook function calls by replacing PLT(Procedure Linkage Table) entries. · GitHub
Skip to content

Repository files navigation

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2024-09-02: Fix issues on macOS (#48)

2024-08-05: Add plthook_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: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01:plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_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 when my_recv() is in the file opened by plthook_open() as described here. 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 C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

voidprint_plt_entries(constchar*filename)
{
plthook_t*plthook;
unsigned intpos=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;
}

Supported Platforms

Platformsource filestatus
Linux i386 and x86_64plthook_elf.ctested using github actions
Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU using github actions
Windows 32-bit and x64 (MSVC)plthook_win32.ctested using github actions
macOS (intel) (*4)plthook_osx.ctested using github actions
macOS (arm)plthook_osx.ctested using github actions
Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps(*2)
Solaris x86_64plthook_elf.cperhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OSplthook_elf.cperhaps(*1)
Android(*3)plthook_elf.cperhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 macOS 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

About

Hook function calls by replacing PLT(Procedure Linkage Table) entries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages