3.5 KiB
Intercepting and Manipulating Function and System Calls in Linux
Option 1: Preloading (LD_PRELOAD)
From the ENVIRONMENT section in the Linux manual page ld.so(8):
LD_PRELOADA list of additional, user-specified, ELF shared objects to be loaded before all others. This feature can be used to selectively override functions in other shared objects. [...]
- No need to re-link
- Works for all functions
- Works only on dynamically linked executables
- Intercepts all calls (including calls inside libraries etc.)
Example (preload.c):
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
void *malloc(size_t size) {
// before call to malloc
void *(*_malloc)(size_t);
if ((_malloc = dlsym(RTLD_NEXT, "malloc")) == NULL) {
errno = ENOSYS;
return NULL;
}
void *ret = _malloc(size);
// after call to malloc
return ret;
}
# ./main is already compiled and ready
gcc -shared -fPIC -o preload.so preload.c
LD_PRELOAD="$(pwd)/preload.so" ./main
Option 2: Wrapper functions (gcc -Wl,--wrap=, ld --wrap=)
From the OPTIONS section in the Linux manual page ld(1):
--wrap=symbolUse a wrapper function for symbol. Any undefined reference to symbol will be resolved to
__wrap_<symbol>. Any undefined reference to__real_<symbol>will be resolved to symbol.This can be used to provide a wrapper for a system function. The wrapper function should be called
__wrap_<symbol>. If it wishes to call the system function, it should call__real_<symbol>. [...]
From the OPTIONS section in the Linux manual page gcc(1):
-Wl,optionPass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example,
-Wl,-Map,output.mappasses-Map output.mapto the linker. When using the GNU linker, you can also get the same effect with-Wl,-Map=output.map.
- Need to re-link(/-comiple)
- Relatively simple code:
- Function name:
__wrap_<symbol> - Call to real function inside wrapper:
__real_<symbol>
- Function name:
- Works for all functions
- Works only on dynamically linked executables
- Intercepts only calls inside the given source file
Example (wrap.c):
extern void *__real_malloc(size_t size);
void *__wrap_malloc(size_t size) {
// before call to malloc
void *ret = __real_malloc(size);
// after call to malloc
return ret;
}
gcc -o main_wrapped main.c wrap.c -Wl,--wrap=malloc
./main_wrapped
Option 3: Kernel module
- Only works with Linux system calls
- Also works with statically linked executables
- Only possible with some "hacks" inside the kernel module (to access the Syscall Table)
- litux.nl?
Option 4: Emulating
- Valgrind, GDB
Option 5: Modifying the kernel
- Add a special Syscall to intercept/modify other Syscalls