→ ハードウェアDEP機能の調査 (CodeZine) 完成図 †→ Check NX-bit/XD-bit Tools v.0.1 w/src (MD5SUM : 93fcb3cfcdafd19c38c38068f713ceb8) はじめに †この記事では、Windows XP SP2(XPSP2) より実装された DEP 機能について考察を行います。 今回、デモプログラム(checknx)を作成しました。このプログラムよりどのように DEP 機能が働いているかを確認し、実際にメモリが保護されているかを考察します。 対象読者 †
必要な環境 †下記の環境が揃わないと面白くありません。Visual C++ version 6 SP6(MFC) で開発を行いましたが、他の開発環境でも簡単に移植できると思っています。
データ実行防止(DEP)機能について †ハードウェア DEP 機能 †Windows XP SP2(XPSP2)/Windows Server 2003 SP1 よりサポートされた DEP(Data Execution Protections、データ実行防止) 機能というのはご存知でしょうか。 DEP 機能とは、XPSP2 にて新たに追加されたセキュリティ機能で、悪質なプログラムによるバッファ・オーバーラン等の攻撃からシステムを守る働きをします。DEP 機能には、CPU に備わったメモリ保護機能(NX-bit/XD-bit)を利用してるバッファ・オーバーランを検知する「ハードウェア DEP 機能」と、ソフトウェアのみによるバッファ・オーバーランを検知する「ソフトウェア DEP 機能」の2通りがあります。「ソフトウェア DEP 機能」は全てのシステムで適用されますが、「ハードウェア DEP 機能」の場合、CPU 自身が NX-bit/XD-bit という機能を持ち合わせていないと動作しません。 今回は、この NX-bit/XD-bit を使用した「ハードウェア DEP 機能」に焦点を合わせます。 NX-bit/XD-bit †NX-bit(No eXecute) は、AMD 社が搭載したメモリ保護機能で、AMD 社ではこの機能のことを「拡張ウィルス防止機能(Enhanced Virus Protection)」と命名しています。後に Intel 社も同様の機能を搭載していますが、名称は NX-bit ではなくXD-bit (eXecution Disable) と命名しています。機能的には NX-bit、XD-bit 共に同様と考えてよいでしょう。最近では、VIA 社や Transmeta 社の CPU にも NX-bit が採用されています。 NX-bit のメモリ保護機能 †NX-bit は、システム上のメモリの実行可能・不可能の種類を示すフラグです。NX-bit が有効な場合、実行不可能なメモリ上でプログラムを実行しようとするとCPU は保護例外を起こし、システムに「ホントに実行していいの?」とお伺いを立てます。システムは保護例外に対し、意図して実行を続けることも出来ますし、その場で例外エラーを発生させることも出来ます。 単純な機能ですが、セキュリティ面でかなり役立つ機能と言われています。 悪質なプログラムの多くは、バッファ・オーバーランというプログラム上のバグを利用してシステムを破壊します。 バッファ・オーバーランは、プログラムを開発する上で 非常に発見しづらく、セキュリティが重要視される今日ですらバッファ・オーバーランによる攻撃が未だに行われています。NX-bit は、このバッファ・オーバーラン攻撃を ハードウェア的に検知し、未然にメモリを保護することを目的として開発されています。 ハードウェア/ソフトウェア DEP 機能の確認方法 †以下の手順にて『データ実行防止』タブを開き、適用されている DEP 機能を確認します。
DEP には以下の2通りあります。デフォルトでは「システム内のモジュールのみ有効(a.)」に設定されています。
上記より DEP には以下の4通りの設定があります。この中では (4) が一番厳しい DEP となります。
checknx によるデモ †ハードウェア DEP 機能の確認を確認するためサンプルプログラム「checknx」を作成しました。以下は Windows XP SP2 上で動作確認を行いました。 結果は以下の通り。 (1) と (2) の場合 1. Is Supported CPUID? ---> OK 2. Is Supported Extended CPUID? ---> OK 3. Is Supported NX/XD-bit? ---> Not supported. 4. Is Supported NX/XD-bit by OS? ---> OK 5. Is Enabled NX/XD-bit by OS? ---> Disable 6. Is working Hardware DEP? ---> NOT working... finish... (3) の場合 1. Is Supported CPUID? ---> OK 2. Is Supported Extended CPUID? ---> OK 3. Is Supported NX/XD-bit? ---> OK 4. Is Supported NX/XD-bit by OS? ---> OK 5. Is Enabled NX/XD-bit by OS? ---> Enable 6. Is working Hardware DEP? ---> NOT working... finish... (4) の場合 1. Is Supported CPUID? ---> OK 2. Is Supported Extended CPUID? ---> OK 3. Is Supported NX/XD-bit? ---> OK 4. Is Supported NX/XD-bit by OS? ---> OK 5. Is Enabled NX/XD-bit by OS? ---> Enable 6. Is working Hardware DEP? ---> Working! EXTRA. Try to Execute NX error? ---> push [Try NX!] finish... checknx のアルゴリズム †
checknx の要点 †CPUID 命令 †CPUID(CPU Identification) 命令は i386 系の CPU より追加された命令です。この命令を実行することで使用している CPU の詳細情報を取得できます。例えば SSE 命令をサポートしているかどうかを CPUID 命令から取得し、サポートしている CPU ならば SSE 専用の高速化ライブラリを使用するというような切り分けに用いられます。 今回は NX-bit をサポートしているかどうかの確認のため使用します。 RDMSR 命令 †RDMSR/WRMSR 命令は Pentium Pro より追加された命令です。この命令を実行することで CPU 内の MSR(Model-Specific Register)を読み書きすることが出来ます。一般的に MSR は CPU の動作を変更させることの出来る重要なレジスターであることより、特権レベルでないと読み書きできません。 今回は、実際に NX-bit によるメモリ保護を行っているかの確認に使用します。 SEH †SEH (Structured Exception Handling, 構造化例外処理) は Windows に標準で実装されているシステムレベルの例外処理です。今回は SEH を用いて CPUID 命令を直接実行しています。本来ならシステムが CPUID 命令をサポートしているか複雑な手順で確認してから CPUID 命令を実行する必要があります。仮に cpuid 命令をサポートしていない CPU で命令を実行した場合、CPU はシステムに対し一般保護例外を発行し、通常システムはその時点で実行を中断します。 SEH は、この一般保護エラーを例外処理として処理することが出来、実行を中断させること無く処理を続けることが出来ます。今回はこの機能を利用して CPUID 命令をサポートしているかどうかのチェックを省いています(手抜き?)。そもそも Windows XP を実行出来る CPU を実装しているのであれば CPUID 命令は当然実装されているであろう、という考えもありました。 一般保護エラーを発生させること無く CPUID 命令をサポートしているかどうかの処理については AP-485*1 が詳しいです。 ZwSystemDebugControl †上記で説明した RDMSR 命令ですが通常の方法では実行できません。一般的には特権レベルで動作するデバイスドライバを作成し、Read/Write/DeviceIoConrol を経由して MSR 情報を取得します。ひよひよさんの CrystalCPUID*2 はこの方法を用いているようです。デバイスドライバによる取得方法は自由度が高いのですが、手順が面倒くさい、少しのミスがクリティカル(BSoD?) 等、QuickHack には向いていません。 そこで今回は ZwSystemDebugControl という非公開の Native API を用いて MSR 情報を取得することにしました。元ネタは SecurityFocus に投稿されたセキュリティ・ホール情報です。この投稿に対し Microsoft 社は Debug 権限を取得できた時点で相当危険な状態であり動作は仕様、との見解のようです。折角ですので今回使わせてもらいました =)。ただし残念ながらこの API は Windows XP でのみでしか使用出来ないようです。Windows Server 2003 や Windows XP x64 Edition では関数が正常に動作しないようでした。 Native API は、Kernel mode と User mode との橋渡しに使用される API で基本的には非公開情報のようです。 VirtualAlloc †当初、スタックを使って一般保護エラーを発生させようと試みたのですが、Release build の時と Debug build の時で挙動が異なり、安定して(?)エラーを発生させることが出来ませんでした。そこで、明示的に VirtualAlloc にて明示的に実行可能なメモリ領域を作成し問題なくデータ実行が行えることを確認した後、実行可能なメモリ領域を実行不可能なメモリ領域に変更し、再度、データ実行が行えるかで DEP が有効かどうかを判定しました。実行不可能なメモリ領域に変更してもデータ実行が行えた場合、DEP が働いていないと判断しました。 DEP 環境下でデータ実行が必要である場合(動的コード生成を実行するアプリケーション等)、明示的にデータ実行を可能にするフラグを立ててメモリ取得を行うことが推奨されています。 // Read/Write/Execute pf = (PBYTE)VirtualAlloc( NULL, 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( pf == NULL ){ return FALSE; } *pf = 0xC3; // ret*4 *(pf+1) = 0xC3; *(pf+2) = 0xC3; *(pf+3) = 0xC3; pfunc = (PFUNC)pf; // SEH __try{ (*pfunc)(); }__except(EXCEPTION_EXECUTE_HANDLER){ // unknown... goto cleanup; } // Read/Write, NO Execute if( VirtualProtect(pf, 4, PAGE_READWRITE, &dwProt) ){ __try{ (*pfunc)(); }__except(EXCEPTION_EXECUTE_HANDLER){ // work NX-bit! bRet = TRUE; } } まとめ †今回の調査より、ハードウェア DEP が動作している環境下では常に NX-bit が使用されていることが分かりました。ただし、デフォルトの設定ではシステムに関わる部分に限ったメモリ保護であり、全てのアプリケーションが保護されるわけではありませんでした。これは、NX-bit による一般保護エラーが発生した場合でも、システムに直接関わるモジュールではない場合、そのまま実行を続けることを許す処理になっていると考えられます。 データ保護設定を「次に選択するものを除くすべてのプログラムおよびサービスについて DEP を有効にする(U:)」に変更することより、全てのアプリケーションに対しデータ保護が有効になります。 ユーザーのシステム環境を守るためにも、ハードウェア DEP が有効である環境で安全に動作するアプリケーション開発が求められることになるでしょう。それに備えるためにも今後、全てのアプリケーションでハードウェア DEP を有効にした環境で開発を行うことが 推奨されることになるかもしれません。 参考資料 †インテル †
AMD †マイクロソフト †
DEP に関する記事 †
Native API に関する情報 †
関連書籍 †
From SyncHack †
History † |