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).

Figure 3: Thanks to libfaketime, the clock on the right is already 2.5 hours later than the clock on the left.

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.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • strace

    Get started with strace by examining a pair of "Hello World" programs. Next month, in the second part of this two-part series, I'll take a deeper look at strace output.

  • Practical strace

    After "Hello World," you really need to look at system calls in more detail. In this second of two articles, we'll look at debugging in the real world.

  • Perl: Ptrace

    Linux lets users watch the kernel at work with a little help from Ptrace, a tool that both debuggers and malicious process kidnappers use. A CPAN module introduces this technology to Perl and, if this is not enough, C extensions add functionality.

  • MITRE ATT&CK Workshop

    The MITRE ATT&CK website keeps information on attackers and intrusion techniques. We'll show you how to use that information to look for evidence of an attack.

  • Traffic shaping with Trickle

    Is your Internet connection groaning under the load of too many simultaneous downloads? If so, try Trickle, a simple application that gives you more granular control over network traffic.

comments powered by Disqus
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.

Learn More

News