SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

CPUIDチュートリアル

ハードウェアDEP機能の調査

Windows XP SP2のDEP機能によるメモリ保護の検証

  • このエントリーをはてなブックマークに追加

この記事では、Windows XP SP2より実装されたDEP 機能について考察を行います。今回、デモプログラム(checknx)を作成しました。このプログラムよりどのようにDEP機能が働いているかを確認し、実際にメモリが保護されているかを考察します。

  • このエントリーをはてなブックマークに追加
完成図
完成図

はじめに

 この記事では、Windows XP SP2(以下、「XPSP2」と略記)より実装されたDEP機能について考察を行います。

 今回、デモプログラム(checknx)を作成しました。このプログラムよりどのようにDEP機能が働いているかを確認し、実際にメモリが保護されているかを考察します。

対象読者

必要な環境

 Visual C++ version 6 SP6(MFC)で開発を行いましたが、他の開発環境でも簡単に移植できると思っています。できれば「NX-bitをサポートしたシステム」+「XPSP2」上でテストしてください。

  • NX-bit/XD-bitが有効なシステム
  • XPSP2/Windows Server 2003 SP1
    Windows 2000以降で動作しますが、Windows 2000にはDEPは実装されていません。

データ実行防止(DEP)機能について

ハードウェアDEPとソフトウェアDEP機能

 DEP(Data Execution Protections:データ実行防止)機能とは、XPSP2/Windows Server 2003 SP1 より新たにサポートされたセキュリティ機能で、悪質なプログラムによるバッファ・オーバーランなどの攻撃からシステムを守る働きをします。

 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がCPUに搭載したメモリ保護機能です。

 AMDではこの機能のことを「拡張ウィルス防止機能(Enhanced Virus Protection)」と命名しています。後にIntelも同様の機能を搭載していますが、名称は NX-bitではなく「XD-bit(eXecution Disable bit:エグゼキュート・ディスエーブル・ビット)」と命名しました。しかし、機能的には NX-bit、XD-bitともに同様と考えてよいでしょう。最近では、VIATransmetaのCPUにも NX-bitが採用され始めています。

 以後、NX-bit/XD-bitのことを NX-bitと記載します。

NX-bitのメモリ保護機能

 NX-bitは、メモリの実行可能・不可能の種類を示すメモリ管理領域のフラグです。

 NX-bitが有効な場合、実行不可能なメモリ上でプログラムを実行しようとするとCPUは保護例外を起こし、システムに「ホントに実行していいの?」とお伺いを立てます。システムは保護例外に対し、意図して実行を続けることもできますし、その場で例外エラーを発生させることも可能です。

 単純な機能ですが、セキュリティ面でかなり役立つ機能と言われています。

 悪質なプログラムの多くは、バッファ・オーバーランというプログラム上のバグを利用してシステムを破壊します。 バッファ・オーバーランは、プログラムを開発する上で非常に発見しづらく、セキュリティが重要視される今日ですらバッファ・オーバーランによる攻撃がいまだに行われています。NX-bitは、このバッファ・オーバーラン攻撃をハードウェア的に検知し、未然にメモリを保護することを目的として開発されています。

