Search this site


Metadata

Articles

Projects

Presentations

liboverride - simpler function injection

What is liboverride?

liboverride is a small toolset which (hopefully) lets you quickly write code you can inject to another program.

It uses the run-time linker and LD_PRELOAD to inject functions into your programs.

Requirements

  • gnu make
  • m4

How do I use it?

Let's use an example.

You have a server program that you want to bind to a specific IP, but this program doesn't have any simple way of saying "bind to 127.0.0.1". You could inject your own version of socket() or listen() to make sure that it was bound to the correct IP. To hack this with liboverride, you would use this code:
override(`bind', `
  {
    inet_aton("127.0.0.1", &(((struct sockaddr_in*)addr)->sin_addr));
  }
') 
Server's must call bind() to start listening on a specific port. So, we override bind() with our own, which sets the sin_addr to 127.0.0.1, making our server listen only on that IP. The original function 'bind' is called after your injected code, and uses the modified sockaddr.

Let's show this working with netcat:
% nc -l 8888 &
% sockstat -4 | grep 8888
jls      nc         36250 3  tcp4   *:8888                *:*

# Now run with our function override:
% gmake bind.so
% LD_PRELOAD=./bind.so nc -l 8888 &
% sockstat -4 | grep 8888
jls      nc         36253 3  tcp4   127.0.0.1:8888        *:*
Notice how nc is now listening on 127.0.0.1:8888. Pretty simple :)

Adding new functions to liboverride.

The release of liboverride only includes a small set of functions it knows how to override. You can add your own! All you need to know is:
  • The function prototype
  • The function's include requirements
  • The library providing this function

Example: add override for read()

The manpage for read() says the prototype looks like this:
ssize_t read(int d, void *buf, size_t nbytes)
Add this line to 'funcdefs' in liboverride:
 define(`read_prototype', `ssize_t read(int d, void *buf, size_t nbytes)') 
The manpage also says which includes are needed for read(2), so let's add that to funcdefs too:
 define(`read_includes', `
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
')
Lastly, we'll need to tell liboverride what library provides read(2). In this case, it is libc:
define(`read_library', '/lib/libc.so.6')
Now that we have added the ability to override read(2), we can write an override for this function. In 'testing.over' put:
override(`read', `
  {
    fprintf(stderr, "read(%d, 0x%tx, %zd)\n", d, buf, nbytes);
  }
')
Now build it (with gnu make): make testing.so

Assuming everything was done correctly, you should have a 'testing.so' file. Let's test it with cat(1):
    LD_PRELOAD=./testing.so cat /etc/motd > /dev/null
    read(3, 0x504000, 4096)
    read(3, 0x504000, 4096)

If we wanted to something more advanced, such as providing strace or truss output for the read(2) function, we would use this:
#include <stdio.h>
override(`read', `
  {
    ssize_t ret;
    fprintf(stderr, "read(%d, 0x%tx, %zd) = ", d, buf, nbytes);
    ret = real_func(d, buf, nbytes);
    fprintf(stderr, "(%zd bytes) ", ret);
    if (ret > 0)
      fprintf(stderr, "\"%.*s%s\"", 30, buf, (ret > 30 ? "..." : ""));
    fprintf(stderr, "\n");
    return ret;
  }
')
Rebuild testing.so, and run cat again:
% LD_PRELOAD=./testing.so cat /etc/motd > /dev/null
read(3, 0x504000, 4096) = (1110 bytes) "FreeBSD 6.2-RELEASE (SMP) #0: ..."
read(3, 0x504000, 4096) = (0 bytes)

Implementation details

Your compiled code looks like this:
function_prototype {
  real_func = real_function_being_overridden;
  <... your code ..>
  return real_func(args...);
}

Download

Download liboverride

Looking for older releases? Try the liboverride releases archive