Modify program behavior with LD_PRELOAD
Customizing Program Behavior
A typical scenario for using LD_PRELOAD
would be to change undesirable behavior in one of your applications, where the application's source code is too complex to search for the right location or is simply not available. As an alternative, you can use strace
[2] or ltrace
[3] (see the "strace and ltrace" box) to check the order in which the application tries to open files or network connections.
strace and ltrace
The strace
and ltrace
command-line tools from the strace and ltrace packages (which are preinstalled on most systems) let you monitor a process as it executes, logging specific events. The strace
tool takes care of system calls such as requests to the Linux kernel to perform a kernel task for the process (opening files, reading from them, closing files).
To log all system calls of a program prog
, you would need to launch it with
strace -o prog.log prog
and any other call parameters that prog
needs. The log is written to prog.log
. However, the list created in this way turns out to be very confusing because a typical program executes a large number of system calls in a short time. It makes more sense to log only certain calls with the -e trace=
option. For example, the call from the first line of Listing 5 would only show the calls to open
, openat
, and close
.
If you omit the -o
switch, the output is displayed on the terminal. However, this is only useful for programs with very little output of their own or for graphical applications, because otherwise the program and strace
's output will be combined.
The ltrace
tool does for library calls what strace
does for system calls. For example, the call from the second line of Listing 5 displays all function calls to open()
, openat()
, and close()
. The information is similar to that of strace
, but this time the focus is on library functions (such as open()
), which run system calls of the same name (open
, without the parentheses).
Listing 5
strace and ltrace
$ strace -o prog.log -e trace=open,openat,close prog $ ltrace -x open+openat+close prog
It may turn out that a program sources information that it can't handle from a global configuration file. However, you do not want to change the global file because other applications access it and need this information.
In this case, it would be useful to point the problematic program to another configuration file, which you would populate with suitable content to let the program run correctly. To do this, you need to manipulate the open()
function so that it responds to any attempt to open a specific file by opening the alternate file. Listing 6 shows how to redirect all attempts to open the /etc/fstab
file in /tmp/fstab.test
.
Listing 6
openother.c
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <string.h> #define SEARCH_PATH "/etc/fstab" #define REPLACE_PATH "/tmp/fstab.test" int (*true_open)(const char *pathname, int flags, va_list mode); int open (const char *pathname, int flags, va_list mode) { true_open = dlsym (RTLD_NEXT, "open"); char *longpath = realpath (pathname, NULL); if (!strncmp (longpath, SEARCH_PATH, PATH_MAX)) pathname = REPLACE_PATH; int fd = true_open (pathname, flags, mode); free (longpath); return fd; }
The new version of open()
first creates an absolute path specification for the specified file name with realpath()
. You can also call open()
with relative paths, which can then look very different depending on the working directory. The change ensures that only a single path is checked. The strncmp()
function then compares the path with a search term (in my example, /etc/fstab
) and replaces it with the name of the alternative file (/tmp/fstab.test
) if there is a match.
After that, it continues as usual with true_open()
opening the file. The call to free()
at the end of Listing 6 is necessary because longpath()
has reserved memory for storing the path specification. You need to release this before leaving the function. Again, the compile process is a one-liner (Listing 7, line 1).
Listing 7
Tests
01 $ gcc openother.c -o openother.so -fPIC -shared -ldl 02 $ echo "This is not /etc/fstab" > /tmp/fstab.test 03 $ LD_PRELOAD=$PWD/openother.so cat /etc/fstab 04 This is not /etc/fstab
Now when you create a file with the call from line 2 and access /etc/fstab
with cat
and the library enabled via LD_PRELOAD
, you will be opening the file created in /tmp
instead (line 4).
Prohibiting Access
Instead of redirecting file access, the solution to the problem could be to completely prevent access. To do this, you would abort without calling the original function in certain cases, set the errno
global error variable, and return an exit code of -1
.
Listing 8 shows the code that terminates open()
upon accessing the /etc/fstab
file with an error code of ENOENT
and an exit code of -1
. The attempt to open the file then causes the program to abort as desired (Listing 9).
Listing 8
dontopen.c
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define SEARCH_PATH "/etc/fstab" int (*true_open)(const char *pathname, int flags, va_list mode); int open (const char *pathname, int flags, va_list mode) { true_open = dlsym (RTLD_NEXT, "open"); char *longpath = realpath (pathname, NULL); int check = strncmp (longpath, SEARCH_PATH, PATH_MAX); free (longpath); if (!check) { errno = ENOENT; // file not found return -1; } return true_open (pathname, flags, mode); }
Listing 9
Program Abort
$ LD_PRELOAD=$PWD/dontopen.so cat /etc/fstab cat: /etc/fstab: File or directory not found
ENOENT
stands for "File or directory not found." The definitions for error codes can be found in errno-base.h
and errno.h
in the /usr/include/asm-generic/
folder; you can also select other codes.
If you test access blocking with other programs, you may notice that some editors like Vim, mcedit, or Nano are also blocked, but gedit can open the file. An analysis with strace
shows that gedit uses openat
instead of open()
to open files. Consequently, you will need an alternative implementation for gedit.
More Examples
You can find more potential uses on GitHub [4]. For example, libfaketime
lets you fool processes into believing that the system time is different and, in particular, that the date is different. This proves useful if you want to start a program whose usage license has expired. The change only applies to processes that are started by libfaketime
. The tool uses LD_PRELOAD
to load a library that replaces various functions, including time()
, ftime()
, and gettimeofday()
(Figure 3).
The stderred
library (the name combines stderr
for standard error and the color "red") colors all output on the terminal that a process sends to the standard error channel. This means that error messages can be easily distinguished from other output.
The fsatrace
monitors file access and detects read and write access, moves, deletions, and status queries. However, you can easily achieve the same behavior with strace
.
« Previous 1 2 3 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Red Hat Adds New Deployment Option for Enterprise Linux Platforms
Red Hat has re-imagined enterprise Linux for an AI future with Image Mode.
-
OSJH and LPI Release 2024 Open Source Pros Job Survey Results
See what open source professionals look for in a new role.
-
Proton 9.0-1 Released to Improve Gaming with Steam
The latest release of Proton 9 adds several improvements and fixes an issue that has been problematic for Linux users.
-
So Long Neofetch and Thanks for the Info
Today is a day that every Linux user who enjoys bragging about their system(s) will mourn, as Neofetch has come to an end.
-
Ubuntu 24.04 Comes with a “Flaw"
If you're thinking you might want to upgrade from your current Ubuntu release to the latest, there's something you might want to consider before doing so.
-
Canonical Releases Ubuntu 24.04
After a brief pause because of the XZ vulnerability, Ubuntu 24.04 is now available for install.
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.
-
Canonical Collaborates with Qualcomm on New Venture
This new joint effort is geared toward bringing Ubuntu and Ubuntu Core to Qualcomm-powered devices.