LD_PRELOADで関数フックしてみよう!
「たのしいバイナリの歩き方」の4章でWindows環境におけるAPIフックについて解説しました。今回はLinux環境についても解説しましょう。
Linux環境(Ubuntu 12.04.2)ではLD_PRELOADという環境変数を使うことで似たようなことが可能です。LD_PRELOADでググってみるとサンプルコードがいくつも見つかります。
試しにwriteをフックする例を示しましょう。
// test.c
// $ gcc test -o test.c
#include <string.h>
#include <unistd.h>
int main()
{
char s[] = "Hello World!\n";
write(1, s, strlen(s));
return 0;
}$ gcc test.c -o test
$ ldd test
linux-gate.so.1 => (0xb7771000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c1000)
/lib/ld-linux.so.2 (0xb7772000)// hook.c
// $ gcc -fPIC -shared -o hook.so hook.c -ldl
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
int g_first = 0;
void *g_h;
ssize_t (*g_f)(int, const void *, size_t);
ssize_t write(int fd, const void *buf, size_t count)
{
if(g_first == 0){
g_first = 1;
g_h = dlopen("libc.so.6", RTLD_LAZY);
g_f = dlsym(g_h, "write");
}
printf("call write\n");
return (*g_f)(fd, buf, count);
}$ gcc -fPIC -shared -o hook.so hook.c -ldl $ LD_PRELOAD=./hook.so ./test call write Hello World!
LD_PRELOADに設定した.soファイルが持つ同名関数は優先して実行されます。なのでlibc.so.6が持つwriteを無視してhook.soのwriteが実行されました(結果call writeが表示)。そのhook.soのwrite内であらためてlibc.so.6のwriteを呼び出せばフック完了です(通常どおりHello World!が表示)。