LD_PRELOAD を試したら、Segv した。
はじめに
本記事は、Binary Hackの第3章#22に出てくる【LD_PRELOAD】を簡単に試した結果、Segvしたので、その原因追求について、記述したものである。
作成したソースはGithubに置いておく。
https://github.com/tomohikoseven/LD_PRELOAD
LD_PRELOADとは
バイナリを実行したときに、他のライブラリの読み込み前に、優先的に指定したライブラリを読み込むことができる環境変数である。
Segv 事象とソース
下記のコマンドを実行したら、Segvした。
andre@andre-VirtualBox:~/work/LD_PRELOAD$ LD_PRELOAD=./libhack.so ./test_main Segmentation fault
test_main( test_main.c ) と libhack.so( hack.c )を下記に示す。
andre@andre-VirtualBox:~/work/LD_PRELOAD$ cat main.c #includeint main() { puts("hello world!"); return 0; }
andre@andre-VirtualBox:~/work/LD_PRELOAD$ cat hack.c #includeint puts( const char *str ) { printf("hack!!\n"); }
原因:GCC の最適化
30分くらい原因を探っていたが、答えは、Binary Hack 第3章 #22(LD_PRELOADの前の段落!)に書いてあった。
すなわち、printfは、putsに置き換えることがあると。
これにより、hack.c内のprintfがputsに置き換わって、libhack.so内でputsが再帰的に呼ばれ、スタックオーバーフローでSegvしていた。
スタックオーバーフローの確認
本当にスタックオーバーフローしているのか、検証した。
検証ソースは以下。
andre@andre-VirtualBox:~/work/LD_PRELOAD$ cat main.c_altstack #include#include #include #include #define ALT_STACK_SIZE (64*1024) static sigjmp_buf return_point; static void signal_handler( int sig ) { if( sig == SIGSEGV ) { siglongjmp( return_point, 1 ); } } static void register_sigaltstack() { stack_t newSS; stack_t oldSS; newSS.ss_sp = malloc( ALT_STACK_SIZE ); newSS.ss_size = ALT_STACK_SIZE; newSS.ss_flags = 0; sigaltstack( &newSS, &oldSS ); } int main() { struct sigaction newAct; struct sigaction oldAct; // 代替スタック作成 register_sigaltstack(); // シグナル集合の初期化(空) sigemptyset( &newAct.sa_mask ); // シグナル集合の設定 sigaddset( &newAct.sa_mask, SIGSEGV ); // ハンドラ設定 newAct.sa_handler = signal_handler; // シグナル発生時のスタックを代替スタックに設定 newAct.sa_flags = SA_ONSTACK; // SEGV 時の動作登録 sigaction( SIGSEGV, &newAct, &oldAct ); if( sigsetjmp( return_point, 1 ) == 0 ) { puts("hello world!"); } else { fprintf( stderr, "stack overflow error\n" ); } return 0; }
このソースをmain.cにリネームし、make そして実行すると、[ stack overflow error ]とメッセージが表示され、確認ができた。
andre@andre-VirtualBox:~/work/LD_PRELOAD$ LD_PRELOAD=./libhack.so ./test_main stack overflow error
今回使用したGCCのバージョンを下記に示す。
andre@andre-VirtualBox:~/work/LD_PRELOAD$ gcc --version gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 Copyright (C) 2011 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.