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.
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 :)
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
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)
Your compiled code looks like this:
function_prototype {
real_func = real_function_being_overridden;
<... your code ..>
return real_func(args...);
}
Download liboverride
Looking for older releases? Try the
liboverride releases archive