セキュリティ・コンサルタント
村上 純一

 さて,前編では仮想アドレスの仕組みを解説した。これでメモリー上のデータを隠ぺいするツール「Shadow Walker」を説明するための土台は整ったことになる。

 ではShadow Walkerについて述べよう。Shadow WalkerはOSやプロセスによるメモリー・アクセスをフィルタして,対象のメモリー上のデータを隠ぺいする。これを実現するために以下の三つ技術を実装している(図1)。

図1●Shadow Walkerの概略
図1●Shadow Walkerの概略
[画像のクリックで拡大表示]

(1)OSやプロセスによるメモリー・アクセスをフィルタする
(2)メモリー・アクセスの種類(読み出し・書き込み・実行)を識別する
(3)メモリー・アクセスが「読み出し」または「書き込み」の場合は偽のデータにアクセスさせる

 具体的には,(1)のためにページ・テーブルのPresentビットを書き換え,必ず例外ハンドラに処理が飛ぶようにする(図1【ポイント1】)。さらに処理先の例外ハンドラはルートキットによって書き換えられており,ここで偽のデータが格納されている物理アドレスをページ・テーブルに戻すことで,情報を隠ぺいするわけだ(図1【ポイント2】【ポイント3】)。

 ご存知の方もいると思うが,NX/XDビット(※PTEに導入されたメモリー上でのコード実行の可/不可を制御するためのビット)対応以前のx86プロセッサではPTEにおけるページのアクセス・タイプは「読み出し可能」と「読み書き可能」しかサポートされておらず「読み出し可能」とマークされているページは同時に「実行可能」を意味していた。つまり,データが格納されている領域を読み出し,コードとして実行することが許されているわけだ。

 これは,NX/XDビットに対応したプロセッサにより解消しており,Windows XP SP2ではこれを利用してハードウエアDEP(※コード実行の必要性に応じて実行可能なページと実行不可能なページを明確に分離し,不正なコードの実行を防止する技術)を実装している。

 話を戻すが,上記の理由により汎用的に「読み出し」または「書き込み」のみをフィルタする手段は存在しない。そのため,Shadow Walkerは,OSのページ・フォルト例外ハンドラをあらかじめ別のプログラムに置き換えた上で,隠ぺい対象のページのPTEのPresentビット(存在ビット)をクリアすることでメモリー・アクセスのフィルタを実現している。

 隠ぺい対象のページにアクセスするとPTEのPresentビットがクリアされているため,物理メモリー上にはデータがないと判断されページ・フォルト例外が発生するが,例外ハンドラは悪意あるものに置き換えられているために例外の処理を自由にコントロールできる。なお,ページ・フォルト例外ハンドラの置き換えは,IDT(interrupt description table:割り込みディスクリプタ・テーブル)を改変することで実現している。IDTの詳細については前回を参照していただきたい。

例外発生アドレスを使って実行かどうかを判定

 ページ・フォルト例外が発生するとCPUのCR2レジスタにページ・フォルトが発生した仮想アドレスの値がセットされる。CR2レジスタには例外ハンドラが呼ばれる前のプロセスが処理していたアドレスが格納されている。正規の例外ハンドラは,PTEの更新が完了した後,CR2レジスタにセットされた値に基づいて例外が発生したプロセスに処理を戻す。

 Shadow Walkerでは,置き換えた例外ハンドラがCR2レジスタの値と隠ぺい対象のページの仮想アドレスを比較することでアクセスの種類を識別する。具体的には,CR2レジスタにセットされている値と隠ぺい対象のページの仮想アドレスが等しかった場合,そのメモリー・アクセスは実行を意味している。隠ぺい対象のページ上の処理を実行しようとして例外が発生したからだ。

 もし,二つの値が異なった場合,隠ぺい対象のページの仮想アドレス以外で実行されている処理が,隠ぺい対象のページにアクセスしており,すなわち他のプロセスからの読み出しまたは書き込みだと言える。実行しているコードが書かれているのとは別の場所を読もうとしているからだ。

TLBを参照しないようにクリア

 また,TLBも考慮する必要がある。先に述べたように,ページへのアクセスはページ・テーブルを使ったアドレス変換に先立ってTLBが検索されるため,隠ぺい対象のページに対応するPTEは事前にTLBのキャッシュから消しておく必要がある。TLBは,実行用のページに対応するPTEをキャッシュするITLB(Instruction TLB)と読み書き用のページに対応するPTEをキャッシュしておくDTLB(Data TLB)の二つに分かれている。そこでTLBの内容を消去するinvlpg命令を使って事前に隠ぺい対象のページに対応するPTEを消す。そして実行時に上記の方式でメモリー・アクセスの種類を識別し,実行の場合はあらかじめ用意しておいたShadow Walker内の空処理を呼び出すことでページをITLBにキャッシュさせる。これにより以降は,コードを実行する場合に関してはITLB経由で読まれるようになるために高速にメモリー・アクセスできる。

 一方,メモリー・アクセスの種類が読み出しまたは書き込みだった場合は,偽の物理メモリーに対応したPTEをDTLBにキャッシュさせる。これにより以降のメモリー・アクセスはDTLB経由で高速に偽のページに誘導することができる。以上をまとめたのが図2だ。

図2●Shadow Walkerの詳細
図2●Shadow Walkerの詳細
[画像のクリックで拡大表示]

 このように,Shadow Walkerは,IDTを改変してページ・フォルト例外ハンドラをフックするという点においては,前回紹介した実行パスの改変を行なうルートキットと言える。しかし,メモリー・アクセスのフィルタ技術は,私見だがルートキットに限らず様々な用途に応用可能だと考えられる。これを機に皆さんも色々と考えてみると面白いのではないだろうか。

村上純一(むらかみ・じゅんいち)
国内の大手セキュリティ・コンサルティング会社に勤務。セキュリティ・コンサルタント業務やソフトウエアのぜい弱性を発見する業務に携わる。