LD_PRELOADで動的ライブラリ関数を上書きする

動的リンクされたプログラムでは、同じ関数が複数のライブラリで定義されている場合、最初に見つかった関数が利用される。

環境変数 LD_PRELOAD で指定した共有ライブラリは最優先で読み込まれるため、簡単にプログラムの挙動を変えることができる。

実験用のプログラム

まずは乱数を10個表示するだけの簡単なプログラム(random_num.c)を用意。

/* random_num.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
  srand(time(NULL));
  int i = 10;
  while(i--) printf("%d\n",rand());
  return 0;
}

実験用プログラムの実行

コンパイルする

$ gcc random_num.c -o random_num
$ ldd random_num
	linux-vdso.so.1 =>  (0x00007fffbddff000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f89557e8000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8955b84000)

$ nm -D /lib64/libc.so.6  | grep rand$
00000000000369d0 T rand
0000000000036490 W srand

GNU C Library(libc.so.6) とリンクされており、 実験用で利用する関数 rand もある。nm でシンボルを出力すると、 rand を確認できる。

実際に実行するとランダムに数字が10個表示される。

$ ./random_num
849453208
1166619329
1528092543
1204148216
947393683
1613042737
1981359507
1489207667
381293129
346436639

rand を上書きする

次に、本来はランダムな整数を返す rand 関数がつねに固定の整数 42 を返すように書き換え他プログラムを用意(unrandom.c)。

/* unrandom.c */
int rand(){
    return 42; //the most random number in the universe
}

共有ライブラリとしてコンパイルする

$ gcc -shared -fPIC unrandom.c -o unrandom.so
$ nm -D ./unrandom.so
                 w _Jv_RegisterClasses
00000000002007c8 A __bss_start
                 w __cxa_finalize
                 w __gmon_start__
00000000002007c8 A _edata
00000000002007d8 A _end
0000000000000578 T _fini
0000000000000420 T _init
000000000000052c T rand

LD_PRELOAD でプログラムの実行

環境変数 LD_PRELOAD で作成した共有ライブラリ を指定し、同じプログラムを実行してみる。

$ LD_PRELOAD=./unrandom.so ldd random_num
	linux-vdso.so.1 =>  (0x00007fff54f86000)
	./unrandom.so (0x00007f841064b000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f84102af000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f841084d000)
$ LD_PRELOAD=./unrandom.so ./random_num
42
42
42
42
42
42
42
42
42
42

rand 関数が固定値を返す自作ライブラリの関数に書き換わっているため、プログラムの実行結果もランダム性がなくなっているのがわかる。

rand を上書きする(元の関数も実行)

先ほどの例では関数の中身を完全に書き換えていたので、もう少しソフトに、本来の関数を実行しつつその前後で追加処理を入れたのが次のプログラム(myrandom.c)。

/* myrandom.c */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

typedef int (*orig_rand_f_type)();

int rand(){
  printf("custom rand is called\n");
  orig_rand_f_type orig_rand;
  orig_rand = (orig_rand_f_type)dlsym(RTLD_NEXT, "rand");
  return orig_rand();
}

dlsym(RTLD_NEXT, "rand") でデフォルト(RTLD_DEFAULT)の次に見つかる rand 関数を探し出し、実行している。
メッセージを出力している 箇所(printf("custom rand is called\n");)が追加処理。

同じようにコンパイルして実行してみる。

$ gcc -shared -fPIC myrandom.c -o myrandom.so -ldl
$ LD_PRELOAD=./myrandom.so ./random_num
custom rand is called
1096863217
custom rand is called
505523853
custom rand is called
1514541433
custom rand is called
474180237
custom rand is called
1994792782
custom rand is called
288074905
custom rand is called
1366621408
custom rand is called
1929084141
custom rand is called
1667463609
custom rand is called
1873885528

再びランダムな整数が表示されるようになり、また、追加したデバッグメッセージも表示されている。

References

Advertisements
Tagged with: , ,
Posted in linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
%d bloggers like this: