ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2

ANTICHAT — форум по информационной безопасности, OSINT и технологиям

ANTICHAT — русскоязычное сообщество по безопасности, OSINT и программированию. Форум ранее работал на доменах antichat.ru, antichat.com и antichat.club, и теперь снова доступен на новом адресе — forum.antichat.xyz.
Форум восстановлен и продолжает развитие: доступны архивные темы, добавляются новые обсуждения и материалы.
⚠️ Старые аккаунты восстановить невозможно — необходимо зарегистрироваться заново.
Вернуться   Форум АНТИЧАТ > Программирование > С/С++, C#, Delphi, .NET, Asm
   
 
 
Опции темы Поиск в этой теме Опции просмотра

Описание формата Dmp крешдампов и написание простейшего дампера
  #1  
Старый 07.09.2007, 19:38
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию Описание формата Dmp крешдампов и написание простейшего дампера

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.
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Описание .htaccess LoFFi Чужие Статьи 9 29.03.2008 05:14



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