動的ライブラリ関数をhackしよう
背景
動的ライブラリ関数をフックして独自関数を呼び出してっていうのが面白そうなのでやってみた。
動的ライブラリ関数を独自関数に置き換えてhackしてみます。
対象関数はwrite(2)
どうやってやるの?
プログラムは通常同じ名前の関数は定義できないが、
動的リンクされたプログラムでは、同じ関数が複数のライブラリに存在することがあり得る。
その際は、最初に見つかった関数が利用される。
環境変数 LD_PRELOAD で指定した共有ライブラリは最優先で読み込まれるため、簡単にプログラムの挙動を変えることができます。
ちなみに何もせずgccでコンパイルすると、glibc(libc.so.6)が動的にリンクされます
動的ライブラリ?
プログラム実行中の任意の時点で読み込まれるライブラリ。
- 必要と判断されるまで、読み込まれない。
- プラグインやモジュールの実装に役立つ。
- 動的ライブラリの作成は、共有ライブラリと同様の方法で作成する。
詳しくは下記でご確認を
http://archive.linux.or.jp/JF/JFdocs/Program-Library-HOWTO/dl-libraries.html
実行環境/必要なもの
- gccとlddを使ってますのでこれら2点があれば大丈夫です。
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)
$ ldd --version
ldd (GNU libc) 2.12
実践例
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)
$ ldd --version
ldd (GNU libc) 2.12
まずはhackされるプログラム
標準出力に「hello world」のみを出力するだけ。
#include <string.h>
#include <unistd.h>
int main()
{
char s[] = "Hello World!\n";
write(1, s, strlen(s));
return 0;
}
次はライブラリ
mainで呼んでいるwrite(2)はこちらで定義されているwriteが優先されることを確認。
「hello world」の前に「hello hack」を出力する。
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
int g_first = 0;
void *g_h;
ssize_t (*g_f)(int, const void *, size_t);
//子プロセスに引き継がないよう設定
void __attribute__((constructor)) unset_ld_preload() {
unsetenv("LD_PRELOAD");
}
ssize_t write(int fd, const void *buf, size_t count) {
if(g_first == 0){
g_first = 1;
// 動的ライブラリへの内部ハンドルを取得
g_h = dlopen("libc.so.6", RTLD_LAZY);
// シンボルがロードされたメモリのアドレスを取得
g_f = dlsym(g_h, "write");
}
fprintf(stdout, "Hello Hack\n");
return (*g_f)(fd, buf, count);
}
上記で独自のwrite関数を定義しています。
attribute((constructor)) は GCC の拡張機能で
main関数の前に行いたい処理がある場合に使います。
# コンパイル(ライブラリ作成)
$ gcc -fPIC -shared -o write.o write.c -ldl
# コンパイル
$ gcc main.c -o main
# 実行
$ LD_PRELOAD=./write.o ./main
Hello Hack
Hello World!
結果はこの通り
main.cでは「Hello World」のみの出力のはずが
その直前に「Hello Hack」が出力されています。
# lddコマンドでライブラリの依存関係を確認
$ ldd main
linux-vdso.so.1 => (0x00007fff619d1000)
libc.so.6 => /lib64/libc.so.6 (0x0000003edae00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003edaa00000)
$ export LD_PRELOAD=./write.o
$ ldd main
Hello Hack
linux-vdso.so.1 => (0x00007fff643a7000)
./write.o (0x00002b947289c000)
libc.so.6 => /lib64/libc.so.6 (0x0000003edae00000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003edb600000)
/lib64/ld-linux-x86-64.so.2 (0x0000003edaa00000)
まとめ/感想
今回の方法を使えば独自printf等が作成できます。
完成されたプログラムが使用しているライブラリ関数を
プログラム本体を修正せずに手を加えることが出来るので
使う機会はあまり無いですが面白いのでおすすめです。
参考文献/リンク
Author And Source
この問題について(動的ライブラリ関数をhackしよう), 我々は、より多くの情報をここで見つけました https://qiita.com/ryuichi1208/items/57eb41d5943ae746f0a4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .