WinDbg: ダンプファイルの解析をしたいがオフライン環境のためシンボルファイルがダウンロードできないとき

ダンプファイル(メモリダンプ, .dmp ファイル)を解析したいがデバッグ環境がインターネットから切り離されているため OS のシンボルファイル(デバッグ情報ファイル, プログラムデータベース、.PDB ファイル)がダウンロードできない場合、どこか別のオンライン環境にて適切なシンボルファイルをダウンロードして持ってくる必要があります。その方法についていくつか説明します。

動作確認環境

  • Windows 11 Home 22H2
  • WinDbg 10.0

ダンプファイルを別環境に持っていき WinDbg でダウンロード

ソースファイルは持ち出せないがダンプファイルは持ち出せる場合、そのダンプファイルをインターネットにつながっている環境にコピーし、シンボルファイルをダウンロードし、シンボルファイルを元の環境にコピーすればいいでしょう。

持ち出したダンプファイルが「c:\tmp\test.dmp」にあるとします。

C:\tmp> dir /s /b *.dmp
C:\tmp\test.dmp

シンボルファイルのダウンロード先フォルダを作ります。
今回は「c:\tmp\TestSymbols」に格納することにします。

C:\tmp> md TestSymbols

WinDbg を起動し、[File]-[Open Crash Dump] コマンドから「test.dmp」を開きます。

次のコマンドを順番に入力し、シンボルファイルをダウンロードします。「!sym noisy」でシンボルファイルのダウンロードの様子を表示するよう指示し(こうしないとダウンロード中に無応答になっているように見える)、「.symfix c:\tmp\TestSymbols」でダウンロード先を設定し、「.reload /f」で強制ダウンロードしています。

!sym noisy
.symfix c:\tmp\TestSymbols
.reload /f

ダウンロード中の様子です。

ダウンロードが終わると、「c:\tmp\TestSymbols」内に多くのフォルダとファイルが生成されます。

これらをフォルダ構成はそのままごっそり圧縮し、デバッグ環境に持っていくといいでしょう。

ダンプファイルを別環境に持っていき SymChk でダウンロード

同様の処理を WinDbg と同時にインストールされる SymChk で行うこともできます。

コマンドの例を示します。「/v」でシンボルファイルのダウンロードの様子を表示するよう指示し、「/id」でダンプファイルを指定し、「/s」でシンボルファイルのダウンロード先とダウンロード元を指定しています。

C:\tmp> symchk /v /id test.dmp /s srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols

ダウンロード中の様子です。

[SYMCHK] Searching for symbols to modules in dump file(s) using path srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
DBGHELP: Symbol Search Path: srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
[SYMCHK] Using search path "srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols"
DBGHELP: No header for c:\tmp\TestSymbols\TextShaping.dll\A774FDCDb0000\TextShaping.dll.  Searching for image on disk
DBGHELP: c:\tmp\TestSymbols\TextShaping.dll\A774FDCDb0000\TextShaping.dll - OK
......
[SYMCHK] Using search path "srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols"
DBGHELP: No header for c:\tmp\TestSymbols\KERNELBASE.dll\9223BDA83a6000\KERNELBASE.dll.  Searching for image on disk
DBGHELP: c:\tmp\TestSymbols\KERNELBASE.dll\9223BDA83a6000\KERNELBASE.dll - OK
SYMSRV:  BYINDEX: 0x10
         c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
         kernelbase.pdb
         FD716FB42DA11292354EFD1727DBA7751
SYMSRV:  UNC: c:\tmp\TestSymbols\kernelbase.pdb\FD716FB42DA11292354EFD1727DBA7751\kernelbase.pdb - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\kernelbase.pdb\FD716FB42DA11292354EFD1727DBA7751\kernelbase.pd_ - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\kernelbase.pdb\FD716FB42DA11292354EFD1727DBA7751\file.ptr - path not found
SYMSRV:  HTTPGET: /download/symbols/kernelbase.pdb/FD716FB42DA11292354EFD1727DBA7751/kernelbase.pdb
SYMSRV:  HttpQueryInfo: 801900c8 - HTTP_STATUS_OK
SYMSRV:  kernelbase.pdb from http://msdl.microsoft.com/download/symbols: 11694080 bytes -  29 percent
......

ダウンロードされました。

SymChk を使うと、シンボルファイルだけでなく DLL もダウンロードされるようです。たとえば、上記画像の「advapi32.dll」フォルダの中には DLL が、「advapi32.pdb」フォルダの中にはシンボルファイル (.pdb) が入っています。

ダンプファイルから DLL の一覧を抜き出して別環境でダウンロード

ダンプファイルそのものが持ち出せなくても、ダンプファイルに含まれる DLL の一覧が持ち出せれば、シンボルファイルをダウンロードできます。

まずは SymChk コマンドを使って、ダンプファイルに含まれる DLL の一覧を取得します。
コマンドの例を示します。「/id」でダンプファイルを指定し、「/om」で出力ファイル名を指定しています。

C:\tmp> symchk /id test.dmp /om test.txt

今回は「test.txt」として次のファイルが生成されました。このファイルをマニフェストファイルと呼びます。

C:\tmp> type test.txt
test.exe,653de664a1000,2
TextShaping.dll,a774fdcdb0000,2
textinputframework.dll,75efbdb614a000,2
comctl32.dll,e01e64f5b3000,2
uxtheme.dll,32c68501ab000,2
kernel.appcore.dll,139856da18000,2
gdi32full.dll,dc3616ef118000,2
KERNELBASE.dll,9223bda83a6000,2
......
msvcrt.dll,c4d8152ca7000,2
comdlg32.dll,a61322cdf9000,2
combase.dll,5b9e9b538a000,2
ntdll.dll,806d3ecf217000,2

