TL;DR
LD_PRELOADで特定関数の差し替えをおこない、かつ場合によっては元の関数呼び出しをできるようにする。
dlsym
に RTLD_NEXT
を指定して元の関数の関数ポインタを取得し、それを実行すれば良い。
実行方法まとめ
まずは差し替え対象の関数を定義する。今回は puts
を対象とする。
#include <stdio.h>
int main(int argc, char const* argv[])
{
puts("hoge");
return 0;
}
$ gcc print.c
$ ./a.out
hoge
LD_PRELOADで差し替えるための関数を定義する。内部で元の関数を呼び出し、その前後でログを出力する。
そのまま puts
を呼ぶと無限再帰呼び出しになってスタックオーバーフローするので、dlsym(RTLD_NEXT, "puts")
を使用して、元の関数のの関数ポインタを取得して、そちらを呼び出すようにしている。
(RTLD_NEXT
は、ライブラリ検索順序の中で現在のライブラリ以降で最初に 関数が現れるところを探す。)
#include <stdio.h>
#include <assert.h>
#define __USE_GNU
#include <dlfcn.h>
int puts(const char *__s) {
fprintf(stdout, "before\n");
void *handle = dlsym(RTLD_NEXT, "puts");
assert(handle);
int (*func)(const char *) = (int (*)(const char*))handle;
int ret = func(__s);
fprintf(stdout, "after\n");
}
$ gcc -shared -fPIC override.c -ldl -o liboverride.so
$ LD_PRELOAD=./liboverride.so ./a.out
before
hoge
after
これで対象のバイナリを再ビルドせずにputsの呼び出し前後にログを入れることができた。
関連
LD_PRELOADでprintfを後から差し替える - Qiita
参考書籍
『BINARY HACKS』 #HACK 61 : LD_PRELOADで既存の関数をラップする
参考
Man page of DLOPEN
LD_PRELOADを使ったテスト(C言語編)
[Debug] LD_PRELOAD, dlsym, GCC拡張機能によって共有ライブラリの関数の呼出し前後で任意の処理を実行する - th0x4c 備忘録