PIEバイナリとLD_PRELOAD
PIE (位置独立実行形式) を作成する - bkブログ
この辺読んでたらPIEはPLT経由の呼出じゃ無い為LD_PRELOADで上書き不可
と書いてあったので、どうなってるのだろうと思い実験してみる。
対象コード
$ cat test.c int main(int argc, char ** argv){ printf("a\n"); hoge(); return 0; } int hoge(){ putchar('b'); putchar('\n'); return 0; }
対象ライブラリコード
$ cat test_ld.c int printf(const char * format, ...){ putchar('A'); putchar('\n'); return 0; } int hoge(){ putchar('B'); putchar('\n'); return 0; }
それぞれコンパイル
# デフォ $ gcc -c test.c && gcc -o exe_default test.o # PIE付き $ gcc -c -fPIE test.c && gcc -o exe_pie -pie test.o # ライブラリ $ gcc -c -fPIC test_ld.c && gcc -shared -o test_ld.so test_ld.o
実行
$ ./exe_default a b $ ./exe_pie a b $ LD_PRELOAD=./test_ld.so ./exe_default A b $ LD_PRELOAD=./test_ld.so ./exe_pie A b
実行バイナリ自体が持っている関数hogeが乗っ取れないのはbindingの対象に
ならないので良いとして、何故かPIEの場合でも関数乗っ取りが効いている。
私が読み違えていて、実は共有ライブラリ内で呼んでいる関数の事なのかと思い
ライブラリ自体をPIC/PIEで作ってみたけど、共にPLT経由で呼び出している。
$ gcc -c -fPIE test_ld.c && gcc -shared -pie -o test_link.so.pie test_ld.o $ gcc -c -fPIC test_ld.c && gcc -shared -o test_link.so.pic test_ld.o $ readelf -s test_link.so.pic | grep putchar 11: 00000000 266 FUNC GLOBAL DEFAULT UND putchar@GLIBC_2.0 (2) 43: 00000000 266 FUNC GLOBAL DEFAULT UND putchar@@GLIBC_2.0 $ readelf -s test_link.so.pie | grep putchar 11: 00000000 266 FUNC GLOBAL DEFAULT UND putchar@GLIBC_2.0 (2) 44: 00000000 266 FUNC GLOBAL DEFAULT UND putchar@@GLIBC_2.0 $ objdump -d test_link.so.pic | grep putchar | grep call 50c: e8 07 ff ff ff call 418 <putchar@plt> 519: e8 fa fe ff ff call 418 <putchar@plt> $ objdump -d test_link.so.pie | grep putchar | grep call 4d8: e8 07 ff ff ff call 3e4 <putchar@plt> 4e5: e8 fa fe ff ff call 3e4 <putchar@plt>
dlopen,dlsymしてる場合は効かないけど、これは検索対象がそのときにopenした物
だけなのでそもそもLD_PRELOADの対象外のはず。LD_DEBUGでトレースしてみても
該当ライブラリのみしかopenしていなかった。
PLT未使用でPosition independentかつLD_PRELOADでシンボル置き換え不可と
いうのは実装的に気になる所なんだけど、再現させられなかったので謎のまま。