LD PRELOAD notes

From Noah.org
Jump to: navigation, search

LD_PRELOAD comes in handy when hacking some program to force it to do what you want. You can hack any shared library call to lobotomize or bypass it by using LD_PRELOAD to replace a library function with your own.

See also

ptrace_notes is an alternative to LD_PRELOAD which allows even finer grained manipulation of running program. It will also allow modification of of code control inside of static areas of a program. LD_PRELOAD allows only hooks into functions in shared object libraries.

Snoopy logger

The Snoopy Logger is a tool that will log every exec system call (and its many siblings). It can also be setup just to log a single command. For example this will log a bash session LD_PRELOAD=/lib/snoopy.so bash. This would log every command entered into bash, except for the hell built-in commands. The log output usually ends up in /var/log/auth.log or /var/log/syslog depending on how your syslog is configured. You can also log the entire system this way doing the following echo "/lib/snoopy.so" > /etc/ld.so.preload... Note that even after you remove snoopy.so from /etc/ls.so.preload you may still see data logged to this file. The because there must be a process that was started before snoopy.so was disabled, so any process like this will continue to use the snoopy library for as long as the process runs. Don't forget that echo is a shell built-in and will not be logged (and command echo does not work for some reason; although, this technique works for other shell build-in commands.

Jordan Sissel offers liboverride, which is a small toolset that lets you quickly inject code into another program.

Example

An example of hooking into a library call using LD_PRELOAD:

/* This example shows how to hook into library calls using LD_PRELOAD.
 * This example hooks into the malloc() call. This is not a "serious"
 * example. See the end of this comment to see why.
 *
 * To build this example run the following:
 *      gcc -ldl -Wall -shared -fPIC -o malloc_hook.so -D_GNU_SOURCE malloc_hook.c
 * To test this example run the following:
 *      LD_PRELOAD=./malloc_hook.so ls -l /proc
 * Note that we need -D_GNU_SOURCE to make this work. This enables
 * some non-standard GNU extensions. Without this we would get
 * this compilation error:
 *     malloc_hook.c:51: error: 'RTLD_NEXT' undeclared (first use in this function)
 *
 * Note that when you hook into a library call you usually want to
 * eventually hand off the work to the original call.
 * Beware because you can't simply call the original library call
 * because your hook is running under the LD_PRELOAD modification.
 * This is what <dlfcn.h> is for. It provides the dlsym() call which
 * is used to find the address of the function. Then we call the
 * function directly by its address. The RTLD_NEXT handle used in 
 * dlsym() is specifically to allow you to create wrappers around
 * functions in another shared library.
 *
 * The next thing to notice is that hooking into malloc this way
 * is a very BAD IDEA! Notice the calls to printf() and dlsym().
 * What would have happened if these calls had tried to call
 * malloc() themselves? You would have ended up with an
 * infinite loop. We just got lucky here. It turns out that
 * Linux provides hooks for you to get around problems like this.
 * In this case we should have looked at using __malloc_hook().
 *
 * Copyright (c) 2010, Noah Spurrier <noah@noah.org>
 */

#include <dlfcn.h>
#include <stdio.h>

void *malloc(size_t size)
{
    static void * (*malloc_original)(size_t) = NULL;
    void * memory_pointer;

    /* This result of dlsym is preserved to speed up access next time.
     * This isn't a thread-safe practice, but it shouldn't matter here
     * since a library can have only one pointer to a function.
     * If it became a problem I would have removed the 'if' statement
     * and the 'static' declaration. It isn't much slower by calling
     * dlsym() every time.
     */
    if (malloc_original == NULL)
        malloc_original = (void *(*)()) dlsym(RTLD_NEXT, "malloc");

    memory_pointer = malloc_original(size);
    printf("malloc(%d) returns pointer %p\n", size, memory_pointer);     
    return memory_pointer;
}