Windows のダンプファイルのフォーマットを知る
Windows のダンプファイル (*.dmp) はどういうフォーマット(構造)になっているのでしょうか。調べてみましょう。対象はユーザーモードダンプの主流であるミニダンプです。
動作確認環境
- Windows 11 Home 22H2
ダンプファイルの概要
ダンプファイル(ミニダンプ)のフォーマットは、マイクロソフトのサイトにある「MINIDUMP_HEADER 構造体」の説明を起点に、各メンバーの詳細を追っていくと分かります [1]。
ざっくり言うと、先頭に「ヘッダ」があり、ヘッダが「ストリームディレクトリ」を指し、ストリームディレクトリが各種の「ストリーム」を指し、ストリームは別の場所にある「データ」を指すことがある、という構造になっています。
ダンプファイルのヘッダ
ヘッダの構造は次の通りです。
typedef struct _MINIDUMP_HEADER {
ULONG32 Signature; // 署名, 'M', 'D', 'M', 'P'
ULONG32 Version; // バージョン
ULONG32 NumberOfStreams; // ストリーム数
RVA StreamDirectoryRva; // ストリームディレクトリのファイル内アドレス
ULONG32 CheckSum; // チェックサム
union {
ULONG32 Reserved; // 予約
ULONG32 TimeDateStamp; // タイムスタンプ
};
ULONG64 Flags; // フラグ(ダンプファイルの種類)
} MINIDUMP_HEADER, *PMINIDUMP_HEADER;
例を挙げます。
先頭の「署名」は ‘M’, ‘D’, ‘M’, ‘P’ です。Mini Dump の意味でしょう。
「ストリームディレクトリのファイル内アドレス」と「ストリーム数」は、ダンプファイルの 0x00000020 バイト目から 15 個のストリームディレクトリ(後述)が記録されていることを示しています。
「フラグ」には、ダンプファイルの種類が書き込まれています。次の MINIDUMP_TYPE 列挙型の値の論理和です。
typedef enum _MINIDUMP_TYPE {
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandleData = 0x00000004,
MiniDumpFilterMemory = 0x00000008,
MiniDumpScanMemory = 0x00000010,
MiniDumpWithUnloadedModules = 0x00000020,
MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
MiniDumpFilterModulePaths = 0x00000080,
MiniDumpWithProcessThreadData = 0x00000100,
MiniDumpWithPrivateReadWriteMemory = 0x00000200,
MiniDumpWithoutOptionalData = 0x00000400,
MiniDumpWithFullMemoryInfo = 0x00000800,
MiniDumpWithThreadInfo = 0x00001000,
MiniDumpWithCodeSegs = 0x00002000,
MiniDumpWithoutAuxiliaryState = 0x00004000,
MiniDumpWithFullAuxiliaryState = 0x00008000,
MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
MiniDumpIgnoreInaccessibleMemory = 0x00020000,
MiniDumpWithTokenInformation = 0x00040000,
MiniDumpWithModuleHeaders = 0x00080000,
MiniDumpFilterTriage = 0x00100000,
MiniDumpWithAvxXStateContext = 0x00200000,
MiniDumpWithIptTrace = 0x00400000,
MiniDumpScanInaccessiblePartialPages = 0x00800000,
MiniDumpFilterWriteCombinedMemory,
MiniDumpValidTypeFlags = 0x01ffffff
} MINIDUMP_TYPE;
0x428126 であれば、次の意味になります。
- MiniDumpWithFullMemory(プロセス内の全メモリ)
- MiniDumpWithHandleData(OS のハンドル情報)
- MiniDumpWithUnloadedModules(最近アンロードされたモジュールの情報)
- MiniDumpWithFullMemoryInfo(各メモリ領域の情報)
- MiniDumpWithThreadInfo(スレッドの情報)
- MiniDumpIgnoreInaccessibleMemory(読めなかったメモリ領域は無視してダンプ取得)
- MiniDumpWithIptTrace(インテルプロセッサートレースの情報)
ダンプファイルのストリームディレクトリ
ストリームディレクトリは、次の構造体の繰り返しです。
typedef struct _MINIDUMP_DIRECTORY {
ULONG32 StreamType; // ストリームの種類
MINIDUMP_LOCATION_DESCRIPTOR Location; // ストリームの場所
} MINIDUMP_DIRECTORY, *PMINIDUMP_DIRECTORY;
typedef struct _MINIDUMP_LOCATION_DESCRIPTOR {
ULONG32 DataSize; // ストリームのサイズ
RVA Rva; // ストリームのファイル内アドレス
} MINIDUMP_LOCATION_DESCRIPTOR;
typedef DWORD RVA;
(※ MINIDUMP_LOCATION_DESCRIPTOR64; は考慮しない)
言い換えると、次のデータセットの繰り返しです。
- ストリームの種類 (4 バイト)
- ストリームのサイズ (4 バイト)
- ストリームのファイル内アドレス (4 バイト)
例を挙げます。分かりやすくするため 12 バイト単位で折り返しています。
たとえば一番上の行は、ダンプファイル内のアドレス「0x00000660」から「0x000000C4」バイト分、ストリームの種類「0x00000003 (= ThreadListStream)」のデータが格納されている、という意味になります。
「ストリームの種類」には以下が定義されています。
typedef enum _MINIDUMP_STREAM_TYPE {
UnusedStream = 0,
ReservedStream0 = 1,
ReservedStream1 = 2,
ThreadListStream = 3,
ModuleListStream = 4,
MemoryListStream = 5,
ExceptionStream = 6,
SystemInfoStream = 7,
ThreadExListStream = 8,
Memory64ListStream = 9,
CommentStreamA = 10,
CommentStreamW = 11,
HandleDataStream = 12,
FunctionTableStream = 13,
UnloadedModuleListStream = 14,
MiscInfoStream = 15,
MemoryInfoListStream = 16,
ThreadInfoListStream = 17,
HandleOperationListStream = 18,
TokenStream = 19,
JavaScriptDataStream = 20,
SystemMemoryInfoStream = 21,
ProcessVmCountersStream = 22,
IptTraceStream = 23,
ThreadNamesStream = 24,
ceStreamNull = 0x8000,
ceStreamSystemInfo = 0x8001,
ceStreamException = 0x8002,
ceStreamModuleList = 0x8003,
ceStreamProcessList = 0x8004,
ceStreamThreadList = 0x8005,
ceStreamThreadContextList = 0x8006,
ceStreamThreadCallStackList = 0x8007,
ceStreamMemoryVirtualList = 0x8008,
ceStreamMemoryPhysicalList = 0x8009,
ceStreamBucketParameters = 0x800A,
ceStreamProcessModuleMap = 0x800B,
ceStreamDiagnosisList = 0x800C,
LastReservedStream = 0xffff
} MINIDUMP_STREAM_TYPE;
ダンプファイルのストリーム
各ストリーム内のデータの構造は、ストリームの種類によって変わります。
例として、ストリームの種類「3 (= ThreadListStream)」について見てみましょう。
関連する構造体の定義は以下になります。
typedef struct _MINIDUMP_THREAD_LIST {
ULONG32 NumberOfThreads;
MINIDUMP_THREAD Threads[0];
} MINIDUMP_THREAD_LIST, *PMINIDUMP_THREAD_LIST;
typedef struct _MINIDUMP_THREAD {
ULONG32 ThreadId;
ULONG32 SuspendCount;
ULONG32 PriorityClass;
ULONG32 Priority;
ULONG64 Teb;
MINIDUMP_MEMORY_DESCRIPTOR Stack;
MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
} MINIDUMP_THREAD, *PMINIDUMP_THREAD;
typedef struct _MINIDUMP_MEMORY_DESCRIPTOR {
ULONG64 StartOfMemoryRange;
MINIDUMP_LOCATION_DESCRIPTOR Memory;
} MINIDUMP_MEMORY_DESCRIPTOR, *PMINIDUMP_MEMORY_DESCRIPTOR;
typedef struct _MINIDUMP_LOCATION_DESCRIPTOR {
ULONG32 DataSize;
RVA Rva;
} MINIDUMP_LOCATION_DESCRIPTOR;
typedef DWORD RVA;
(※ MINIDUMP_LOCATION_DESCRIPTOR64; は考慮しない)
実データと照らし合わせると次のようになります。
先頭に「スレッドの数 (NumberOfThreads)」があり、その後ろに各スレッドの「スレッド ID (ThreadId) 」「優先度クラス (PriorityClass)」「スレッド環境ブロックのメモリ内アドレス (Teb)」などが記録されていることが分かります。
DumpChk コマンドで簡単解析
ここまでダンプファイルのバイナリデータと構造体の定義を見比べてきましたが、実はこんな手間のかかることをしなくても、WinDbg と同時にインストールされる DumpChk ツールを使えばダンプファイルの構造が表示されます。
実行します。
C:\tmp> DumpChk test.dmp
結果です。長いので適当に編集しています。
......
MINIDUMP_HEADER:
Version A793 (A05D)
NumberOfStreams 15
Flags 421826
0002 MiniDumpWithFullMemory
0004 MiniDumpWithHandleData
0020 MiniDumpWithUnloadedModules
0800 MiniDumpWithFullMemoryInfo
1000 MiniDumpWithThreadInfo
20000 MiniDumpIgnoreInaccessibleMemory
400000 MiniDumpWithIptTrace
Streams:
Stream 0: type ThreadListStream (3), size 000000C4, RVA 00000660
4 threads
RVA 00000664, ID 2398, Teb:0000000000C12000
RVA 00000694, ID 43B4, Teb:0000000000C16000
RVA 000006C4, ID 1CF4, Teb:0000000000C1A000
RVA 000006F4, ID 32A4, Teb:0000000000C1E000
Stream 1: type ThreadInfoListStream (17), size 0000010C, RVA 00000724
RVA 00000730, ID 2398
RVA 00000770, ID 43B4
RVA 000007B0, ID 1CF4
RVA 000007F0, ID 32A4
Stream 2: type ModuleListStream (4), size 00000220, RVA 00000830
5 modules
RVA 00000834, 00f20000 - 00f3e000: 'C:\tp\dumpfile_format\test.exe', ......
RVA 000008A0, 77da0000 - 77f4f000: 'C:\Windows\System32\ntdll.dll', ......
RVA 0000090C, 76610000 - 76700000: 'C:\Windows\System32\kernel32.dll', ......
RVA 00000978, 77920000 - 77b93000: 'C:\Windows\System32\KERNELBASE.dll', ......
RVA 000009E4, 77d90000 - 77d9a000: 'C:\Windows\System32\wow64cpu.dll', ......
Stream 3: type Memory64ListStream (9), size 000003F0, RVA 000047AD
62 memory ranges
RVA 0x4B9D BaseRva
range# RVA Address Size
0 00004B9D 00a90000 00003000
1 00007B9D 00aa0000 00010000
2 00017B9D 00ab0000 00003000
......
59 006E4B9D 7fd40000 00001000
60 006E5B9D 7ffe0000 00001000
61 006E6B9D 7ffea000 00001000
Total memory: 6e3000
Stream 4: type MemoryInfoListStream (16), size 00001780, RVA 0000302D
Stream 5: type SystemInfoStream (7), size 00000038, RVA 000000D4
ProcessorArchitecture 0000 (PROCESSOR_ARCHITECTURE_INTEL)
ProcessorLevel 0006
ProcessorRevision 8E0C
NumberOfProcessors 08
MajorVersion 0000000A
MinorVersion 00000000
BuildNumber 0000585D (22621)
PlatformId 00000002 (VER_PLATFORM_WIN32_NT)
CSDVersionRva 00000CD4
Length: 0
Product: WinNt, suite: SingleUserTS Personal
Stream 6: type MiscInfoStream (15), size 00000554, RVA 0000010C
Stream 7: type HandleDataStream (12), size 00000A60, RVA 000025CD
66 descriptors, header size is 16, descriptor size is 40
Handle(0000000000000004,"File","")
Handle(0000000000000008,"Event","")
Handle(000000000000000C,"Event","")
Handle(0000000000000010,"Event","")
Handle(0000000000000014,"WaitCompletionPacket","")
Handle(0000000000000018,"IoCompletion","")
......
Handle(00000000000000E4,"Thread","")
Handle(00000000000000E8,"Thread","")
Handle(00000000000000EC,"Thread","")
Handle(0000000000000100,"File","")
......
Stream 8: type SystemMemoyInfoStream (21), size 000001EC, RVA 00000A50
Revision : 1
Flags : 0xf
BasicInfo
TimerResolution : 156,250
PageSize : 0x1000
NumberOfPhysicalPages : 2,048,069
......
PerfInfo
IdleProcessTime : 15,127,742,812,500
IoReadTransferCount : 160,141,277,450
IoWriteTransferCount : 110,970,695,188
......
Stream 9: type ProcessVmCountersStream (22), size 00000098, RVA 00000C3C
Revision : 2
Process Counters
PageFaultCount : 1,086
PeakWorkingSetSize : 0x404000
WorkingSetSize : 0x404000
......
Stream 10: type UnusedStream (0), size 00000000, RVA 00000000
Stream 11: type UnusedStream (0), size 00000000, RVA 00000000
Stream 12: type UnusedStream (0), size 00000000, RVA 00000000
Stream 13: type UnusedStream (0), size 00000000, RVA 00000000
Stream 14: type UnusedStream (0), size 00000000, RVA 00000000
......
Finished dump check
先頭は「ヘッダ (MINIDUMP_HEADER)」です。ストリームの数 (NumberOfStreams) やフラグ (Flags) の意味も表示されています。
その後、各種ストリームの情報が続きます。
たとえば、最初のストリーム (Stream 0) については、ストリームの種類が ThreadListStream (3) であること、スレッド数は 4 であること、各スレッドのスレッド ID がそれぞれ 0x2398, 0x43B4, 0x1CF4, 0x32A4 であることなどが表示されています。
おわりに
MINIDUMP_HEADER 構造体の定義を起点に、ミニダンプのフォーマットを見てみました。
また、DumpChk コマンドでダンプファイルの構造が表示できることを確認しました。
参考文献
[1] マイクロソフト「MINIDUMP_HEADER 構造体」
https://learn.microsoft.com/ja-jp/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_header(※ 機械翻訳の日本語。画面下部にある[日本語]のクリックで原文の英語などに変更可。)