LD_の解決PRELAADがprintfをキャプチャできない問題


前の博文Linuxの下の入門レベルのエクスポート関数のキャプチャ-LDを使用PRELAAD環境変数ではLD_PRELAADのメソッドはダイナミックライブラリの関数をキャプチャし、printfをキャプチャできないと聞かれたので、この質問に答えましょう.
まず、彼が書いたブロック用のコードとテストコードを見てみましょう.
    
#include <unistd.h>

extern void printf(const char *format,...);
void printf(const char *format,...)
{
	write(0,"abc",4);
	return;
}

       
gcc -g3 -O0 -shared -fPIC -o fake.so fake.c
    
#include <stdio.h>
#include <unistd.h>

int main()
{
	printf("123
"); while(1) sleep(1); return 0; } gcc -g3 -O0 -o test test.c
結果プログラム出力:
[root@localhost Desktop]# export LD_PRELOAD=/root/Desktop/fake.so 
[root@localhost Desktop]# ./test 
123
はやはり人の意を尽くさず、止めなかったようだ.
このような状況が発生すると、以下のような状況が推測されます.
1).動的ライブラリでエクスポートされた関数名と呼び出された関数名が一致しない場合、g++コンパイルで生成された動的ライブラリなどで発生する可能性があります.しかし、ここではそうではありません.動的ライブラリからエクスポートされた関数名をnmで確認します.
[root@localhost Desktop]# nm -Do fake.so
fake.so:                 w _Jv_RegisterClasses
fake.so:00000000002008a0 A __bss_start
fake.so:                 w __cxa_finalize
fake.so:                 w __gmon_start__
fake.so:00000000002008a0 A _edata
fake.so:00000000002008b0 A _end
fake.so:0000000000000648 T _fini
fake.so:0000000000000460 T _init
fake.so:000000000000057c T printf
fake.so:                 U write
はい、大丈夫そうです.他の状況かどうか見てみましょう.
2).ダイナミックライブラリはプロセススペースに注入されませんでした.linuxでは、/procファイルシステムを使用して、プロセスがロードしたダイナミックライブラリを次のように表示できます.
[root@localhost ~]# ps x|grep test
 3879 pts/12   S+     0:00 ./test
 3895 pts/13   S+     0:00 grep test
[root@localhost ~]# cat /proc/3879/maps
00400000-00401000 r-xp 00000000 fd:00 132597                             /root/Desktop/test
00600000-00601000 rw-p 00000000 fd:00 132597                             /root/Desktop/test
3326400000-3326420000 r-xp 00000000 fd:00 75830                          /lib64/ld-2.12.so
332661f000-3326620000 r--p 0001f000 fd:00 75830                          /lib64/ld-2.12.so
3326620000-3326621000 rw-p 00020000 fd:00 75830                          /lib64/ld-2.12.so
3326621000-3326622000 rw-p 00000000 00:00 0 
3326c00000-3326d8b000 r-xp 00000000 fd:00 75831                          /lib64/libc-2.12.so
3326d8b000-3326f8a000 ---p 0018b000 fd:00 75831                          /lib64/libc-2.12.so
3326f8a000-3326f8e000 r--p 0018a000 fd:00 75831                          /lib64/libc-2.12.so
3326f8e000-3326f8f000 rw-p 0018e000 fd:00 75831                          /lib64/libc-2.12.so
3326f8f000-3326f94000 rw-p 00000000 00:00 0 
7f0465071000-7f0465074000 rw-p 00000000 00:00 0 
7f0465081000-7f0465082000 rw-p 00000000 00:00 0 
7f0465082000-7f0465083000 r-xp 00000000 fd:00 131339                     /root/Desktop/fake.so
7f0465083000-7f0465282000 ---p 00001000 fd:00 131339                     /root/Desktop/fake.so
7f0465282000-7f0465283000 rw-p 00000000 fd:00 131339                     /root/Desktop/fake.so
7f0465283000-7f0465284000 rw-p 00000000 00:00 0 
7fff2c239000-7fff2c24e000 rw-p 00000000 00:00 0                          [stack]
7fff2c34b000-7fff2c34c000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
ここからfakeが見えます.soはターゲットプロセスのアドレス空間において,言い換えれば注入に成功した.それはどんな状況ですか.
3).ターゲットプロセスは、ターゲットインタフェース、すなわちprintfを呼び出さなかった.testのシンボル情報を見てみましょう.
root@localhost Desktop]# nm -Do test
test:                 w __gmon_start__
test:                 U __libc_start_main
test:                 U puts
test:                 U sleep
testはprintfを呼び出していませんが、出力結果からtestがputsを呼び出したのはどんな状況ですか?
実は、これはgccがやったことで、printfの代わりに内蔵関数putsを使っています.これは最適化です.どうやってこの問題を解決しますか?
2つの解決策があります.
1)gccが組み込み関数を使用することを禁止し、またはgcc接続指定関数を指定する方法は以下の通りである.
gcc -g3 -O0 -o nbi_test nbi_test.c -fno-builtin-printf
このように生成されたnbi_testのprintfはputsに置き換えられないため、正しい実行結果が得られます.
[root@localhost Desktop]# nm -Do nbi_test
nbi_test:                 w __gmon_start__
nbi_test:                 U __libc_start_main
nbi_test:                 U printf
nbi_test:                 U sleep
[root@localhost Desktop]# export LD_PRELOAD=/root/Desktop/fake.so 
[root@localhost Desktop]# ./nbi_test 
abc
)putsインタフェースのキャプチャ(未検証)