ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > Программирование > С/С++, C#, Delphi, .NET, Asm
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

Описание формата Dmp крешдампов и написание простейшего дампера
  #1  
Старый 07.09.2007, 19:38
Аватар для _Great_
_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.
 
Ответить с цитированием

  #2  
Старый 07.09.2007, 19:39
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

II. Написание анализатора дампа

Теперь у нас есть достаточно знаний, чтобы написать простейший анализатор крешдампа.
Загруженный дамп в нашем анализаторе будет представлен структурой:
Код:
// Mapped crash dump descriptor
struct MappedCrashDump {
	HANDLE hFile;
	HANDLE hMapping;
	union {
		PBYTE lpMapping;
		PDUMP_HEADER DumpHeader;
		ULONG* DataBlocks;
	};
	ULONG *DumpType;
	ULONG *DumpFlags;
	PLARGE_INTEGER DumpSize;
	PPHYSICAL_MEMORY_DESCRIPTOR PhysicalMemoryDescriptor;
	PCONTEXT Context;
	union {
		// Summary dump type
		struct {
			PSUMMARY_DUMP_HEADER pSummaryDumpHeader;
			RTL_BITMAP KernelMemoryMap;
		};

		// Triage dump type
		PTRIAGE_DUMP_HEADER pTriageDumpHeader;
	};
};
План действий будет таков - мы открываем файл, создаем объект проекции и проецируем первые 10 страниц файла дампа, остальное будет проецироваться по мере необходимости. Для реализации этого напишем функцию MapDumpPages, которая будет проецировать вид файла, если он еще не спроецирован:

Код:
//
// This routine maps dump page at given file offset (DesiredAddress is the sum of BaseMapping and desired offset)
//

PVOID MapDumpPage( HANDLE hMapping, PBYTE BaseMapping, PBYTE DesiredAddress )
{
	ULONG FileOffset = (ULONG) ( ( (ULONG_PTR)DesiredAddress-(ULONG_PTR)BaseMapping ) & 0xFFFF0000 );
	PVOID Base = (PVOID)(ULONG_PTR) ( ((ULONG)(ULONG_PTR)DesiredAddress)&0xFFFF0000 );
	MEMORY_BASIC_INFORMATION mbi = {0};
	PVOID Ret = 0;

	VirtualQuery( DesiredAddress, &mbi, sizeof(mbi) );

	if( mbi.State != MEM_COMMIT ) {
		Ret = MapViewOfFileEx(	hMapping,
								FILE_MAP_READ,
								0,
								FileOffset,
								0x10000,
								Base
								);
		if( Ret == Base ) {
			return DesiredAddress;
		}

		// Loaded at different address. Fail
		UnmapViewOfFile( Ret );
		return NULL;
	}

	return DesiredAddress;
}
Дальше мы реализуем функцию, которая получит страницу из summary-дампа по её физическому адресу в момент краха:
Код:
PBYTE GetSummaryDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
{
	PBYTE StartingAddress = (PBYTE)(ULONG_PTR) ( (ULONG)(ULONG_PTR)CrashDump->pSummaryDumpHeader + CrashDump->pSummaryDumpHeader->HeaderSize - 0x1000 );
	ULONG NumberOfPage = (ULONG) (AddressAtCrashTime / 0x1000);
	ULONG OffsetInPage = (ULONG) (AddressAtCrashTime & 0xFFF);

	if( NumberOfPage >= CrashDump->KernelMemoryMap.SizeOfBitMap )
		return NULL;

    if( RtlCheckBit( &CrashDump->KernelMemoryMap, NumberOfPage ) == 0 )
		return NULL; // not included in dump

	// Calculate page number in dump
	ULONG PageNumber = 0;

	for( ULONG i=0; i<CrashDump->KernelMemoryMap.SizeOfBitMap; i++ ) {
		if( i == NumberOfPage ) {
			PBYTE Result = StartingAddress + PageNumber*0x1000 + OffsetInPage;

			return (PBYTE) MapDumpPage( CrashDump->hMapping, CrashDump->lpMapping, Result );
		}

		if( RtlCheckBit( &CrashDump->KernelMemoryMap, i ) ) {
			PageNumber++;
		}
	}

	return NULL;
}
Тут реализована работа с битовой картой, чтобы узнать, присутствует ли страница в дампе, и чтобы узнать ее номер на диске. После этого она проецируется с диска в оперативную память, если еще не спроецирована.

Дальше нужно определить аналогичную функцию для полного дампа. Для начала нужно научиться конвертировать PFN страницы в памяти в PFN страницы на диске, и наоборот. С этим справятся следующие две функции:
Код:
PFN_NUMBER GetPhysicalPFN( PPHYSICAL_MEMORY_DESCRIPTOR ppmd, PFN_NUMBER DumpPFN )
{
	PFN_NUMBER iBuffPage, iPage = iBuffPage = DumpPFN;

	if( iBuffPage >= ppmd->NumberOfPages )
		return -1;

	// Calculate page in memory
	ULONG NumberOfRunsRequired = 0;
	PFN_NUMBER TotalPageCount = 0;

	for( ; NumberOfRunsRequired<ppmd->NumberOfRuns; NumberOfRunsRequired++ )
	{
		PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;

		if( iBuffPage >= TotalPageCount &&
			iBuffPage < TotalPageCount + Runs[NumberOfRunsRequired].PageCount )
			break;

		TotalPageCount += (Runs[NumberOfRunsRequired].PageCount);
	}

	PFN_NUMBER PreviousEnd = 0;
	NumberOfRunsRequired ++;

	for( ULONG i=0; i<NumberOfRunsRequired; i++ )
	{
		PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;

		iPage += (Runs[i].BasePage - PreviousEnd);
		PreviousEnd = Runs[i].BasePage + Runs[i].PageCount;
	}

	return iPage;
}

PFN_NUMBER GetDumpPFN( PPHYSICAL_MEMORY_DESCRIPTOR ppmd, PFN_NUMBER PhysicalPFN )
{
	for( int run=0; run<ppmd->NumberOfRuns; run++ )
	{
		PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;

		if( PhysicalPFN >= Runs[run].BasePage &&
			PhysicalPFN < (Runs[run].BasePage + Runs[run].PageCount) )
			break;
	}

	if( run == ppmd->NumberOfRuns )
		return -1;

	PFN_NUMBER iDumpPFN = 0;

	for( int i=0; i<run; i++ )
	{
		iDumpPFN += ppmd->Run[i].PageCount;
	}

	iDumpPFN += PhysicalPFN - ppmd->Run[i].BasePage;

	return iDumpPFN;
}
Дальше можно описать функцию, получающую страницу из дампа по ее физическому адресу на момент краха и общую функцию, получающую тип дампа и вызывающую нужную из двух функций получения адреса:
Код:
PBYTE GetCompleteDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
{
	PBYTE StartingAddress = (PBYTE)(ULONG_PTR)( (ULONG)(ULONG_PTR)CrashDump->lpMapping + 0x1000 );
	PFN_NUMBER PFN = (ULONG) (AddressAtCrashTime >> 12);
	ULONG OffsetInPage = (ULONG) (AddressAtCrashTime & 0xFFF);

	// Calculate page number in dump
	for( ULONG i=0; i<CrashDump->PhysicalMemoryDescriptor->NumberOfRuns; i++ ) {    
		PPHYSICAL_MEMORY_RUN Runs = (PPHYSICAL_MEMORY_RUN) &CrashDump->PhysicalMemoryDescriptor->Run;

		if( PFN >= Runs[i].BasePage && PFN < (Runs[i].BasePage + Runs[i].PageCount) ) {
		
			PFN_NUMBER DumpPFN = GetDumpPFN( CrashDump->PhysicalMemoryDescriptor, PFN );
			PBYTE Result = StartingAddress + DumpPFN*0x1000 + OffsetInPage;

			return (PBYTE) MapDumpPage( CrashDump->hMapping, CrashDump->lpMapping, Result );
		}
	}
	return NULL;
}

PBYTE GetDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
{
	if( *CrashDump->DumpType == DUMP_TYPE_SUMMARY ) {

		return GetSummaryDumpPagesByPhysicalAddress( CrashDump, AddressAtCrashTime );

	} else if( *CrashDump->DumpType == DUMP_TYPE_COMPLETE ) {

		return GetCompleteDumpPagesByPhysicalAddress( CrashDump, AddressAtCrashTime );

	} else {

		return NULL;
	}
}
Теперь нам нужно научиться транслировать виртуальные адреса в физические, и тогда мы полностью сможем вынуть данные по любому виртуальному адресу из дампа. Для начала определим структуры каталогов PDPE, PDE и PTE, которые нам могут понадобиться:
Код:
//
// Page Directory Entry
//

struct PDE {
	DWORD  Present:1;
	DWORD  ReadWrite:1;
	DWORD  UserSupervisor:1;
	DWORD  WriteThrough:1;
	DWORD  CacheDisabled:1;
	DWORD  Accessed:1;
	DWORD  Reserved:1;		// Dirty, ignored
	DWORD  PageSize:1;
	DWORD  GlobalPage:1;	// Ignored
	DWORD  Available:3;
	DWORD  Pte:19;
};


//
// Page Table Entry
//

struct PTE {
	DWORD  Present:1;
	DWORD  ReadWrite:1;
	DWORD  UserSupervisor:1;
	DWORD  WriteThrough:1;
	DWORD  CacheDisabled:1;
	DWORD  Accessed:1;
	DWORD  Dirty:1;
	DWORD  PageTableAttributeIndex:1;
	DWORD  GlobalPage:1;
	DWORD  Available:3;
	DWORD  PageFrameNumber:19;
};

// Virtual address
struct VIRTUAL_ADDRESS {
	DWORD  Offset:12;
	DWORD  Table:10;
	DWORD  Directory:10;
};


//
// Page Directory Entry in PAE mode
//

#define QWORD ULONGLONG

struct LongPDE {
	QWORD  Present:1;
	QWORD  ReadWrite:1;
	QWORD  UserSupervisor:1;
	QWORD  WriteThrough:1;
	QWORD  CacheDisabled:1;
	QWORD  Accessed:1;
	QWORD  Reserved:1;		// Dirty, ignored
	QWORD  PageSize:1;
	QWORD  GlobalPage:1;	// Ignored
	QWORD  Available:3;
	QWORD  Pte:24;
	QWORD  ReservedHigh:28;
};


//
// Page Table Entry in PAE mode
//

struct LongPTE {
	QWORD  Present:1;
	QWORD  ReadWrite:1;
	QWORD  UserSupervisor:1;
	QWORD  WriteThrough:1;
	QWORD  CacheDisabled:1;
	QWORD  Accessed:1;
	QWORD  Dirty:1;
	QWORD  PageTableAttributeIndex:1;
	QWORD  GlobalPage:1;
	QWORD  Available:3;
	QWORD  PageFrameNumber:24;
	QWORD  ReservedHigh:28;
};

//
// Page Directory Pointer Table (PAE mode only)
//

struct PDPE {
	QWORD  Present:1;
	QWORD  Reserved1:2;
	QWORD  WriteThough:1;
	QWORD  CacheDisabled:1;
	QWORD  Reserved2:4;
	QWORD  Available:3;
	QWORD  Pdt:24;
	QWORD  ReservedHigh:28;
};

// Virtual address (PAE)
struct PAE_VIRTUAL_ADDRESS {
	DWORD  Offset:12;
	DWORD  Table:9;
	DWORD  Directory:9;
	DWORD  DirectoryPointer:2;
};
Тут определены две серии структур - без включенного PAE и с включенным PAE.
Напомню, что в режиме PAE (Physical Address Extensions) адресация стала трехуровневой. Регистр CR3 больше не указывает на PDE, теперь он указывает на массив Page Directory Pointer Entries - PDPE, которых должно быть 4 штуки. Каждый из PDPE указывает на массив PDE, а уже PDE указывают на PTE. Структура виртуального адреса немного изменилась, у PDE и PTE "откусили" по одному биту в пользу двухбитового поля номера PDPE.

С учетом всего этого, функция трансляции адресов:
Код:
//
// Translates virtual address to physical address
//

ULONGLONG VirtualToPhysical( MappedCrashDump *CrashDump, ULONG VirtualAddress )
{
	ULONG CR3 = CrashDump->DumpHeader->DirectoryTableBase;

	CR3 &= 0xFFFFFFF0;   // clear flags in cr3

	if( CrashDump->DumpHeader->PaeEnabled )
	{
		// PAE enabled. Use 3-level addressing system: PDPE->PDE->PTE->Page
		PAE_VIRTUAL_ADDRESS va;

		*(ULONG*)&va = VirtualAddress;

		PDPE* dirptr = (PDPE*)GetDumpPagesByPhysicalAddress( CrashDump, CR3 );

		if( dirptr != NULL && dirptr[va.DirectoryPointer].Present )
		{
			LongPDE *dir = (LongPDE*)GetDumpPagesByPhysicalAddress( CrashDump, dirptr[va.DirectoryPointer].Pdt << 12 );

			if( dir != NULL && dir[va.Directory].Present )
			{
				LongPTE* tbl = (LongPTE*)GetDumpPagesByPhysicalAddress( CrashDump, dir[va.Directory].Pte << 12 );

				if( tbl != NULL && tbl[va.Table].Present )
				{
					return ( tbl[va.Table].PageFrameNumber << 12 ) | va.Offset;
				}
			}
		}
	}
	else
	{
		// PAE disabled. Use 2-level addressing system: PDE->PTE->Page
		VIRTUAL_ADDRESS va;
		*(ULONG*)&va = VirtualAddress;

		PDE *dir = (PDE*)GetDumpPagesByPhysicalAddress( CrashDump, CR3 );
		
		if( dir != NULL && dir[va.Directory].Present )
		{
			PTE* tbl = (PTE*)GetDumpPagesByPhysicalAddress( CrashDump, dir[va.Directory].Pte << 12 );

			if( tbl != NULL && tbl[va.Table].Present )
			{
				return ( tbl[va.Table].PageFrameNumber << 12 ) | va.Offset;
			}
		}
	}

	return NULL;
}

//
// Translates virtual address to physical address and loads that physical pages
//

PVOID RetrieveDumpData( MappedCrashDump* CrashDump, PVOID VirtualAddress )
{
	return GetDumpPagesByPhysicalAddress( CrashDump, VirtualToPhysical( CrashDump, (ULONG)(ULONG_PTR)VirtualAddress ) );
}
Всё готово. Напишем небольшой демонстрационный код для анализа крешдампа:
 
Ответить с цитированием

  #3  
Старый 07.09.2007, 19:39
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

Код:
struct CONST_DESCRIPTION {
	ULONG	Value;
	LPSTR	Desc;
#define DEFINE_STRING(x) { x, #x }
#define TABLE_END { 0, 0 }
};

CONST_DESCRIPTION MachineTypes[] = {
	DEFINE_STRING( IMAGE_FILE_MACHINE_I386 ),
	DEFINE_STRING( IMAGE_FILE_MACHINE_IA64 ),
	TABLE_END
};

CONST_DESCRIPTION DumpTypes[] = {
	DEFINE_STRING( DUMP_TYPE_TRIAGE ),
	DEFINE_STRING( DUMP_TYPE_SUMMARY ),
	DEFINE_STRING( DUMP_TYPE_FULL ),
	TABLE_END
};

// Bugcheck descriptions
typedef ULONG NTSTATUS;
#include "D:\Progs\driverdev\bcdesc.h"    // багчек коды бсодов, я не прилагаю этот файл, скажу лишь что там лежит функция BugCheckDescf, получающая имя бсода по его номеру.

// Получение имени константы по значению
LPSTR LookupConstDesc( CONST_DESCRIPTION* Table, ULONG Value )
{
	while( Table->Desc ) {
		if( Table->Value == Value ) {
			return Table->Desc;
		}
		Table ++;
	}
	return "(unknown)";
}

void ExtractDumpHeader( MappedCrashDump* CrashDump, char* saveto )
{
	HANDLE hFile = CreateFile( saveto, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0 );
	DWORD wr;
	WriteFile( hFile, CrashDump->lpMapping, 0x1000, &wr, 0 );
	SetEndOfFile( hFile );
	CloseHandle( hFile );
}


int AnalyseDump( char* filename )
{
	MappedCrashDump CrashDump = {0}; 
	CrashDump.hFile = INVALID_HANDLE_VALUE;

	__try
	{
		CrashDump.hFile = CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0 );
		if( CrashDump.hFile == INVALID_HANDLE_VALUE )
			return 0;

		CrashDump.hMapping = CreateFileMapping( CrashDump.hFile, 0, PAGE_READONLY, 0, 0, NULL );
		if( CrashDump.hMapping == NULL )
			return 0;

		CrashDump.lpMapping = (PBYTE) MapViewOfFile( CrashDump.hMapping, FILE_MAP_READ, 0, 0, 0x10000 ); //0x12000 );
		if( CrashDump.lpMapping == NULL )
			return 0;

		PDUMP_HEADER hdr = CrashDump.DumpHeader;

		if( hdr->ValidDump != 'PMUD' || hdr->Signature != 'EGAP' ) {
			printf("Invalid dump header\n");
			return 0;
		}

		printf("Crash dump '%s' analysing started. Version: %d.%d\n\n", filename, hdr->MajorVersion, hdr->MinorVersion);

		// Header
		printf("CR3 = 0x%08x                   PfnDatabase = 0x%08x\n"
			   "PsLoadedModuleList = 0x%08x    PsActiveProcessHead = 0x%08x\n", hdr->DirectoryTableBase, (ULONG_PTR)hdr->PfnDataBase,
			   (ULONG_PTR)hdr->PsLoadedModuleList, (ULONG_PTR)hdr->PsActiveProcessHead );
		printf("Machine type: %s,  NumberProcessors: %d\n", LookupConstDesc( MachineTypes, hdr->MachineImageType ), hdr->NumberProcessors);
		printf("PaeEnabled = %d, KdDebuggerDataBlock = 0x%08x\n", hdr->PaeEnabled, (ULONG_PTR)hdr->KdDebuggerDataBlock);
		printf("\n");

		// Bug check
		printf("Bugcheck code %s (0x%08x)\n", BugCheckDescf(hdr->BugCheckCode), hdr->BugCheckCode);
		printf("Arguments[0] = 0x%08x\n", hdr->BugCheckParameter1);
		printf("Arguments[1] = 0x%08x\n", hdr->BugCheckParameter2);
		printf("Arguments[2] = 0x%08x\n", hdr->BugCheckParameter3);
		printf("Arguments[3] = 0x%08x\n", hdr->BugCheckParameter4);
		printf("\n");

		// Data blocks
		ULONG* block = CrashDump.DataBlocks;

		// Dump type & size
		CrashDump.DumpType  = &block[ DH_DUMP_TYPE     ];
		CrashDump.DumpFlags = &block[ DH_DUMP_TYPE + 1 ];
		CrashDump.DumpSize = (PLARGE_INTEGER) &block[ DH_REQUIRED_DUMP_SPACE ];
		printf(	"Dump type: %s, DumpSize: %d bytes (0x%08x)\n",
				LookupConstDesc( DumpTypes, *CrashDump.DumpType ),
				CrashDump.DumpSize->LowPart,
				CrashDump.DumpSize->LowPart );
		printf("\n");

		// Physical memory descriptor
		CrashDump.PhysicalMemoryDescriptor = (PPHYSICAL_MEMORY_DESCRIPTOR)( &block[DH_PHYSICAL_MEMORY_BLOCK] );
		if( CrashDump.PhysicalMemoryDescriptor->NumberOfRuns == 'EGAP' )
		{
			printf("PPHYSICAL_MEMORY_DESCRIPTOR: Invalid\n");
		}
		else
		{
			printf(	"PPHYSICAL_MEMORY_DESCRIPTOR:\nNumberOfRuns = 0x%08x  NumberOfPages = 0x%08x\n",
					CrashDump.PhysicalMemoryDescriptor->NumberOfRuns,
					CrashDump.PhysicalMemoryDescriptor->NumberOfPages );
			for( ULONG i=0;i<CrashDump.PhysicalMemoryDescriptor->NumberOfRuns;i++ )
			{
				printf(	"PPHYSICAL_MEMORY_RUN[%d]: BasePage = 0x%08x   PageCount = 0x%08x\n",
						i,
						CrashDump.PhysicalMemoryDescriptor->Run[i].BasePage,
						CrashDump.PhysicalMemoryDescriptor->Run[i].PageCount );
			}
		}
		printf("\n");

		// Context record:
		CrashDump.Context = (PCONTEXT)( &block[DH_CONTEXT_RECORD] );
		printf(	"Context record:\nEip = 0x%08x   ESP = 0x%08x   EBP = 0x%08x\n",
				CrashDump.Context->Eip,
				CrashDump.Context->Esp,
				CrashDump.Context->Ebp );
		printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x ESI=%08x EDI=%08x\n",
			CrashDump.Context->Eax,
			CrashDump.Context->Ebx,
			CrashDump.Context->Ecx,
			CrashDump.Context->Edx,
			CrashDump.Context->Esi,
			CrashDump.Context->Edi,
			CrashDump.Context->Eax);
		printf("\n");

        // Analyse dump
		if( *CrashDump.DumpType == DUMP_TYPE_TRIAGE )
		{
			//
			// Minidump
			//

			CrashDump.pTriageDumpHeader = (PTRIAGE_DUMP_HEADER)( CrashDump.lpMapping + 0x1000 );

			printf("Analysing triage dump header\n");

			printf("[-] Not implemented\n");

			__asm nop;
		}
		else if( *CrashDump.DumpType == DUMP_TYPE_SUMMARY )
		{
			//
			// Kernel summary dump - only kernel address space available
			//

			CrashDump.pSummaryDumpHeader = (PSUMMARY_DUMP_HEADER) ( CrashDump.lpMapping + 0x1000 );
			CrashDump.KernelMemoryMap.SizeOfBitMap = CrashDump.pSummaryDumpHeader->BitmapSize;
			CrashDump.KernelMemoryMap.Buffer = (PULONG) ( CrashDump.pSummaryDumpHeader + 1 );

			if( CrashDump.pSummaryDumpHeader->ValidDump != 'PMUD' ) {
				printf("Invalid summary dump header\n");
				return 0;
			}

			printf("Analyzing summary dump header\n");
			printf(	"HeaderSize = 0x%08x   BitmapSize = 0x%08x\n",
					CrashDump.pSummaryDumpHeader->HeaderSize, 
					CrashDump.pSummaryDumpHeader->BitmapSize );
			printf(	"Number of kernel pages in dump: 0x%08x\n\n", 
					CrashDump.pSummaryDumpHeader->Pages );


			ULONG Virtual = CrashDump.Context->Eip;
			ULONGLONG Physical;
			Physical = VirtualToPhysical( &CrashDump, Virtual  );

			printf("Translating virtual address [0x%08x]: 0x%08x\n", Virtual, Physical);

			PBYTE MappedEIP = (PBYTE)RetrieveDumpData( &CrashDump, (void*)(ULONG_PTR)CrashDump.Context->Eip );

			if( MappedEIP )
			{
				printf("Bytes at [EIP=%08x]: %02x %02x %02x %02x  %02x %02x %02x %02x\n", CrashDump.Context->Eip,
							MappedEIP[0], MappedEIP[1], MappedEIP[2], MappedEIP[3],
							MappedEIP[4], MappedEIP[5], MappedEIP[6], MappedEIP[7] );
			}
			else printf("Memory pointed by EIP is not present\n");

			__asm nop;
		}
		else if( *CrashDump.DumpType == DUMP_TYPE_COMPLETE )
		{
			//
			// Complete memory dump - full address space available
			//

			ULONG Virtual = CrashDump.Context->Eip;
			ULONGLONG Physical;
			Physical = VirtualToPhysical( &CrashDump, Virtual  );

			printf("Translating virtual address [0x%08x]: 0x%08x\n", Virtual, Physical);

			PBYTE MappedEIP = (PBYTE)RetrieveDumpData( &CrashDump, (void*)(ULONG_PTR)CrashDump.Context->Eip );

			printf("Bytes at [EIP=%08x]: %02x %02x %02x %02x  %02x %02x %02x %02x\n", CrashDump.Context->Eip,
						MappedEIP[0], MappedEIP[1], MappedEIP[2], MappedEIP[3],
						MappedEIP[4], MappedEIP[5], MappedEIP[6], MappedEIP[7] );

			ExtractDumpHeader( &CrashDump, "crashdump.hdr" );

			__asm nop;

		}


		printf("\nDump analysis finished\n");
	}
	__finally
	{
		if( CrashDump.lpMapping )
			UnmapViewOfFile( CrashDump.lpMapping );

		if( CrashDump.hMapping )
			CloseHandle( CrashDump.hMapping );

		if( CrashDump.hFile != INVALID_HANDLE_VALUE )
			CloseHandle( CrashDump.hFile );

		Sleep(INFINITE);
	}

	return 0;
}


int main()
{
	AnalyseDump( "C:\\gr8lkd.dmp" );
	//AnalyseDump( "D:\\memory.dmp" );

	return 0;
}
В нем уже забит путь к моему крешдампу c:\gr8lkd.dmp от моей утилиты gr8lkd.

Вывод примерно следующий:
Цитата:
Crash dump 'C:\gr8lkd.dmp' analysing started. Version: 15.2600

CR3 = 0x00373000 PfnDatabase = 0x80557b48
PsLoadedModuleList = 0x805531a0 PsActiveProcessHead = 0x80559258
Machine type: IMAGE_FILE_MACHINE_I386, NumberProcessors: 1
PaeEnabled = 1, KdDebuggerDataBlock = 0x80544ce0

Bugcheck code KMODE_EXCEPTION_NOT_HANDLED (0x0000001e)
Arguments[0] = 0x80000004
Arguments[1] = 0xf3b21315
Arguments[2] = 0x00000000
Arguments[3] = 0x00000000

Dump type: DUMP_TYPE_FULL, DumpSize: 536403968 bytes (0x1ff8e000)

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

Context record:
Eip = 0xf3b21315 ESP = 0xf8ab4928 EBP = 0xf8ab4c1c
EAX=00000000 EBX=816b7000 ECX=00000000 EDX=00004e24 ESI=00000000 EDI=f8ab4c00

Translating virtual address [0xf3b21315]: 0x1d9ac315
Bytes at [EIP=f3b21315]: 58 89 85 d0 fd ff ff 9c

Dump analysis finished
III. Создание собственного дампа.

Мы подошли к самому интересному моменту, а именно к созданию собственного файда дампа.
Сперва стоит описать недокументированную структуру KdDebuggerDataBlock, поскольку она нам очень поможет при создании дампа. Итак, по адресу KdDebuggerDataBlock лежит следующее:
Код:
template <class T>
struct PFUNC {
	T  VirtualAddress;
	ULONG  ZeroField;
};