ハードウェア/ソフトウェアDEP機能の確認方法

 以下の手順に従って[データ実行防止]タブを開き、適用されているDEP機能を確認します。

  1. システムのプロパティを開く
    [スタート]→[コントロールパネル]を選択し、コントロールパネルで[パフォーマンスとメンテナンス]→[システム]を選択します。
  2. データ実行防止を開く
    [システムのプロパティ]ダイアログの[詳細設定]タブを選択し、[パフォーマンス]の[設定]ボタンをクリックします。[パフォーマンスオプション]ダイアログの[データ実行防止]タブを選択します。
  3. ハードウェア・ソフトウェアDEPの確認
  • ソフトウェアDEPのみ有効な場合(A.)
    ダイアログの下に「お使いのコンピュータのプロセッサでは、ハードウェアによるDEPはサポートされません。ただし、DEPソフトウェアを使用することにより、ある種類の攻撃を阻止することができます。」とのメッセージが表示されます。
    ソフトウェアDEPが有効な場合のダイアログ
    ソフトウェアDEPが有効な場合のダイアログ
  • ハードウェアDEPが有効な場合(B.)
    上記のメッセージは表示されません。見分け方はこの程度で非常に分かりづらいです。
    ハードウェアDEPが有効な場合のダイアログ
    ハードウェアDEPが有効な場合のダイアログ

 DEPには以下の2通りがあります。デフォルトでは「システム内のモジュールのみ有効(a.)」に設定されています。

  • システム内のモジュールのみに有効(a.)
  • Windows内のプログラム全部に有効(b.)

 上記よりDEPには以下の4通りの設定があります。この中では、設定「4」が一番厳しいDEPとなります。

  1. (A.)-(a.) (設定「1」)
  2. (A.)-(b.) (設定「2」)
  3. (B.)-(a.) (設定「3」)
  4. (B.)-(b.) (設定「4」)

checknxによるデモ

 ハードウェアDEP機能の確認を確認するためサンプルプログラム「checknx」を作成しました。以下に、XPSP2上で動作確認を行った結果を示します。

設定「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 のアルゴリズム

  1. CPUID(0)が取得できるか?
    取得できない場合、該当CPUか判断できないので終了。
  2. 拡張CPUID(8000-0000h)が取得できるか?
    取得できない場合、NX-bit機能が実装されていないと判断し終了。
    EAXの値が 8000-0000h以上でない場合も終了。これはIntelの拡張CPUID使用方法に記載されていた方法です。拡張CPUIDをサポートしていない場合、CPUIDの最大INDEXの値が返却されるのが仕様です。
  3. 拡張CPUID(8000-0001h)が取得できるか?
    取得できない場合、NX-bit機能が実装されていないと判断し終了。
    NX/XD-bitフラグが立っていない場合、NX-bit機能が実装されていないと判断しますが継続(Software DEP対応)。
  4. サポートOSかどうか?
    XPSP2以降、もしくはWindows Server 2003 SP1以降ではない場合、DEPサポートOSではないと判断し終了。
  5. NX-bitが有効かどうか?
    MSR(0xC0000080)の11bit目が立っている場合、NX-bitが働いていると判断し継続。立っていない場合、NX-bitが働いていないと判断するが継続。
    RDMSRが実行できなかった場合、とりあえず継続。
  6. メモリ保護が行われているか?
    動的に取得した実行可能なメモリ上で実行を行い、問題がなければメモリを実行不許可に変更し、再度実行を試みます。実行不許可にしたメモリ上で実行した時に保護エラーが発生した場合、メモリ保護が働いていると判断しました。
  7. EXTRA. 実際にメモリ保護が行われるダイアログを見ますか?
    6. の時点でメモリ保護が有効だった場合、実際にメモリ保護ダイアログを見るためのソフトウェアを用意しました(checknx自身がエラーを出力するのは嫌なので)。
    [TRY NX!]ボタンが有効になっているはずなので押してみてください。以下のダイアログが表示されれば、めでたく一般アプリケーションのメモリ保護エラーを拾える設定になっていることが確認できます。
    データ実行防止ダイアログ
    データ実行防止ダイアログ
    [メッセージを閉じる]ボタンを押すと、見慣れた「問題が発生したため、……」ダイアログが表示されます。[エラー報告を送信する]ボタンを押しても何ら改善される余地はありませんので、[送信しない]ボタンを選択してください。
    「問題が発生したため、testnx.exeを終了します。」ダイアログ
    「問題が発生したため、testnx.exeを終了します。」ダイアログ

checknxの要点

CPUID命令

 CPUID(CPU Identification)命令は、i386系のCPUより実装された命令です。この命令を実行することで使用しているCPUの詳細情報を取得できます。

 例えばSSE命令をサポートしているかどうかをCPUID命令から取得し、サポートしているCPUならばSSE専用の高速化ライブラリを使用するというような切り分けに用いられます。

 今回は NX-bitをサポートしているかどうかの確認のため使用します。

