on
x84-64環境でシステムコールをフックする
はじめに
x86-64環境において,syscall命令実行時のエントリポイントを書き換えることで,システムコールをフックする.
システムコールを呼び出す従来の手法では,int 0x80命令による割り込みが用いられていたが,x86-64環境ではではより高速なsyscall命令が用いられる.
ユーザ空間からsyscall命令が発行されると,カーネルは,MSRレジスタのMSR_LSTARに設定されている値をRIPにセットする.
この値が,システムコールのエントリポイントのアドレスとなっており,システムコールが実行される.
システムコールのエントリポイントでは,スタックポインタの切り替えや,ユーザ空間のレジスタの退避を行い,システムコール番号や引数を元に対応するシステムコールが呼び出される.
syscall命令によるシステムコールをフックするには,MSRレジスタのMSR_LSTARの値を任意の値に書き換えることで実現する.
なお,MSRレジスタの読み書き込みは,特権命令であるrdmsr/wrmsr命令でのみ行える.
設計
前述の通り,syscall命令によるシステムコール呼び出しでは,MSRレジスタのMSR_LSTARの値がシステムコールのエントリポイントを表す.
システムコールをフックするためには,このMSR_LSTARの値を任意の値に書き換えれば良い.
今回は,MSR_LSTARの値をトランポリンのアドレスに書き換え,プロセスの生成/終了に関わるシステムコールを検知する.その後,正規のエントリポイントから命令を実行させ,システムコールを継続させる.
まとめると以下のようになる.
- MSRレジスタのMSR_LSTARの値(正規のエントリポイントの値)を読み出し保存する.
- MSRレジスタのMSR_LSTARの値をトランポリンのドレスに書き換える
トランポリンにおける処理
- スタックポインタをユーザ空間からカーネル空間に切り替える
- ユーザ空間のレジスタを退避
- 任意の処理の追加(プロセス生成/終了の検知)
- ユーザ空間のレジスタのロード
- スタックの値をカーネル空間からユーザ空間に切り替える
- 正規のエントリポイントにジャンプ