typedef struct _KD_DEBUGGER_DATA_BLOCK {
	ULONG  Unknown1[4];
	ULONG  ValidBlock; // 'GBDK'
	ULONG  Size; // 0x290
	PFUNC<PVOID>  _imp__VidInitialize;
	PFUNC<PVOID>  RtlpBreakWithStatusInstruction;
	ULONG  Unknown2[4];
	PFUNC<PVOID>  KiCallUserMode;
	ULONG  Unknown3[2];
	PFUNC<PVOID>  PsLoadedModuleList;
	PFUNC<PVOID>  PsActiveProcessHead;
	PFUNC<PVOID>  PspCidTable;
	PFUNC<PVOID>  ExpSystemResourcesList;
	PFUNC<PVOID>  ExpPagedPoolDescriptor;
	PFUNC<PVOID>  ExpNumberOfPagedPools;
	PFUNC<PVOID>  KeTimeIncrement;
	PFUNC<PVOID>  KeBugCheckCallbackListHead;
	PFUNC<PVOID>  KiBugCheckData;
	PFUNC<PVOID>  IopErrorLogListHead;
	PFUNC<PVOID>  ObpRootDirectoryObject;
	PFUNC<PVOID>  ObpTypeObjectType;
	PFUNC<PVOID>  MmSystemCacheStart;
	PFUNC<PVOID>  MmSystemCacheEnd;
	PFUNC<PVOID>  MmSystemCacheWs;
	PFUNC<PVOID>  MmPfnDatabase;
	PFUNC<PVOID>  MmSystemPtesStart;
	PFUNC<PVOID>  MmSystemPtesEnd;
	PFUNC<PVOID>  MmSubsectionBase;
	PFUNC<PVOID>  MmNumberOfPagingFiles;
	PFUNC<PVOID>  MmLowestPhysicalPage;
	PFUNC<PVOID>  MmHighestPhysicalPage;
	PFUNC<PVOID>  MmNumberOfPhysicalPages;
	PFUNC<PVOID>  MmMaximumNonPagedPoolInBytes;
	PFUNC<PVOID>  MmNonPagedSystemStart;
	PFUNC<PVOID>  MmNonPagedPoolStart;
	PFUNC<PVOID>  MmNonPagedPoolEnd;
	PFUNC<PVOID>  MmPagedPoolStart;
	PFUNC<PVOID>  MmPagedPoolEnd;
	PFUNC<PVOID>  MmPagedPoolInfo;
	PFUNC<PVOID>  Unknown4;
	PFUNC<PVOID>  MmSizeOfPagedPoolInBytes;
	PFUNC<PVOID>  MmTotalCommitLimit;
	PFUNC<PVOID>  MmTotalCommittedPages;
	PFUNC<PVOID>  MmSharedCommit;
	PFUNC<PVOID>  MmDriverCommit;
	PFUNC<PVOID>  MmProcessCommit;
	PFUNC<PVOID>  MmPagedPoolCommit;
	PFUNC<PVOID>  Unknown5;
	PFUNC<PVOID>  MmZeroedPageListHead;
	PFUNC<PVOID>  MmFreePageListHead;
	PFUNC<PVOID>  MmStandbyPageListHead;
	PFUNC<PVOID>  MmModifiedPageListHead;
	PFUNC<PVOID>  MmModifiedNoWritePageListHead;
	PFUNC<PVOID>  MmAvailablePages;
	PFUNC<PVOID>  MmResidentAvailablePages;
	PFUNC<PVOID>  PoolTrackTable;
	PFUNC<PVOID>  NonPagedPoolDescriptor;
	PFUNC<PVOID>  MmHighestUserAddress;
	PFUNC<PVOID>  MmSystemRangeStart;
	PFUNC<PVOID>  MmUserProbeAddress;
	PFUNC<PVOID>  KdPrintCircularBuffer;
	PFUNC<PVOID>  KdPrintWritePointer;
	PFUNC<PVOID>  KdPrintWritePointer2;
	PFUNC<PVOID>  KdPrintRolloverCount;
	PFUNC<PVOID>  MmLoadedUserImageList;
	PFUNC<PVOID>  NtBuildLab;
	PFUNC<PVOID>  Unknown6;
	PFUNC<PVOID>  KiProcessorBlock;
	PFUNC<PVOID>  MmUnloadedDrivers;
	PFUNC<PVOID>  MmLastUnloadedDriver;
	PFUNC<PVOID>  MmTriageActionTaken;
	PFUNC<PVOID>  MmSpecialPoolTag;
	PFUNC<PVOID>  KernelVerifier;
	PFUNC<PVOID>  MmVerifierData;
	PFUNC<PVOID>  MmAllocateNonPagedPool;
	PFUNC<PVOID>  MmPeakCommitment;
	PFUNC<PVOID>  MmTotalCommitLimitMaximum;
	PFUNC<PVOID>  CmNtCSDVersion;
	PFUNC<PPHYSICAL_MEMORY_DESCRIPTOR*>  MmPhysicalMemoryBlock;
	PFUNC<PVOID>  MmSessionBase;
	PFUNC<PVOID>  MmSessionSize;
	PFUNC<PVOID>  Unknown7;

} KD_DEBUGGER_DATA_BLOCK, *PKD_DEBUGGER_DATA_BLOCK;
 
Ответить с цитированием

  #4  
Старый 07.09.2007, 19:40
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

Это же очень удобно! Достаточно получить лишь один неэкспортируемый адрес KdDebuggerDataBlock, как все остальные нужные адреса как на ладони.
Поле ValidBlock должно содержать сигнатуру 'GBDK', а Size должно точно равняться sizeof(KD_DEBUGGER_DATA_BLOCK).

Получить адрес KdDebuggerDataBlock можно через экспортируемый символ KeCapturePersistentThreadState, а именно (для версии xp 2600):
KdDebuggerDataBlock = *(PVOID*)((ULONG)KeCapturePersistentThreadState + *(ULONG*)((ULONG)KeCapturePersistentThreadState + 0xC )+ 0x11);

Не очень симпотично, зато что нам это дает! Множество неэкспортируемых адресов внутренних структур. Оно нам потребуется для заполнения хидера дампа.