CPUIDの実装
    __try{
    _asm{
        pushad
        mov eax, idx
        cpuid
        mov a, eax
        mov b, ebx
        mov c, ecx
        mov d, edx
        popad
    }
    }__except(EXCEPTION_EXECUTE_HANDLER){
        bRet = FALSE;
    }

MSR(Model-Specific Register)

 MSRは、Pentiumより実装されたCPU固有のレジスタです。これらのレジスタはCPU内部の特殊な制御を行うのに使用します。

 RDMSR/WRMSR命令よりCPU内のMSRを読み書きを行います。一般的にMSRはCPUの動作を変更させることのできる重要なレジスタであることより、特権レベルでないと読み書きすることはできません。

 今回は、実際に NX-bitによるメモリ保護を行っているかの確認に使用します。

SEH(Structured Exception Handling)

 SEH(Structured Exception Handling、構造化例外処理)は、Windowsに標準で実装されているシステムレベルの例外処理です。

 今回はSEHを用いてCPUID命令を直接実行しています。本来ならシステムがCPUID命令をサポートしているかどうかを、複雑な手順で確認してからCPUID命令を実行する必要があります。仮にCPUID命令をサポートしていないCPUで命令を実行した場合、CPUはシステムに対し一般保護例外を発行し、通常システムはその時点で実行を中断します。

 SEHは、この一般保護エラーを例外処理として処理することができ、実行を中断させることなく処理を続けることができます。今回はこの機能を利用して、CPUID命令をサポートしているかどうかのチェックを省いています(手抜き?)。そもそもWindows XPを実行できるCPUを実装しているのであればCPUID命令は当然実装されているであろう、という考えもありました。

 一般保護エラーを発生させることなく、CPUID命令をサポートしているかどうかを判断する処理については、Intelの資料AP-485が詳しいです。

ZwSystemDebugControl

 ZwSystemDebugControlは、Windows NT系で実装されている非公開のNative APIです。

 上記で説明したRDMSR命令ですが、通常の方法では実行できません。一般的に、MSRの制御を行うには特権レベルで動作するデバイスドライバを作成し、Read/Write/DeviceIoConrolを経由してMSR情報を取得します。ひよひよさん作の「CrystalCPUID」は、この方法を用いているようです。デバイスドライバによる取得方法は自由度が高いのですが、手順が面倒くさい、少しのミスがクリティカル(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

 VirtualAllocは、メモリ取得APIの一つです。メモリ管理領域に対し、明示的に実行可能/不可能を設定することができます。

 当初、スタックを使って一般保護エラーを発生させようと試みたのですが、Release buildの時とDebug buildの時で挙動が異なり、安定して(?)メモリ保護によるエラーを発生させることができませんでした。そこで、まずVirtualAllocを利用して明示的に実行可能なメモリ領域を作成し、問題なくデータ実行が行えることを確認しました。次に、実行可能なメモリ領域を実行不可能なメモリ領域に変更し、再度、データ実行が行えるかでDEPが有効かどうかを判定しました。実行不可能なメモリ領域に変更してもデータ実行が行えた場合、DEPが働いていないと判断しました。

 DEP環境下でデータ実行が必要である場合(動的コード生成を実行するアプリケーションなど)、明示的にデータ実行を可能にするフラグを立ててメモリ取得を行うことが推奨されています。

VirtualAlloc()使用部
// 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を有効にする]に変更することより、全てのアプリケーションに対しデータ保護が有効になります。

 ユーザーのシステム環境を守るためにも、ハードウェアDEPが有効である環境で安全に動作するアプリケーション開発が求められることになるでしょう。それに備えるためにも今後、全てのアプリケーションの開発を、ハードウェアDEPを有効にした環境で行うことが推奨されることになるかもしれません。

参考資料

インテル

AMD

マイクロソフト

DEP に関する記事

Native API に関する情報

関連書籍

修正履歴

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
CPUIDチュートリアル連載記事一覧

もっと読む

この記事の著者

Mc.N(エムシイエヌ)

SyncHack 管理人。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/136 2008/09/02 12:30

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング