Linuxのシステムコールをフックしてみます。
システムコールはユーザ空間のアプリケーションがカーネルの機能を利用するために必ず通る必要のある道で、ここに手を加えることですべてのアプリケーションを制御することができます。
しかし、その強力さからrootkitなどにも利用できるため、最近のカーネルでは以下の2つの壁によってシステムコールのフックができないようになっています。
- sys_call_tableをエクスポートしない
- sys_call_tableを読み取り専用にする
今回は、この2つを無効化したカーネルを作成し、自由にシステムコールをフックできる環境を構築します。当然ですが、rootkitの攻撃も受け入れる状態になってしまいますので、本番環境では絶対に使用しないでください。
実験環境
今回はScientific Linux 6.1 (32bit)をベースにカーネルを3.1.0に置き換えて実験しました。
カーネルをビルド
まず、改造前のカーネルをビルドして動作を確認しておきます。
makeはかなり時間がかかりますが、気長に待ちましょう。
GRUBのメニューに項目を追加します。また、hiddenmenuをコメントアウトして起動時にメニューが表示されるようにします。
再起動してScientific Linux (3.1.0)を選択し、正しく起動すれば成功です。
sys_call_tableをエクスポート
システムコールの実体のアドレス一覧は、sys_call_tableという配列に保持されていますが、2.6以降のカーネルではエクスポートされなくなってしまったため、対策が必要です。実行時に強引にアドレスを探り当てるという方法もありますが、もう1点の問題を解決するためにどちらにしろカーネルへの変更が必要になるため、sys_call_tableをエクスポートするようにカーネルを改造します。
ファイルの末尾に以下の2行を追加します。
sys_call_tableの読み取り専用属性を解除
いつのバージョンからかは未確認ですが、sys_call_tableが読み取り専用になり、仮にアドレスを探り当てたとしても書き込めないようになってしまいました。ここでは読み取り専用属性を解除し、自由に書き換えられるようにします。
.section部分の記述を修正します。
改造したカーネルをビルド
2回目のmakeですがそれなりに時間が掛かります。やはり気長に待ちましょう。
無事に再起動できればそこは自由な世界、になっていると思われます。
動作確認
openシステムコールをフックするLKMを作って動作させてみます。今回作成したプログラムは、システムコールに渡された引数を表示してオリジナルのopenシステムコールにそのまま流すだけのものです。
なお、モジュールをつくるところまではrootでなくてもできるので普通のユーザで作業しています。
syscall-hook.c
Makefile
結果
rootになってモジュールをinsmod/rmmodしてみます。フックできていれば、/var/log/messagesにopenシステムコール呼び出しの内容が出力されます。
おわりに
こういうのを見てると、安全と自由を両立させることの難しさを感じますね。なんとか解決できればとは思うのですが……。
参考