All Articles

Windows 環境のトラブルシューティングに役立つ GFlags の設定についてのメモ書き

先日作成した WinDbg でダンプ解析、ライブデバッグを行う時のチートシート からの派生で、 Windows のトラブルシューティング時に覚えておくと便利な GFlags の設定値についてまとめました。

個人的に役に立つと感じる設定のみ記載していますが、こちらも自分用のメモ書きなので、また今後別の場面で活用した設定などあれば追記していきたいと思います。

もくじ

まえがき

本記事の内容はすべて一般に公開されている情報や、出版された書籍、または個人の検証環境で動作確認を実施した結果のみを元に作成しています。

関連記事はこちら。

GFlags とは

GFlags(Global Flags Editor) は、特定のデバッグ機能の有効化/無効化に使用できるツールです。

Debugging Tools for Windows 10 (WinDbg) に含まれているので、Classic の WinDbg をインストールしている環境の場合は、WinDbg と同じフォルダ(C:\Program Files (x86)\Windows Kits\10\Debuggers\x64など)に gflags.exe と gflags.dll が配置されています。

参考:GFlags - Windows drivers | Microsoft Learn

GFlags は CLI および GUI の両方で使用することができ、これを利用してシステムやイメージ(プログラム)のグローバルフラグを操作することが可能です。

コマンドで GFlags を操作する方法は以下に記載されています。

参考:GFlags Commands - Windows drivers | Microsoft Learn

グローバルフラグとは

グローバルフラグは、Windows のシステムやイメージのデバッグやトレースをサポートするための変数です。

システムのグローバルフラグ

システムのグローバルフラグは、 NtGlobalFlag と NtGlobalFlag2 の 2 つのグローバル変数で管理されています。

これらのシステム変数はHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Managerで定義されていて、システムの起動時に初期化されます。(既定値はどちらも 0 で、グローバルフラグは設定されていません。)

image-20230506234025475

システム側のグローバルフラグは、カーネルデバッグ時に!gflag拡張機能を使用することで参照できます。

ただし、現在はこの拡張は NtGlobalFlag の設定値にしか対応していないようです。

image-20230506235525826

!gflag -?コマンドを実行すると、グローバルフラグのヘルプを出力することができます。

イメージファイルごとのグローバルフラグ

また、各イメージ(プログラム)にもグローバルフラグの設定が用意されています。

各イメージファイルごとのグローバルフラグはHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<Application>に定義されています。

このレジストリ配下にはアプリケーション起動時にプログラムを開くアプリケーションを指定するDebuggerオプションや、アプリケーション終了時のプロセス起動に悪用できるSirentProcessExitが含まれます。

これらの Image File Execution Options(IEFO) オプションは、デバッグ用途だけでなく Malware が Persistence の目的で悪用する場合があることも知られています。

各イメージのグローバルフラグは、ローダや Windows エラー報告(WER) などのユーザモードコンポーネントによって処理されます。

例えばローダがグローバルフラグを処理する場合には、プログラムが起動するためにプロセスを初期化して依存モジュールをロードする際にグローバルフラグが処理されます。

この時にローダが実行するLdrpInitializeExecutionOptions関数は、イメージファイルの名前に基づいて IEFO のレジストリキーを参照し、グローバルフラグを取得します。

ローダがレジストリから取得したグローバルフラグの情報は PEB 内の NtGlobalFlag と NtGlobalFlag2 に格納されます。

実際に GFlags でグローバルフラグを設定したメモ帳アプリの PEB を参照してみると、以下のようにNtGlobalFlagに値が設定されていることを確認できました。

> !ped
No export ped found
0:000> !peb
PEB at 00000030dbc1e000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            Yes
    ImageBaseAddress:         00007ff689330000
    NtGlobalFlag:             70
    NtGlobalFlag2:            0
    Ldr                       00007ffcf7f9c4c0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 000001744bbe2a10 . 000001744bbe3130
    Ldr.InLoadOrderModuleList:           000001744bbe2bc0 . 000001744bbf04e0
    Ldr.InMemoryOrderModuleList:         000001744bbe2bd0 . 000001744bbf04f0

参考:インサイドWindows 第7版 下

カーネルモードスタックをページアウトされないようにする

GFlags からDisable paging of kernel stacksのグローバルフラグを設定することで、カーネルモードスタックのページアウトを無効化することができます。

Disable paging of kernel stacksは、FLG_DISABLE_PAGE_KERNEL_STACKS(0x80000)で設定されます。

以下の Docs に記載の通り、カーネルモードスタックは通常ページングされず、メモリ内に常駐することが保証されています。

