ユーザー・モードのルートキットは高レベルのAPIを横取り
 ルートキットの仕組みをさらに詳しく見ていこう。最初に,ユーザー・モードのルートキットを説明したい。図2で示したように,最も高いレベルのAPIレイヤーは,ユーザー・モードのWindows APIレイヤーである。このレイヤーは,「Platform Software Development Kit(SDK)」でマイクロソフトが解説しているベースOS APIで構成されている。ディレクトリのファイルをリスト表示するWindows APIには,FindFirstFileとFindNextFileがあり,いずれも\windows\system32\kernel32.dllというDLLファイルに実装されている。Windowsエクスプローラやコマンド・プロンプトといったアプリケーションは,kernel32.dllから静的または動的に同APIをインポートして利用している。


△ 図をクリックすると拡大されます
図3●kernel32のFindFirstFile関数をインポートする実行ファイル

△ 図をクリックすると拡大されます
図4●メモ帳(Notepad.exe)プログラムがFindFirstFileを静的にインポートしている様子
 DLL(動的リンク・ライブラリ)は,プロセス中の実行ファイルが,そのDLLにエクスポートされたAPIを必要としたときにのみ,プロセスにロードされるようになっている。静的インポートでは,実行ファイルのイメージの中に,DLLとそのAPIへの参照がインポート・テーブルと呼ばれる部分に格納される。このインポート・テーブルは,プロセスの開始時にOSのローダーというプログラムがチェックする。ローダーは,インポート・テーブルにリストアップされた各DLLをプロセスのアドレス空間にロードし,イメージの参照先と参照された関数とを結びつける(図3)。

 一方,動的インポートは,実行中のプロセスがWindowsのLoadLibrary関数を使用してDLLをロードし,GetProcAddress関数を使用して,DLLが持つエクスポートされた関数のアドレスを取得することでその関数が利用できるようになる。Webサイトで入手できるDependency Walkerというツールを使うと,実行イメージの静的インポートとエクスポートの状況を確認できる。さらにプロファイリング機能によって実行プログラムにおける動的インポートの状況をキャプチャすることも可能だ。図4に,メモ帳(Notepad.exe)プログラムがFindFirstFileを静的にインポートしている様子を示した。

 マルウエアを隠すために多くのルートキットで使われている手法の1つに,プロセスのインポート・テーブルをスキャンし,キーとなるシステムDLL関数への参照を,ルートキットの関数へのポインタに置き換えるというものがある。ルートキットの関数はルートキット自身のDLLに実装されていたり,プロセスのアドレス空間に直接書き込まれたりする。


△ 図をクリックすると拡大されます
図5●FindFirstFileをフックするルートキットの仕組み
 「DLLインポート・フッキング」として知られるこういった手法でプロセスがフックされたとしよう。このとき例えば,あるプロセスがFindNextFileを呼び出すと,その情報はルートキットのコードに送られる。ルートキットのコードは,kernel32.dllの本来の関数を呼び出すが,その関数が終了したときにルートキットの制御が始まる。ルートキットはFindNextFile関数の出力内容を調べ,隠そうとするファイルやディレクトリに関するデータを取り去ってしまう。動的リンクに対応するとき,ルートキットはGetProcAddress関数をフックして,呼び出し元が使用したい関数の代わりに,ルートキットがフックした関数へのポインタを返す必要がある。図5に,このテクニックを使用する「Vanquish」というルートキットの仕組みを示した。

 ほとんどのファイル関連,メモリー関連,そしてプロセス関連のWindows APIは,\Windows\system32\ntdll.dllによってエクスポートされる低レベルAPIに依存している。ntdll.dllは,OSのカーネルが公開するユーザー・モードのネイティブAPIに対するインターフェースを提供する。タスク・マネージャはntdll.dllに装備されたNtQuerySystemInformation APIを利用して,アクティブなプロセスを検索している。つまり,プロセスを隠すときルートキットはこの関数の処理を横取りし,その出力からマルウエア・プロセスが取り除かれるように手を加える。

 前述したWindowsのファイル・リスト表示APIは,ntdll.dllのNtQueryDirectoryFileを使用するので,NtQueryDirectoryFileから返されるデータを操作することで,FindFirstFileとFindNextFileが返すデータに影響を及ぼせる。しばしばルートキットは,比較的高いレベルの関数の処理を横取りする(低レベルのntdll.dllは必要最小限しか処理を横取りしない)。これは,概して容易に行えるし,多くのシステム・ユーティリティが高レベル関数だけを使用しているためだ。

 もう1つ,ユーザー・モードのルートキットのテクニックに,ユーザー・モードのWindows APIとネイティブAPI関数にパッチを当てる(部分修正を加える)というものがある。有名なHacker Defenderというルートキットはこの手法を採用する。部分修正では,標的となるプロセスにルートキット・コードを書き込むとともに,そのプロセスにある改ざん対象の関数の最初の数バイトを変更する。これは,ルートキットのコードに制御を移行させるために行われる。そして,ルートキットのコードは制御を得ると,未変更バージョンの関数を呼び出し,呼び出し元に返される前の関数を操作する。


筆者紹介
Windows IT Proの寄稿編集者で,米Winternals Softwareの最高ソフトウエア開発責任者(CSA)でもある。また「Windows Internals」(日本語版は「インサイド Microsoft Windows」,日経BPソフトプレス発行)の共著者で,Process Explorer,Filemon,Regmonなど数多くの有名なWindowsユーティリティの作成者。