Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Delphi, .NET, Asm (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   Описание формата Dmp крешдампов и написание простейшего дампера (https://forum.antichat.xyz/showthread.php?t=48533)

_Great_ 07.09.2007 19:38

Описание формата 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.

_Great_ 07.09.2007 19:39

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 ) );
}

Всё готово. Напишем небольшой демонстрационный код для анализа крешдампа:

_Great_ 07.09.2007 19:39

Код:

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;


_Great_ 07.09.2007 19:40

Это же очень удобно! Достаточно получить лишь один неэкспортируемый адрес 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)

_Great_ 07.09.2007 19:59

Лог анализа сгенеренного дампа в 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
---------


_Great_ 07.09.2007 20:12

Поправочка:
Цитата:

Например на моей системе (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.

z01b 13.09.2007 17:00

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

_Great_ 11.10.2007 06:16

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

клац: http://www.wasm.ru/article.php?article=dmp_format


Время: 21:53