Ну а теперь, поскольку мы всё знаем, приведу отрывочный код заполнения заглавной страницы дампа:
Код:
BOOLEAN
InitializeDumpHeader(
	IN	PBYTE HeaderPage
	)
{
	PDUMP_HEADER hdr;
	ULONG* blocks;
	PKD_DEBUGGER_DATA_BLOCK KdDebuggerDataBlock;
	EXCEPTION_RECORD exception;
	PVOID KeCapturePersistentThreadState;
	UNICODE_STRING uKeCapturePersistentThreadState;
	PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
	CONTEXT ctx = {CONTEXT_FULL};
	PEXCEPTION_POINTERS pei;
	
	// Get context
	__asm
	{
		// Common registers
        mov [ctx.Eax], eax
		mov [ctx.Ebx], ebx
		mov [ctx.Ecx], ecx
		mov [ctx.Edx], edx
		mov [ctx.Esi], esi
		mov [ctx.Edi], edi

		// Control registers
		mov [ctx.Esp], esp
		mov [ctx.Ebp], ebp

		call _1
		// This address will appear in kd as crash address:
_1:		pop eax
		mov [ctx.Eip], eax

		pushfd
		pop eax
		mov [ctx.EFlags], eax

		// Debug registers
		__emit 0x0F
		__emit 0x21
		__emit 0xC0 ; mov eax, dr0
		mov [ctx.Dr0], eax
		__emit 0x0F
		__emit 0x21
		__emit 0xC8 ; mov eax, dr1
		mov [ctx.Dr1], eax
		__emit 0x0F
		__emit 0x21
		__emit 0xD0 ; mov eax, dr2
		mov [ctx.Dr2], eax
		__emit 0x0F
		__emit 0x21
		__emit 0xD8 ; mov eax, dr3
		mov [ctx.Dr3], eax
		__emit 0x0F
		__emit 0x21
		__emit 0xF0 ; mov eax, dr6
		mov [ctx.Dr6], eax
		__emit 0x0F
		__emit 0x21
		__emit 0xF8 ; mov eax, dr7
		mov [ctx.Dr7], eax

		// Segment registers
		push cs
		pop eax
        mov [ctx.SegCs], eax
		xor eax,eax
		mov ax, ss
		mov [ctx.SegSs], eax
		mov ax, ds
		mov [ctx.SegDs], eax
		mov ax, es
		mov [ctx.SegEs], eax
		mov ax, fs
		mov [ctx.SegFs], eax
		mov ax, gs
		mov [ctx.SegGs], eax
	}

	// Get KeCapturePersistentThreadState address
	RtlInitUnicodeString( &uKeCapturePersistentThreadState, L"KeCapturePersistentThreadState" );
	KeCapturePersistentThreadState = MmGetSystemRoutineAddress( &uKeCapturePersistentThreadState );

	// Initialize dump header
	hdr = (PDUMP_HEADER) HeaderPage;
	hdr->ValidDump = 'PMUD';
	hdr->MinorVersion = (USHORT) *NtBuildNumber;
    hdr->MajorVersion = (USHORT) 0xF; // checked/free, в идеале нужно брать инфу из реестра; если посмотреть код в приложении к статье - там так и сделано
	hdr->DirectoryTableBase = CR3();

	//
	// Capture KdDebuggerDataBlock
	//
	
	__try {
		hdr->KdDebuggerDataBlock = *(PVOID*)((ULONG)KeCapturePersistentThreadState + *(ULONG*)((ULONG)KeCapturePersistentThreadState + 0xC )+ 0x11);

	} __except( (pei=GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER ) {
		ULONG i;

		DbgPrint("An exception occurred while trying to get KdDebuggerDataBlock address:\n");
		DbgPrint("Exception code: 0x%08x\n", pei->ExceptionRecord->ExceptionCode);
		DbgPrint("Number of arguments: 0x%08x\n", pei->ExceptionRecord->NumberParameters);

		for( i = 0; i < pei->ExceptionRecord->NumberParameters; i++ ) {
			DbgPrint("Argument[%d]: 0x%08x\n", i, pei->ExceptionRecord->ExceptionInformation[i]);
		}

		return FALSE;
	}
	hdr->MachineImageType = 0x14c;
	hdr->NumberProcessors = 1;
	hdr->BugCheckCode = KMODE_EXCEPTION_NOT_HANDLED;
	hdr->BugCheckParameter1 = STATUS_BREAKPOINT;
	hdr->BugCheckParameter2 = ctx.Eip;
	hdr->BugCheckParameter3 = 0;
	hdr->BugCheckParameter4 = 0;
	hdr->PaeEnabled = (CR4() & PAE_ENABLED) ? TRUE : FALSE;

	KdDebuggerDataBlock = (PKD_DEBUGGER_DATA_BLOCK) hdr->KdDebuggerDataBlock;

	// Check KdDebuggerDataBlock
	if( KdDebuggerDataBlock->ValidBlock != 'GBDK' || KdDebuggerDataBlock->Size != sizeof(*KdDebuggerDataBlock) )
	{
		// Invalid debugger data block
		DbgPrint(	"KdDebuggerDataBlock is not valid.\nSignature = 0x%08x (should be 0x%08x)\nSize = 0x%08x (should be 0x%08x)\n",
					KdDebuggerDataBlock->ValidBlock, 'GBDK',
					KdDebuggerDataBlock->Size, sizeof(*KdDebuggerDataBlock) );
		return FALSE;
	}

	DbgPrint("Got valid KdDebuggerDataBlock=0x%08x\n", KdDebuggerDataBlock);
	
	hdr->PfnDataBase = (PULONG) KdDebuggerDataBlock->MmPfnDatabase.VirtualAddress;
	hdr->PsLoadedModuleList = (PLIST_ENTRY) KdDebuggerDataBlock->PsLoadedModuleList.VirtualAddress;
	hdr->PsActiveProcessHead = (PLIST_ENTRY) KdDebuggerDataBlock->PsActiveProcessHead.VirtualAddress;

	DbgPrint("PfnDataBase = 0x%08x\n", hdr->PfnDataBase);
	DbgPrint("PsLoadedModuleList = 0x%08x\n", hdr->PsLoadedModuleList);
	DbgPrint("PsActiveProcessHead = 0x%08x\n", hdr->PsActiveProcessHead);

	blocks = (ULONG*)(ULONG_PTR)HeaderPage;

	//
	// Get physical memory descriptor
	//

	MmPhysicalMemoryBlock = *(KdDebuggerDataBlock->MmPhysicalMemoryBlock.VirtualAddress);

	DbgPrint("MmPhysicalMemoryBlock = 0x%08x\n", MmPhysicalMemoryBlock);

	if( MmPhysicalMemoryBlock->NumberOfRuns == 'EGAP' ) {
		RtlCopyMemory(	&blocks[ DH_PHYSICAL_MEMORY_BLOCK ],
						MmPhysicalMemoryBlock, 
						sizeof(PHYSICAL_MEMORY_DESCRIPTOR)
						);
	} else {
		RtlCopyMemory(	&blocks[ DH_PHYSICAL_MEMORY_BLOCK ],
						MmPhysicalMemoryBlock, 
						sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) + 
						sizeof(PHYSICAL_MEMORY_RUN)*MmPhysicalMemoryBlock->NumberOfRuns
						);
	}

	//
	// Save context record
	//

	RtlCopyMemory(	&blocks[ DH_CONTEXT_RECORD ],
					&ctx,
					sizeof(CONTEXT)
					);
	DbgPrint("Context record saved.\n");
	DbgPrint("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx);
	DbgPrint("ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", ctx.Esi, ctx.Edi, ctx.Ebp, ctx.Esp);
	DbgPrint("EIP=%08x CS=%08x FS=%08x DS=%08x\n", ctx.Eip, ctx.SegCs, ctx.SegFs, ctx.SegDs);

	//
	// Create & store exception record
	//

	exception.ExceptionCode = STATUS_BREAKPOINT;
	exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
	exception.ExceptionRecord = NULL;
	exception.ExceptionAddress = (PVOID) ctx.Eip;
	exception.NumberParameters = 0;

	RtlCopyMemory(	&blocks[ DH_EXCEPTION_RECORD ],
					&exception,
					sizeof(EXCEPTION_RECORD)
					);

	//
	// Initialize dump type & size
	//

	blocks[ DH_DUMP_TYPE ] = DUMP_TYPE_COMPLETE;

    ((LARGE_INTEGER*)&blocks[DH_REQUIRED_DUMP_SPACE])->QuadPart = ( MmPhysicalMemoryBlock->NumberOfPages << 12 ) + 0x1000;

	DbgPrint("Header page initialized OK\n");

	return TRUE;
}
На этом всё. В приложении к статье можно найти более новую версию gr8lkd, которую я еще никуда не выкладывал, исходники AnalyseCrashDump и исходники gendump драйвера.

http://gr8.cih.ms/uploads/gr8lkd2.rar (81.1 Kb)
http://gr8.cih.ms/uploads/AnalyseCrashDump.rar (11.6 Kb)
http://gr8.cih.ms/uploads/gendump.rar (10.1 Kb)
 
Ответить с цитированием

  #5  
Старый 07.09.2007, 19:59
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

Лог анализа сгенеренного дампа в WinDbg:

Цитата:
Microsoft (R) Windows Debugger Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [D:\CRASHDMP.DMP]
Kernel Complete Dump File: Full address space is available

Symbol search path is: D:\Symbols
Executable search path is:
Windows XP Kernel Version 2600 (Service Pack 2) UP Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 2600.xpsp_sp2_rtm.040803-2158
Kernel base = 0x804d7000 PsLoadedModuleList = 0x805531a0
Debug session time: Sun Feb 13 06:34:57.897 17420 (GMT+4)
System Uptime: 3 days 4:14:47.773
WARNING: Process directory table base 00373000 doesn't match CR3 05A4E5E0
WARNING: Process directory table base 00373000 doesn't match CR3 05A4E5E0
Loading Kernel Symbols
.................................................. .................................................. ..................................
Loading User Symbols

Loading unloaded module list
..............
*** ERROR: Module load completed but symbols could not be loaded for LiveKdD.SYS
************************************************** *****************************
* *
* Bugcheck Analysis *
* *
************************************************** *****************************

Use !analyze -v to get detailed debugging information.

BugCheck 1E, {80000003, f8beb4d7, 0, 0}

Probably caused by : gendump.sys ( gendump!InitializeDumpHeader+77 )

Followup: MachineOwner
---------

kd> !analyze -v
************************************************** *****************************
* *
* Bugcheck Analysis *
* *
************************************************** *****************************

KMODE_EXCEPTION_NOT_HANDLED (1e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: 80000003, The exception code that was not handled
Arg2: f8beb4d7, The address that the exception occurred at
Arg3: 00000000, Parameter 0 of the exception
Arg4: 00000000, Parameter 1 of the exception

Debugging Details:
------------------


EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - One or more arguments are invalid

FAULTING_IP:
gendump!InitializeDumpHeader+77 [d:\progs\driverdev\gendump\gendump.cpp @ 171]
f8beb4d7 58 pop eax

EXCEPTION_PARAMETER1: 00000000

EXCEPTION_PARAMETER2: 00000000

DEFAULT_BUCKET_ID: DRIVER_FAULT

BUGCHECK_STR: 0x1E

PROCESS_NAME: System

LAST_CONTROL_TRANSFER: from 00000000 to f8bc02d1

STACK_TEXT:
f188ac50 00000000 00000000 00000000 00000001 LiveKdD+0x12d1


STACK_COMMAND: .bugcheck ; kb

FOLLOWUP_IP:
gendump!InitializeDumpHeader+77 [d:\progs\driverdev\gendump\gendump.cpp @ 171]
f8beb4d7 58 pop eax

FAULTING_SOURCE_CODE:
167: mov [ctx.Ebp], ebp
168:
169: call _1
170: // This address will appear in kd as crash address:
> 171: _1: pop eax
172: mov [ctx.Eip], eax
173:
174: pushfd
175: pop eax
176: mov [ctx.EFlags], eax


SYMBOL_NAME: gendump!InitializeDumpHeader+77

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: gendump

IMAGE_NAME: gendump.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 45004080

FAILURE_BUCKET_ID: 0x1E_gendump!InitializeDumpHeader+77

BUCKET_ID: 0x1E_gendump!InitializeDumpHeader+77

Followup: MachineOwner
---------
 
Ответить с цитированием

  #6  
Старый 07.09.2007, 20:12
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

Поправочка:
Цитата:
Например на моей системе (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.
Тут я, мягко говоря, нагнал. Надо так:

То есть у меня доступны физические страницы с pfn'ами (номерами, другими словами) от 1 до 0x9f, от 0x100 до 0xfff и от 0x1000 до 0x1fff0.
 
Ответить с цитированием

  #7  
Старый 13.09.2007, 17:00
Аватар для z01b
z01b
Постоянный
Регистрация: 05.01.2007
Сообщений: 508
Провел на форуме:
2360904

Репутация: 1393


По умолчанию

Тулза полезная ) +1
P.S. Gr8t вылезай из 0 колица )))
 
Ответить с цитированием

  #8  
Старый 11.10.2007, 06:16
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


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

Эта статья на васме

клац: http://www.wasm.ru/article.php?article=dmp_format
 
Ответить с цитированием
Ответ



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



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


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




ANTICHAT.XYZ