しかし、非アクティブなスレッドのカーネルスタックについてまれにページアウトが発生する場合があります。

カーネルスタックがページアウトされると、カーネルデバッグやフルダンプの解析時に対象のスレッドに関する情報を参照することができなくなります。

そのため、例えばデッドロック事象のデバッグや、すべてのスレッドを追跡する必要がある場合にこのグローバルフラグを設定することが有効です。

参考:Disable paging of kernel stacks - Windows drivers | Microsoft Learn

Disable paging of kernel stacksは以下の箇所から設定できます。

image-20230512201841302

なお、実際のデバッグの際には上記のカーネルスタックのページアウトの防止だけでなく、プール領域ののメモリ割り当てに関する統計を計算することができるFLG_POOL_ENABLE_TAGGING(0x400)のグローバルフラグを設定するのも良いという情報が以下のブログに記載されていました。

参考:カーネルモードスタックをページアウトされないようにするための方法 | 窓のくすり箱

参考:Enable pool tagging - Windows drivers | Microsoft Learn

イメージローダの処理を見る

GFlags を使ってShow Loader Snapsのデバッグ機能を有効化するグローバルフラグをセットすることで、対象のアプリケーションの起動時にイメージローダのデバッグ出力を参照できるようになります。

これにより、実行ファイルとライブラリのロードとアンロードの情報をデバッガに出力できます。

Show Loader SnapsFLG_SHOW_LDR_SNAPS(0x2)で、システム全体に設定された場合はドライバのロードおよびアンロードの情報を出力します。

特定の ImageFile のみに設定された場合は、アプリケーション起動時の DLL のロードとアンロードに関する情報を出力します。

参考:Show loader snaps - Windows drivers | Microsoft Learn

例えば、gflags.exe を起動して [Image File] から以下のように設定することで、Notepad.exe に対してShow Loader SnapsDebuggerフラグを設定します。

image-20230506194811885

この設定を適用することで、HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Notepad.exe配下のDebuggerに WinDbg のパスが設定されます。

また、GlobalFlagに値 2 がセットされます。(この環境では他に Notepad.exe に GlobalFlag は設定していないため、Show Loader Snapsの Hexadecimal value である 0x2 がそのまま適用されます。)

これで Notepad.exe にはDebuggerフラグが設定されたため、メモ帳を起動しようとすると自動的に WinDbg がアタッチされ、以下のような出力が記録されます。

ModLoad: 00007ff7`8c3b0000 00007ff7`8c3e8000   notepad.exe
ModLoad: 00007ffd`1f2d0000 00007ffd`1f4c8000   ntdll.dll
22e0:0710 @ 00503718 - LdrpInitializeProcess - INFO: Beginning execution of notepad.exe (C:\WINDOWS\system32\notepad.exe)
	Current directory: C:\Users\Tadpole01\
	Package directories: (null)
22e0:0710 @ 00503718 - LdrLoadDll - ENTER: DLL name: KERNEL32.DLL
22e0:0710 @ 00503718 - LdrpLoadDllInternal - ENTER: DLL name: KERNEL32.DLL
22e0:0710 @ 00503718 - LdrpFindKnownDll - ENTER: DLL name: KERNEL32.DLL
22e0:0710 @ 00503718 - LdrpFindKnownDll - RETURN: Status: 0x00000000
22e0:0710 @ 00503718 - LdrpMinimalMapModule - ENTER: DLL name: C:\WINDOWS\System32\KERNEL32.DLL
ModLoad: 00007ffd`1d390000 00007ffd`1d44f000   C:\WINDOWS\System32\KERNEL32.DLL
22e0:0710 @ 00503718 - LdrpMinimalMapModule - RETURN: Status: 0x00000000
22e0:0710 @ 00503718 - LdrpPreprocessDllName - INFO: DLL api-ms-win-core-rtlsupport-l1-1-0.dll was redirected to C:\WINDOWS\SYSTEM32\ntdll.dll by API set
22e0:0710 @ 00503718 - LdrpFindKnownDll - ENTER: DLL name: KERNELBASE.dll
22e0:0710 @ 00503718 - LdrpFindKnownDll - RETURN: Status: 0x00000000
22e0:0710 @ 00503718 - LdrpMinimalMapModule - ENTER: DLL name: C:\WINDOWS\System32\KERNELBASE.dll
ModLoad: 00007ffd`1ca10000 00007ffd`1cd06000   C:\WINDOWS\System32\KERNELBASE.dll
22e0:0710 @ 00503718 - LdrpMinimalMapModule - RETURN: Status: 0x00000000
22e0:0710 @ 00503718 - LdrpPreprocessDllName - INFO: DLL api-ms-win-eventing-provider-l1-1-0.dll was redirected to C:\WINDOWS\SYSTEM32\kernelbase.dll by API set
22e0:0710 @ 00503718 - LdrpPreprocessDllName - INFO: DLL api-ms-win-core-apiquery-l1-1-0.dll was redirected to C:\WINDOWS\SYSTEM32\ntdll.dll by API set
22e0:0710 @ 00503718 - LdrpPreprocessDllName - INFO: DLL api-ms-win-core-apiquery-l1-1-1.dll was redirected to C:\WINDOWS\SYSTEM32\ntdll.dll by API set

