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!が表示)。