ファイル名に続く 16 進数は、モジュールのバージョンを識別するための、タイムスタンプ(time date stamp)とイメージサイズ(size of image)を連結した値です。

たとえば、最終行にある「ntdll.dll」ファイルのヘッダ情報を見てみましょう。

C:\Windows\System32> dumpbin /headers ntdll.dll
        ......
        806D3ECF time date stamp
        ......
          217000 size of image
        ........

「time date stamp」の値と「size of image」の値を連結すると、マニフェストファイルに含まれる値「806d3ecf217000」になります。

さて、このマニフェストファイルをインターネットにつながっている環境にコピーし、同じく SymChk コマンドを使うと、対応するシンボルファイルがダウンロードできます。

コマンドの例を示します。「/v」でシンボルファイルのダウンロードの様子を表示するよう指示し、「/im」でマニフェストファイルを指定し、「/s」でシンボルファイルのダウンロード先とダウンロード元を指定しています。

C:\tmp> symchk /v /im test.txt /s srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols

ダウンロード中の様子です。

DBGHELP: Symbol Search Path: srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
[SYMCHK] Using search path "srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols"
DBGHELP: No header for c:\tmp\TestSymbols\TextShaping.dll\A774FDCDb0000\TextShaping.dll.  Searching for image on disk
DBGHELP: c:\tmp\TestSymbols\TextShaping.dll\A774FDCDb0000\TextShaping.dll - OK
......
[SYMCHK] [ 0x00000000 - 0x00030001 ] Checked "c:\tmp\TestSymbols\textinputframework.dll\75efbdb614a000\textinputframework.dll"
DBGHELP: Symbol Search Path: srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
SYMSRV:  BYINDEX: 0x6
         c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
         comctl32.dll
         e01e64f5b3000
SYMSRV:  UNC: c:\tmp\TestSymbols\comctl32.dll\e01e64f5b3000\comctl32.dll - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\comctl32.dll\e01e64f5b3000\comctl32.dl_ - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\comctl32.dll\e01e64f5b3000\file.ptr - path not found
SYMSRV:  HTTPGET: /download/symbols/comctl32.dll/e01e64f5b3000/comctl32.dll
SYMSRV:  HttpQueryInfo: 801900c8 - HTTP_STATUS_OK
SYMSRV:  comctl32.dll from http://msdl.microsoft.com/download/symbols: 729376 bytes -  87 percent
......

ダウンロードされました。

ところが、ダンプファイルをもとにダウンロードしたファイルと見比べてみると、「combase」に関するファイルが欠けています。
改めてログを見てみると、「combase.dll」のダウンロードがエラーコード 2(= ファイルが見つからない)で失敗していました。

......
DBGHELP: Symbol Search Path: srv*c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
SYMSRV:  BYINDEX: 0x36
         c:\tmp\TestSymbols*http://msdl.microsoft.com/download/symbols
         combase.dll
         5b9e9b538a000
SYMSRV:  UNC: c:\tmp\TestSymbols\combase.dll\5b9e9b538a000\combase.dll - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\combase.dll\5b9e9b538a000\combase.dl_ - path not found
SYMSRV:  UNC: c:\tmp\TestSymbols\combase.dll\5b9e9b538a000\file.ptr - path not found
SYMSRV:  HTTPGET: /download/symbols/combase.dll/5b9e9b538a000/combase.dll
SYMSRV:  HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV:  HTTPGET: /download/symbols/combase.dll/5b9e9b538a000/combase.dl_
SYMSRV:  HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV:  HTTPGET: /download/symbols/combase.dll/5b9e9b538a000/file.ptr
SYMSRV:  HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV:  RESULT: 0x80190194
SYMCHK: combase.dll          ERROR - Unable to download file. Error reported was 2  ★ 失敗
......

調べたところ、マニフェストファイル内の「combase.dll」の識別子が、「05b9e9b538a000」であるべきところ、先頭の「0」を除いた「5b9e9b538a000」になっているのが原因のようでした。
「combase.dll」のヘッダ情報を見てみると、「time date stamp」の値が、実際のタイムスタンプではなく最近よくある repro 用ハッシュ値になっています [1]。

C:\Windows\System32> dumpbin /headers combase.dll
        ......
         5B9E9B5 time date stamp  ★ タイムスタンプではなくハッシュ値
        ......
          38A000 size of image
        ......
    05B9E9B5 repro ......  ★ repro 指定あり

そのため、「time date stamp」の長さが、タイムスタンプだとしたら事実上あり得ない 7 桁以下の値(おおよそ 1978 年以前)になってしまい、しかし symchk コマンドが 7 桁以下を想定しておらず、エラーになったと思われます。
マニフェストファイルの「combase.dll,5b9e9b538a000,2」の行を「combase.dll,05b9e9b538a000,2」に手作業で書き換えたところ、正しくダウンロードできました。

おわりに

シンボルファイルのダウンロード方法をいくつか説明しました。シンボルファイルをデバッグ環境に持っていたら、その場所の情報を、.sympath や .symfix コマンドで WinDbg に教えてください。

参考文献

[1] やや低レイヤー研究所, Windows システムファイルのビルド日時が表示されない謎を探る. https://yaya.lsv.jp/repro/