Skip to main content

C/C++ Function Hooking with LD_PRELOAD

Sometimes you would like to hook the glibc functions with your own implementations so that you can hack the output or monitor some metrics. Here's a simple guideline on how to do it with LD_PRELOAD on linux.

The following demos are written in C but it can apply to C++ as well.

Here you wrote a simple program that generates random numbers:

// file: main.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
  srand(time(NULL));
  for (int i = 0; i < 10; ++i) {
    printf("%02d,", rand() % 100);
  }
  printf("\n");

  return 0;
}

You find that the random function rand() generates random numbers but that's not good when you're teasing your friends. So ... let's hook the time() and rand() and let it always return 42.

You would need to first write the implementations yourself:

// file: hook.c
#include <time.h>
// hooking the time() function can help you use the same random seed
time_t time(time_t *arg) { return 42; }
// hooking the rand() function will always generate the same random number
int rand() { return 42; }

Now build your functions and let it be prioritized before glibc.

gcc -o main main.c
gcc -shared -fPIC -o libhook.so hook.c

Running the program will emit the following result:

LD_PRELOAD=$(PWD)/libhook.so ./main
42,42,42,42,42,42,42,42,42,42,

 

The mechanism behind is that for main program, it'll need to find the rand(), time(), srand(), printf, etc. symbols so the functions can be executed. You can tell with the following command that it links to system libc to find these functions.

$ ldd main
      linux-vdso.so.1 (0x00007fffa7bbd000)
      libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faf216f3000) # <- this is the system libc
      /lib64/ld-linux-x86-64.so.2 (0x00007faf21916000)

With LD_PRELOAD environment variable, you can load the libhook.so before libc.so.6 and when trying to find symbols on execution, it'll find the symbols in libhook.so.

 

Sometimes you would like to use the original function when writing the hook. It is doable as well. We just need to find the symbol in the next dynamic libraries with dlsym().

// file: hook.c
// _GNU_SOURCE macro is required to use RTLD_NEXT to find the next symbol in search list
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>

typedef int (*libc_vprintf_t)(const char *format, va_list args);

int libc_vprintf(const char *format, va_list args)
{
  // try to find the vprintf() function in system libc
  return ((libc_vprintf_t)dlsym(RTLD_NEXT, "vprintf"))(format, args);
}

int printf(const char *format, ...)
{
  // Trying to add the "|" fence between each printf format
  char modified_format[128];
  snprintf(modified_format, 128, "|%s|", format);

  va_list args;
  va_start(args, format);
  return libc_vprintf(modified_format, args);
}

After adding these parts and combined with the hooks, we may compile with the following command:

gcc -shared -fPIC -o libhook.so hook.c -ldl

and the final result will become:

|42,||42,||42,||42,||42,||42,||42,||42,||42,||42,|

 

 

Comments

Comments powered by Disqus