_Great_
07.09.2007, 19:38
Article: Описание формата DMP крешдампов и написание простейшего дампера
Author: Great
Date: 07.09.2007
Lang: C++
I. Структура крешдампа
Не так давно я зарелизил свой аналог livekd под названием gr8lkd, который является драйвером-фильтром файловой системы, аттачащийся к диску C:\ и создающий на нем виртуальный креш дамп, перехватывая и обрабатывая все запросы к нему.
Теперь у меня возникло желание описать формат крешдампов в Windows - .DMP. В интернете можно найти всего 1 страницу, и то на английском, с каким-то не очень подробным и довольно кривым описанием)
Я постараюсь описать этот формат наиболее полно и со всех сторон. Все нижеизложенное основывается только лишь на собственных исследованиях дизассемблерного листинга ядра, а именно функции IoWriteCrashDump() и связанных с ней.
Приступим...
Во-первых, немного о крешдампах и о том, как они генерируются. Когда происходит что-то плохое в ядре, например, освобождение уже освобожденной памяти, и когда ExFreePool (функция освобождения пула) это обнаруживает, продолжать работу может быть небезопасно, поэтому систему решается аварийно завершить. ExFreePool вызывает KeBugCheckEx( KeBugCheck2, KeBugCheck3, в зависимости от версии Windows, в первом случае это экспортируемая документированная функция, показывающая синий экран, во втором и третьем - внутренние, делающие тоже самое), которая показывает синий экран BAD_POOL_CALLER и вызывает IoWriteCrashDump для записи крешдампа. Крешдамп записывается аккурат в сектора диска, занимаемые файлом подкачки (для этого заблаговременно вызывается FSCTL_GET_RETRIEVAL_POINTERS для получения карты размещения файла подкачки), потому что на данном этапе вызывать драйвер файловой системы небезопасно - вдруг он и есть причина сбоя. Поэтому используется специальный драйвер, который сбрасывает дамп в файл подкачки. При следующей загрузке он достается оттуда и сохраняется по пути, прописанному в реестре.
Как известно, в Windows существует три типа креш-дампов:
1) Minidump или, как он называется в ядре, triage dump. Размер такого дампа мал (обычно 64к) и они содержат минимум информации о системе в момент краха. Такой тип дампов стоит по умолчанию, но, к сожалению, он не подходит для обычных разработок в области ядра операционной системы, поэтому мы двинемся дальше и посмотрим на следующий тип:
2) Kernel Memory Dump или, как он называется в ядре, summary dump. В этот дамп включаются только страницы памяти ядра, причем не все, а только те, что необходимы для анализа краха. Этот тип наиболее подходит для обычного анализа причины краха системы, т.к. в дамп включаются код и данные ядра, всех загруженных драйверов и системных структур. Файла подкачки должно быть достаточно для размещения всех этих данных. Примерный размер его трудно предсказать, у меня он обычно составляет 1/8 от размера физической памяти.
3) Full Memory Dump. В такой дамп последовательно включаются все страницы физической памяти. Файла подкачки должно быть достаточно, чтобы вместить все страницы памяти.
Рассмотрим сперва структуру, общую для всех дампов - dump header page, это первая страница файла дампа, первые 0x1000 (4096) байт. Структура этой страницы такова - сперва вся страница заполняется заполнителем "PAGE" (байты 'EGAP'), а потом по нужным смещениям записываются данные.
Сперва идет структура, идентифицирующая сам крешдамп:
typedef struct _DUMP_HEADER {
/* 00 */ ULONG Signature;
/* 04 */ ULONG ValidDump;
/* 08 */ ULONG MajorVersion;
/* 0c */ ULONG MinorVersion;
/* 10 */ ULONG DirectoryTableBase;
/* 14 */ PULONG PfnDataBase;
/* 18 */ PLIST_ENTRY PsLoadedModuleList;
/* 1c */ PLIST_ENTRY PsActiveProcessHead;
/* 20 */ ULONG MachineImageType;
/* 24 */ ULONG NumberProcessors;
/* 28 */ ULONG BugCheckCode;
/* 2c */ ULONG BugCheckParameter1;
/* 30 */ ULONG BugCheckParameter2;
/* 34 */ ULONG BugCheckParameter3;
/* 38 */ ULONG BugCheckParameter4;
/* 3c */ CHAR VersionUser[32];
/* 5c */ BYTE PaeEnabled;
BYTE NotUsed[3];
/* 60 */ PVOID KdDebuggerDataBlock;
} DUMP_HEADER, *PDUMP_HEADER;
Signature
Это поле не заполняется и в нем оставляются лежать байты 'EGAP'
ValidDump
Сигнатура дампа - "DUMP" ('PMUD')
MajorVersion
0x0F если Free build
0x0C если Checked build
MinorVersion
Build number системы
DirectoryTableBase
Значение CR3 в момент краха системы - физический адрес каталога страниц
PfnDatabase
Виртуальный адрес MmPfnDatabase - база данных фреймов страниц (PFN)
PsLoadedModuleList
Виртуальный адрес PsLoadedModuleList - список загруженных модулей
PsActiveProcessHead
Виртуальный адрес PsActiveProcessHead - список активных процессов
MachineImageType
на x86 это 0x14C, остальные константы можно посмотреть в winnt.h
NumberProcessors
Число процессоров системы, берется из KeKeNumberProcessors
BugCheckCode
Стоп-код ошибки
BugCheckParameter1
BugCheckParameter2
BugCheckParameter3
BugCheckParameter4
Параметры ошибки
VersionUser
Версия чего-то, так и не выяснил чего. Первый байт обычно записан в 0, остальное не заполнено
PaeEnabled
=1 если включена поддержка Physical Address Extensions (PAE), =0 если выключена
KdDebuggerDataBlock
Виртуальный адрес очень важной структуры KdDebuggerDataBlock, описание которой я дам позже.
Вот так нехитро устроено начало дампа. Дальше по определенным смещениям запихнуты еще несколько блоков данных, для начала определим некоторые константы:
// Data Blocks
#define DH_PHYSICAL_MEMORY_BLOCK 25
#define DH_CONTEXT_RECORD 200
#define DH_EXCEPTION_RECORD 500
#define DH_DUMP_TYPE 994
#define DH_CALLBACKS_STATUS 996 // (поле необязательно)
#define DH_PRODUCT_TYPE 997 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_SUITE_MASK 998 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_REQUIRED_DUMP_SPACE 1000
#define DH_INTERRUPT_TIME 1006 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_SYSTEM_TIME 1008 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
Первая страница рассматривается как массив DWORD-ов и эти константы определяют индексы в этом массиве, где содержатся данные. То есть, другими словами, на си:
ULONG* blocks = (ULONG*) HeaderPage;
//&blocks[DH_xxx] - адрес соответствующего блока
Рассмотрим теперь эти блоки по отдельности:
DH_PHYSICAL_MEMORY_BLOCK
Тут содержится описатель физической памяти, структура PHYSICAL_MEMORY_DESCRIPTOR:
typedef unsigned long PFN_NUMBER;
typedef struct _PHYSICAL_MEMORY_RUN {
PFN_NUMBER BasePage;
PFN_NUMBER PageCount;
} PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;
typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
ULONG NumberOfRuns;
PFN_NUMBER NumberOfPages;
PHYSICAL_MEMORY_RUN Run[1];
} PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;
Она, вместе с массивами Run, описывает доступную физическую память. Обычно в системе есть три-четыре Run'а - сплошных набора физических страниц.
Например на моей системе (512МБ физической памяти) они выглядят так:
PPHYSICAL_MEMORY_DESCRIPTOR:
NumberOfRuns = 0x00000003 NumberOfPages = 0x0001ff8d
PPHYSICAL_MEMORY_RUN[0]: BasePage = 0x00000001 PageCount = 0x0000009e
PPHYSICAL_MEMORY_RUN[1]: BasePage = 0x00000100 PageCount = 0x00000eff
PPHYSICAL_MEMORY_RUN[2]: BasePage = 0x00001000 PageCount = 0x0001eff0
То есть у меня доступны физические страницы с pfn'ами (номерами, другими словами) от 1 до 0x9e, от 0x100 до 0xeff и от 0x1000 до 0x1eff0.
Размер данного блока равен sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) + sizeof(PHYSICAL_MEMORY_RUN) * NumberOfRuns
DH_CONTEXT_RECORD
Тут хранится структура CONTEXT, хранящая в себе контекст потока, вызвавшего ошибку. Дополнительных комментариев, думаю, не нужно.
DH_EXCEPTION_RECORD
Аналогично предыдущему, здесь хранится структура EXCEPTION_RECORD с информацией об исключении, если она была доступна.
DH_DUMP_TYPE
Здесь хранится дворд, определяющий непосредственно тип данного дампа - triage, summary или full dump. Соответствующие значения этого поля:
// Dump types
#define DUMP_TYPE_TRIAGE 4
#define DUMP_TYPE_SUMMARY 2
#define DUMP_TYPE_FULL 1
DH_CALLBACKS_STATUS
Тут сохраняется NTSTATUS от вызова BugcheckCallbacks
DH_PRODUCT_TYPE
Тут сохраняется тип системы, описываемый перечислением enum _NT_PRODUCT_TYPE {
NtProductWinNt = 0x1,
NtProductLanManNt = 0x2,
NtProductServer = 0x3,
};
DH_SUITE_MASK
Здесь сохраняется поле KUSER_SHARED_DATA->SuiteMask, и, честно признаться, я не знаю, что оно означает.
DH_REQUIRED_DUMP_SPACE
Здесь хранится LARGE_INTEGER, содержащий в себе полный размер дампа в байтах.
DH_INTERRUPT_TIME
Назначение этого поля мне неизвестно, берется из KUSER_SHARED_DATA->InterruptTime
DH_SYSTEM_TIME
Это поле берется из KUSER_SHARED_DATA->SystemTime и содержит, насколько мне известно, текущий аптайм.
Вот, собственно, и всё с начальной страницей дампа.
Дальнейшая структура дампа сильно зависит от его типа:
1. Minidump (triage dump)
Данный тип дампа не особо интересен, поэтому просто приведу частичную структуру TRIAGE_DUMP_HEADER без особых комментариев (анализ ее я еще не закончил), отметив лишь, что она содержит оффсеты некоторых структур, которые следуют дальше в сыром виде:
// Triage dump header
typedef struct _TRIAGE_DUMP_HEADER {
ULONG ServicePackBuild; // 00
ULONG SizeOfDump; // 04
ULONG ValidOffset; // 08
ULONG ContextOffset; // 0c
ULONG ExceptionOffset; // 10
ULONG MmOffset; // 14
ULONG UnloadedDriversOffset; // 18
ULONG PrcbOffset; // 1c
ULONG ProcessOffset; // 20
ULONG ThreadOffset; // 24
ULONG Unknown1; // 28
ULONG Unknown2; // 2c
ULONG DriverListOffset; // 30
ULONG DriverCount; // 34
...
ULONG TriageOptions; // 44
} TRIAGE_DUMP_HEADER, *PTRIAGE_DUMP_HEADER;
2. Kernel memory dump (Summary dump)
Вторая страница дампа начинается со структуры // Kernel summary dump header
typedef struct _SUMMARY_DUMP_HEADER {
ULONG Signature1; // 00
ULONG ValidDump; // 04
ULONG Signature2; // 08
ULONG HeaderSize; // 0c
ULONG BitmapSize; // 10
ULONG Pages; // 14
ULONG Unknown3; // 18
ULONG Unknown4; // 1c
} SUMMARY_DUMP_HEADER, *PSUMMARY_DUMP_HEADER;
Signature1
Signature2
Эти поля содержат сигнатуру "SDMP", точнее они просто не заполняются, а вся структура заполняется этими двордами перед заполнением.
ValidDump
Это поле содержит сигнатуру "DUMP"
HeaderSize
Полный размер заголовка дампа
BitmapSize
Размер битовой карты, следующей непосредственно за этой структурой - о ней позже.
Pages
Количество страниц памяти ядра, включенных в дамп (совпадает с числом всех установленных битов битовой карты).
Непосредственно за этой структурой следует битовая карта, число бит в ней равно числу физических страниц в системе, а установленные соответсвующие биты сообщают, включена ли конкретная страница в дамп, или нет. Для работы с такой битовой картой предназначены структура:
typedef struct _RTL_BITMAP {
ULONG SizeOfBitMap; // Number of bits in bit map
PULONG Buffer; // Pointer to the bit map itself
} RTL_BITMAP;
typedef RTL_BITMAP *PRTL_BITMAP; и функции RtlInitializeBitMap, RtlClearAllBits, RtlSetAllBits, RtlFindClearBits, RtlFindSetBits, RtlCheckBit и другие.
Во второй части статьи я приведу пример простейшего анализатора крешдампа и покажу, как работать с этой картой.
3. Full dump
После заглавной страницы дампы данного типа содержат подряд все физические страницы из всех memory runs, начиная с 0 и заканчивая NumberOfRuns-1.
Author: Great
Date: 07.09.2007
Lang: C++
I. Структура крешдампа
Не так давно я зарелизил свой аналог livekd под названием gr8lkd, который является драйвером-фильтром файловой системы, аттачащийся к диску C:\ и создающий на нем виртуальный креш дамп, перехватывая и обрабатывая все запросы к нему.
Теперь у меня возникло желание описать формат крешдампов в Windows - .DMP. В интернете можно найти всего 1 страницу, и то на английском, с каким-то не очень подробным и довольно кривым описанием)
Я постараюсь описать этот формат наиболее полно и со всех сторон. Все нижеизложенное основывается только лишь на собственных исследованиях дизассемблерного листинга ядра, а именно функции IoWriteCrashDump() и связанных с ней.
Приступим...
Во-первых, немного о крешдампах и о том, как они генерируются. Когда происходит что-то плохое в ядре, например, освобождение уже освобожденной памяти, и когда ExFreePool (функция освобождения пула) это обнаруживает, продолжать работу может быть небезопасно, поэтому систему решается аварийно завершить. ExFreePool вызывает KeBugCheckEx( KeBugCheck2, KeBugCheck3, в зависимости от версии Windows, в первом случае это экспортируемая документированная функция, показывающая синий экран, во втором и третьем - внутренние, делающие тоже самое), которая показывает синий экран BAD_POOL_CALLER и вызывает IoWriteCrashDump для записи крешдампа. Крешдамп записывается аккурат в сектора диска, занимаемые файлом подкачки (для этого заблаговременно вызывается FSCTL_GET_RETRIEVAL_POINTERS для получения карты размещения файла подкачки), потому что на данном этапе вызывать драйвер файловой системы небезопасно - вдруг он и есть причина сбоя. Поэтому используется специальный драйвер, который сбрасывает дамп в файл подкачки. При следующей загрузке он достается оттуда и сохраняется по пути, прописанному в реестре.
Как известно, в Windows существует три типа креш-дампов:
1) Minidump или, как он называется в ядре, triage dump. Размер такого дампа мал (обычно 64к) и они содержат минимум информации о системе в момент краха. Такой тип дампов стоит по умолчанию, но, к сожалению, он не подходит для обычных разработок в области ядра операционной системы, поэтому мы двинемся дальше и посмотрим на следующий тип:
2) Kernel Memory Dump или, как он называется в ядре, summary dump. В этот дамп включаются только страницы памяти ядра, причем не все, а только те, что необходимы для анализа краха. Этот тип наиболее подходит для обычного анализа причины краха системы, т.к. в дамп включаются код и данные ядра, всех загруженных драйверов и системных структур. Файла подкачки должно быть достаточно для размещения всех этих данных. Примерный размер его трудно предсказать, у меня он обычно составляет 1/8 от размера физической памяти.
3) Full Memory Dump. В такой дамп последовательно включаются все страницы физической памяти. Файла подкачки должно быть достаточно, чтобы вместить все страницы памяти.
Рассмотрим сперва структуру, общую для всех дампов - dump header page, это первая страница файла дампа, первые 0x1000 (4096) байт. Структура этой страницы такова - сперва вся страница заполняется заполнителем "PAGE" (байты 'EGAP'), а потом по нужным смещениям записываются данные.
Сперва идет структура, идентифицирующая сам крешдамп:
typedef struct _DUMP_HEADER {
/* 00 */ ULONG Signature;
/* 04 */ ULONG ValidDump;
/* 08 */ ULONG MajorVersion;
/* 0c */ ULONG MinorVersion;
/* 10 */ ULONG DirectoryTableBase;
/* 14 */ PULONG PfnDataBase;
/* 18 */ PLIST_ENTRY PsLoadedModuleList;
/* 1c */ PLIST_ENTRY PsActiveProcessHead;
/* 20 */ ULONG MachineImageType;
/* 24 */ ULONG NumberProcessors;
/* 28 */ ULONG BugCheckCode;
/* 2c */ ULONG BugCheckParameter1;
/* 30 */ ULONG BugCheckParameter2;
/* 34 */ ULONG BugCheckParameter3;
/* 38 */ ULONG BugCheckParameter4;
/* 3c */ CHAR VersionUser[32];
/* 5c */ BYTE PaeEnabled;
BYTE NotUsed[3];
/* 60 */ PVOID KdDebuggerDataBlock;
} DUMP_HEADER, *PDUMP_HEADER;
Signature
Это поле не заполняется и в нем оставляются лежать байты 'EGAP'
ValidDump
Сигнатура дампа - "DUMP" ('PMUD')
MajorVersion
0x0F если Free build
0x0C если Checked build
MinorVersion
Build number системы
DirectoryTableBase
Значение CR3 в момент краха системы - физический адрес каталога страниц
PfnDatabase
Виртуальный адрес MmPfnDatabase - база данных фреймов страниц (PFN)
PsLoadedModuleList
Виртуальный адрес PsLoadedModuleList - список загруженных модулей
PsActiveProcessHead
Виртуальный адрес PsActiveProcessHead - список активных процессов
MachineImageType
на x86 это 0x14C, остальные константы можно посмотреть в winnt.h
NumberProcessors
Число процессоров системы, берется из KeKeNumberProcessors
BugCheckCode
Стоп-код ошибки
BugCheckParameter1
BugCheckParameter2
BugCheckParameter3
BugCheckParameter4
Параметры ошибки
VersionUser
Версия чего-то, так и не выяснил чего. Первый байт обычно записан в 0, остальное не заполнено
PaeEnabled
=1 если включена поддержка Physical Address Extensions (PAE), =0 если выключена
KdDebuggerDataBlock
Виртуальный адрес очень важной структуры KdDebuggerDataBlock, описание которой я дам позже.
Вот так нехитро устроено начало дампа. Дальше по определенным смещениям запихнуты еще несколько блоков данных, для начала определим некоторые константы:
// Data Blocks
#define DH_PHYSICAL_MEMORY_BLOCK 25
#define DH_CONTEXT_RECORD 200
#define DH_EXCEPTION_RECORD 500
#define DH_DUMP_TYPE 994
#define DH_CALLBACKS_STATUS 996 // (поле необязательно)
#define DH_PRODUCT_TYPE 997 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_SUITE_MASK 998 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_REQUIRED_DUMP_SPACE 1000
#define DH_INTERRUPT_TIME 1006 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
#define DH_SYSTEM_TIME 1008 // (поле необязательно, значение берется из KUSER_SHARED_DATA)
Первая страница рассматривается как массив DWORD-ов и эти константы определяют индексы в этом массиве, где содержатся данные. То есть, другими словами, на си:
ULONG* blocks = (ULONG*) HeaderPage;
//&blocks[DH_xxx] - адрес соответствующего блока
Рассмотрим теперь эти блоки по отдельности:
DH_PHYSICAL_MEMORY_BLOCK
Тут содержится описатель физической памяти, структура PHYSICAL_MEMORY_DESCRIPTOR:
typedef unsigned long PFN_NUMBER;
typedef struct _PHYSICAL_MEMORY_RUN {
PFN_NUMBER BasePage;
PFN_NUMBER PageCount;
} PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;
typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
ULONG NumberOfRuns;
PFN_NUMBER NumberOfPages;
PHYSICAL_MEMORY_RUN Run[1];
} PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;
Она, вместе с массивами Run, описывает доступную физическую память. Обычно в системе есть три-четыре Run'а - сплошных набора физических страниц.
Например на моей системе (512МБ физической памяти) они выглядят так:
PPHYSICAL_MEMORY_DESCRIPTOR:
NumberOfRuns = 0x00000003 NumberOfPages = 0x0001ff8d
PPHYSICAL_MEMORY_RUN[0]: BasePage = 0x00000001 PageCount = 0x0000009e
PPHYSICAL_MEMORY_RUN[1]: BasePage = 0x00000100 PageCount = 0x00000eff
PPHYSICAL_MEMORY_RUN[2]: BasePage = 0x00001000 PageCount = 0x0001eff0
То есть у меня доступны физические страницы с pfn'ами (номерами, другими словами) от 1 до 0x9e, от 0x100 до 0xeff и от 0x1000 до 0x1eff0.
Размер данного блока равен sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) + sizeof(PHYSICAL_MEMORY_RUN) * NumberOfRuns
DH_CONTEXT_RECORD
Тут хранится структура CONTEXT, хранящая в себе контекст потока, вызвавшего ошибку. Дополнительных комментариев, думаю, не нужно.
DH_EXCEPTION_RECORD
Аналогично предыдущему, здесь хранится структура EXCEPTION_RECORD с информацией об исключении, если она была доступна.
DH_DUMP_TYPE
Здесь хранится дворд, определяющий непосредственно тип данного дампа - triage, summary или full dump. Соответствующие значения этого поля:
// Dump types
#define DUMP_TYPE_TRIAGE 4
#define DUMP_TYPE_SUMMARY 2
#define DUMP_TYPE_FULL 1
DH_CALLBACKS_STATUS
Тут сохраняется NTSTATUS от вызова BugcheckCallbacks
DH_PRODUCT_TYPE
Тут сохраняется тип системы, описываемый перечислением enum _NT_PRODUCT_TYPE {
NtProductWinNt = 0x1,
NtProductLanManNt = 0x2,
NtProductServer = 0x3,
};
DH_SUITE_MASK
Здесь сохраняется поле KUSER_SHARED_DATA->SuiteMask, и, честно признаться, я не знаю, что оно означает.
DH_REQUIRED_DUMP_SPACE
Здесь хранится LARGE_INTEGER, содержащий в себе полный размер дампа в байтах.
DH_INTERRUPT_TIME
Назначение этого поля мне неизвестно, берется из KUSER_SHARED_DATA->InterruptTime
DH_SYSTEM_TIME
Это поле берется из KUSER_SHARED_DATA->SystemTime и содержит, насколько мне известно, текущий аптайм.
Вот, собственно, и всё с начальной страницей дампа.
Дальнейшая структура дампа сильно зависит от его типа:
1. Minidump (triage dump)
Данный тип дампа не особо интересен, поэтому просто приведу частичную структуру TRIAGE_DUMP_HEADER без особых комментариев (анализ ее я еще не закончил), отметив лишь, что она содержит оффсеты некоторых структур, которые следуют дальше в сыром виде:
// Triage dump header
typedef struct _TRIAGE_DUMP_HEADER {
ULONG ServicePackBuild; // 00
ULONG SizeOfDump; // 04
ULONG ValidOffset; // 08
ULONG ContextOffset; // 0c
ULONG ExceptionOffset; // 10
ULONG MmOffset; // 14
ULONG UnloadedDriversOffset; // 18
ULONG PrcbOffset; // 1c
ULONG ProcessOffset; // 20
ULONG ThreadOffset; // 24
ULONG Unknown1; // 28
ULONG Unknown2; // 2c
ULONG DriverListOffset; // 30
ULONG DriverCount; // 34
...
ULONG TriageOptions; // 44
} TRIAGE_DUMP_HEADER, *PTRIAGE_DUMP_HEADER;
2. Kernel memory dump (Summary dump)
Вторая страница дампа начинается со структуры // Kernel summary dump header
typedef struct _SUMMARY_DUMP_HEADER {
ULONG Signature1; // 00
ULONG ValidDump; // 04
ULONG Signature2; // 08
ULONG HeaderSize; // 0c
ULONG BitmapSize; // 10
ULONG Pages; // 14
ULONG Unknown3; // 18
ULONG Unknown4; // 1c
} SUMMARY_DUMP_HEADER, *PSUMMARY_DUMP_HEADER;
Signature1
Signature2
Эти поля содержат сигнатуру "SDMP", точнее они просто не заполняются, а вся структура заполняется этими двордами перед заполнением.
ValidDump
Это поле содержит сигнатуру "DUMP"
HeaderSize
Полный размер заголовка дампа
BitmapSize
Размер битовой карты, следующей непосредственно за этой структурой - о ней позже.
Pages
Количество страниц памяти ядра, включенных в дамп (совпадает с числом всех установленных битов битовой карты).
Непосредственно за этой структурой следует битовая карта, число бит в ней равно числу физических страниц в системе, а установленные соответсвующие биты сообщают, включена ли конкретная страница в дамп, или нет. Для работы с такой битовой картой предназначены структура:
typedef struct _RTL_BITMAP {
ULONG SizeOfBitMap; // Number of bits in bit map
PULONG Buffer; // Pointer to the bit map itself
} RTL_BITMAP;
typedef RTL_BITMAP *PRTL_BITMAP; и функции RtlInitializeBitMap, RtlClearAllBits, RtlSetAllBits, RtlFindClearBits, RtlFindSetBits, RtlCheckBit и другие.
Во второй части статьи я приведу пример простейшего анализатора крешдампа и покажу, как работать с этой картой.
3. Full dump
После заглавной страницы дампы данного типа содержат подряд все физические страницы из всех memory runs, начиная с 0 и заканчивая NumberOfRuns-1.