The system call intercepting library
This project will no longer be maintained by Intel.
Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project.
Intel no longer accepts patches to this project.
If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the open source software community, please create your own fork of this project.
Userspace syscall intercepting library.
The travis builds use some scripts to generate a docker images, in which syscall_intercept is built/tested. These docker images are pushed to Dockerhub, to be reused in later travis builds. The scripts expect four environment variables to be set in the travis environment:
Building libsyscall_intercept requires cmake. Example:
cmake path_to_syscall_intercept -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang
makealternatively:
ccmake path_to_syscall_intercept
makeThere is an install target. For now, all it does, is cp.
make installComing soon:
make test#include cc -lsyscall_intercept -fpic -shared source.c -o preloadlib.so
LD_PRELOAD=preloadlib.so ./applicationThe system call intercepting library provides a low-level interface for hooking Linux system calls in user space. This is achieved by hotpatching the machine code of the standard C library in the memory of a process. The user of this library can provide the functionality of almost any syscall in user space, using the very simple API specified in the libsyscall_intercept_hook_point.h header file:
int (*intercept_hook_point)(long syscall_number,
long arg0, long arg1,
long arg2, long arg3,
long arg4, long arg5,
long *result);The user of the library shall assign to the variable called intercept_hook_point a pointer to the address of a callback function. A non-zero return value returned by the callback function is used to signal to the intercepting library that the specific system call was ignored by the user and the original syscall should be executed. A zero return value signals that the user takes over the system call. In this case, the result of the system call (the value stored in the RAX register after the system call) can be set via the *result pointer. In order to use the library, the intercepting code is expected to be loaded using the LD_PRELOAD feature provided by the system loader.
All syscalls issued by libc are intercepted. Syscalls made by code outside libc are not intercepted. In order to be able to issue syscalls that are not intercepted, a convenience function is provided by the library:
long syscall_no_intercept(long syscall_number, ...);Three environment variables control the operation of the library:
INTERCEPT_LOG -- when set, the library logs each syscall intercepted to a file. If it ends with "-" the path of the file is formed by appending a process id to the value provided in the environment variable. E.g.: initializing the library in a process with pid 123 when the INTERCEPT_LOG is set to "intercept.log-" will result in a log file named intercept.log-123.
*INTERCEPT_LOG_TRUNC -- when set to 0, the log file from INTERCEPT_LOG is not truncated.
INTERCEPT_HOOK_CMDLINE_FILTER -- when set, the library checks the command line used to start the program. Hotpatching, and syscall intercepting is only done, if the last component of the command used to start the program is the same as the string provided in the environment variable. This can also be queried by the user of the library:
int syscall_hook_in_process_allowed(void);…$ cc example.c -lsyscall_intercept -fpic -shared -o example.so
$ LD_LIBRARY_PATH=. LD_PRELOAD=example.so ls
ls: reading directory '.': Operation not supportedIn order to handle syscalls in user space, the library relies on the following assumptions:
The library disassembles the text segment of the libc loaded into the memory space of the process it is initialized in. It locates all syscall instructions, and replaces each of them with a jump to a unique address. Since the syscall instruction of the x86_64 ISA occupies only two bytes, the method involves locating other bytes close to the syscall suitable for overwriting. The destination of the jump (unique for each syscall) is a small routine, which accomplishes the following tasks:
Simple hotpatching: Replace a mov and a syscall instruction with a jmp instruction
…Hotpatching using a trampoline jump: Replace a syscall instruction with a short jmp instruction, the destination of which is a regular jmp instruction. The reason to use this, is that a short jmp instruction consumes only two bytes, thus fits in the place of a syscall instruction. Sometimes the instructions directly preceding or following the syscall instruction can not be overwritten, leaving only the two bytes of the syscall instruction for patching. The hotpatching library looks for place for the trampoline jump in the padding found to the end of each routine. Since the start of all routines is aligned to 16 bytes, often there is a padding space between the end of a symbol, and the start of the next symbol. In the example below, this padding is filled with 7 byte long nop instruction (so the next symbol can start at the address 3f410).
…Besides logging, the most important factor during debugging is to make sure the syscalls in the debugger are not intercepted. To achieve this, use the INTERCEPT_HOOK_CMDLINE_FILTER variable described above.
INTERCEPT_HOOK_CMDLINE_FILTER=ls \
LD_PRELOAD=libsyscall_intercept.so \
gdb lsWith this filtering, the intercepting library is not activated in the gdb process itself.
No open issues yet, or sync has not completed.