出力はかなり多い(手元の環境で 120 KB くらい)ので、必要に応じてログファイルに出力しておくとよいと思います。

例えば、Debuggerフラグに以下のようにログ出力先のファイルパスを含めて実行すると、起動時のログをファイルに出力することができます。

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe -logo  C:\Users\Public\debug.log

Show Loader Snapsのフラグを有効化している場合は、アプリケーションの起動時だけでなく何らかの操作でモジュールが後からロードされる場合にも、WinDbg 上にイベントを出力します。

プログラムの起動時にデバッガを起動する

前述しましたが、各イメージごとのDebuggerオプションを設定することでプログラムの起動時にデバッガをアタッチすることができるようになります。

このような Image File Execution Options(IFEO) の設定はグローバルフラグとは異なる設定ですが、GFlags から設定することが可能です。

例えば、以下のように設定を行うことでメモ帳を起動する際に WinDbg を使用してプログラムを起動し、速やかにデバッグを開始することができます。

image-20230512203828930

この設定は、例えば直接実行することが許可されず、何らかのインターフェースやジョブを通して起動する必要があり、かつシステムに常駐しないためにデバッガをアタッチすることが困難なシナリオでデバッグを行う必要がある際などに非常に役に立ちます。

参考:Image File Execution Options | Microsoft Learn

なお、この時Debuggerのパスに指定できるパスは実際のデバッガのパスに限定されません。

例えば以下のようにエディタやその他の任意のプログラムを指定すると、メモ帳アプリの起動時に自動的に指定したプログラムでメモ帳を開くようになります。

image-20230520162808075

このような仕様は悪用される場合もあるので注意が必要です。

プログラムの終了を監視する

こちらも厳密にはグローバルフラグではないですが、各アプリケーションのSilent Process Exitを有効化することでアプリケーションの終了を監視をすることができます。

監視できる終了操作は ExitProcess による自分自身の終了(右上の終了ボタンを押した場合など)か、TerminateProcess による他アプリケーションからの終了操作のみです。

Silent Process Exitを設定すると、アプリケーションが終了した際にクラッシュダンプを出力したり、ホストに通知を送信したりできます。

また、Monitor process として任意のアプリケーションを起動することもできます。(こちらも悪用される場合があるので注意が必要です。)

設定は、GFlags のSilent Process Exitから行うことができます。

この設定によって、クラッシュではない要因によって終了したプログラムのダンプを取得してトラブルシューティングを行ったり、プログラムを終了させた要因を特定できるようになります。

例えば以下のようなオプションでメモ帳アプリのSilent Process Exitを設定したとします。

※ ダンプの種類を [Custom Dump] に設定した場合、取得できるダンプ種類はMINIDUMP_TYPE (minidumpapiset.h)の値によって決定されます。今回は、MiniDumpWithFullMemoryの値である 0x2 を設定しています。

image-20230520165705480

この状況でタスクマネージャからメモ帳を強制終了した場合、以下のように終了したタスクマネージャのプロセスのダンプも取得されます。

image-20230520164926209

また、Stop-Process -Name notepadによって Power Shell から終了した場合は、このように Power Shell のダンプが取得できます。

image-20230520165942146

参考:Monitoring Silent Process Exit - Windows drivers | Microsoft Learn

参考:プロセスを終了させたプロセスを見つける - Windows の資料採取に関する情報公開サイト

ちなみに、Monitor Process の設定にデバッガのパスを設定しても、残念ながら終了直前のプロセスにデバッガをアタッチすることはできません。

しかし、例えばプログラムの終了を通知するようなプログラムを起動させたり、プログラム終了直後のシステム情報を収集するようなツールを実行させるといった使い道など、活用方法は数多くありそうです。

まとめ

今回は、Windows のトラブルシューティング時に覚えておくと便利な GFlags の設定値についてまとめました。

個人的に役に立つと感じる設定のみ記載していますが、また今後別の場面で活用した設定などあれば追記していきたいと思います。