Description of DMP Format

Дата публикации 8 июн 2008

Description of DMP Format — Архив WASM.RU

I. Structure of crash dump

Not so long ago I've released my counterpart of livekd, named gr8lkd, which is filter driver for file system. It attaches to disk C: and creates here virtual crash dump to intercept and process all requests to it.

Now I've decided to describe the format of Windows crash dumps - .DMP. On the internet you can find only one page in English (original article is in Russian) with incomplete and buggy description.

I will try to describe this format as complete as possible. Everything underwritten is based on my own research of core's dissassembler listing - if to be more precise, function IoWriteCrashDump and those ones associated with it.

Let's begin...

First, let's talk about crash dumps and how they are generated. When something bad happens in the core, for example freeing of already freed memory, and ExFreePool (function for pool freeing) founds it, it is insecure to continue to work so it is decided to make emergency system shudown. ExFreePool calls KeBuCheckEx (KeBugCheck2, KeBugCheck3 - depending on Windows version, in first case it is exported documented function which shows blue screen, in second and third ones - inner function which do the same) which shows blue screen BAD_POOL_CALLER and calls IoWriteCrashDump to write a crash dump. The crash dump is writed in the disk sectors which are occupied by swap file (before this FSCTL_GET_RETRIEVAL_POINTERS is called to get swap file map (карта размещения)) because at this stage calling of file system driver is insecure - maybe it is itseft the reason of fail, so special driver is used for writing dump to swap file. At the next boot dump will be get from swap file and written to the path specified in the registry.

As is known, there are three types of Windows crash dumps:

  1. "Minidump", or "triage dump" as it is called in the core. Usually such dumps are small (about 64k) and they contain least of information about system when it crashed. This type of dumps is set by default but unfortunately it is not fit for usual system core developments, so we move ahead and look at the next type:
  2. "Kernel Memory Dump", or "summary dump" as it called in the core. Such dumps include only core's memory pages, and not all but only those which are neccessary for crash ananalysis. This dump type is fit for usual analysis of system crash's reasons because it includes code and data of the core, all loaded drivers and system structures. Swap file should be big enough to contain all of this. It is hard to predict it's size, at my maching it is usually about 1/8 of physical memory.
  3. "Full Memory Dump"
  4. . This dump includes all pages of physical memory. Swap file should be big enough to contain it.

First we will examine the structure which is common for all dumps - dump header page. It is the first page of dump file, first 0x1000 (4096) bytes. The structure of this page is - first, all page is filled with "PAGE" (bytes 'EGAP') and then data is written to required offsets.

The structure which identify the crash dump is first:

Код (Text):
  1.  
  2. typedef struct _DUMP_HEADER {
  3. /* 00 */    ULONG Signature;
  4. /* 04 */    ULONG ValidDump;
  5. /* 08 */    ULONG MajorVersion;
  6. /* 0c */    ULONG MinorVersion;
  7. /* 10 */    ULONG DirectoryTableBase;
  8. /* 14 */    PULONG PfnDataBase;
  9. /* 18 */    PLIST_ENTRY PsLoadedModuleList;
  10. /* 1c */    PLIST_ENTRY PsActiveProcessHead;
  11. /* 20 */    ULONG MachineImageType;
  12. /* 24 */    ULONG NumberProcessors;
  13. /* 28 */    ULONG BugCheckCode;
  14. /* 2c */    ULONG BugCheckParameter1;
  15. /* 30 */    ULONG BugCheckParameter2;
  16. /* 34 */    ULONG BugCheckParameter3;
  17. /* 38 */    ULONG BugCheckParameter4;
  18. /* 3c */    CHAR  VersionUser[32];
  19. /* 5c */    BYTE  PaeEnabled;
  20.             BYTE  NotUsed[3];
  21. /* 60 */    PVOID KdDebuggerDataBlock;
  22. } DUMP_HEADER, *PDUMP_HEADER;
  • Signature - This field is not filled and leaved with 'EGAP' bytes.
  • ValidDump The dump signature - "DUMP" ('PMUD')
  • MajorVersion - 0x0F if Free build 0x0C if Checked build
  • MinorVersion - Build number системы System's build number
  • DirectoryTableBase - CR3 value at the moment of system's crash - physical address of page directory
  • PfnDatabase - MmPfnDatabase virtual address - page frame (number? - not sure here [translator]) database (PFN)
  • PsLoadedModuleList - PsLoadedModuleList virtual address - list of loaded modules
  • PsActiveProcessHead - PsActiveProcessHead virtual address - list of active processes
  • MachineImageType - on x86 it is equal to 0x14C, you can check other constants in the winnt.h.
  • NumberProcessors - number of processor (from KeNumberProcessors)
  • BugCheckCode - Stop code of error
  • BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4 - Error's parameters
  • VersionUser - Version of something, I did not find out what exactly. As a rule, first byte written as 0, others are not filled.
  • PaeEnabled - =1 if support of Physical Address Extensions (PAE) is turned on, =0 if turned off
  • KdDebuggerDataBlock - Virtual address of very important structure KdDebuggerDataBlock, which will be described later.

Well, this is beginning of the dump. Some data blocks are written to other offsets, so first let's define several constants:

Код (Text):
  1.  
  2. // Data Blocks
  3. #define DH_PHYSICAL_MEMORY_BLOCK        25
  4. #define DH_CONTEXT_RECORD               200
  5. #define DH_EXCEPTION_RECORD             500
  6. #define DH_DUMP_TYPE                    994
  7. #define DH_CALLBACKS_STATUS             996   // (field is not required)
  8. #define DH_PRODUCT_TYPE                 997   // (field is not required, value is taken from KUSER_SHARED_DATA)
  9. #define DH_SUITE_MASK                   998   // (field is not required, value is taken from KUSER_SHARED_DATA)
  10. #define DH_REQUIRED_DUMP_SPACE          1000
  11. #define DH_INTERRUPT_TIME               1006  // (field is not required, value is taken from KUSER_SHARED_DATA)
  12. #define DH_SYSTEM_TIME                  1008  // (field is not required, value is taken from KUSER_SHARED_DATA)

First page is treated as DWORD array, and this constants define indexes of this array where data is contained. So, in other words using C:

Код (Text):
  1. ULONG* blocks = (ULONG*) HeaderPage;
  2. //&blocks[DH_xxx] - address of corresponding block

Let's examine this block one-by-one:

DH_PHYSICAL_MEMORY_BLOCK - Here is the physical memory descriptor, the structure PHYSICAL_MEMORY_DESCRIPTOR:

Код (Text):
  1. typedef unsigned long PFN_NUMBER;
  2.  
  3. typedef struct _PHYSICAL_MEMORY_RUN {
  4.     PFN_NUMBER BasePage;
  5.     PFN_NUMBER PageCount;
  6. } PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;
  7.  
  8. typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
  9.     ULONG NumberOfRuns;
  10.     PFN_NUMBER NumberOfPages;
  11.     PHYSICAL_MEMORY_RUN Run[1];
  12. } PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;

It, as well as Run arrays, describes available physical memory. Usually system has 3-4 Runs - solid sets of physical pages. For example, on my system (512Mb of physical memory) they are showed as:

Код (Text):
  1. PPHYSICAL_MEMORY_DESCRIPTOR:
  2. NumberOfRuns = 0x00000003  NumberOfPages = 0x0001ff8d
  3. PPHYSICAL_MEMORY_RUN[0]: BasePage = 0x00000001   PageCount = 0x0000009e
  4. PPHYSICAL_MEMORY_RUN[1]: BasePage = 0x00000100   PageCount = 0x00000eff
  5. PPHYSICAL_MEMORY_RUN[2]: BasePage = 0x00001000   PageCount = 0x0001eff0

I.e. physical pages with PFNs (numbers in other words) from 1 to 0x9e, from 0x100 to 0xeff and from 0x1000 to 0x1eff0.

Size of this block is equal to sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) + sizeof(PHYSICAL_MEMORY_RUN) * NumberOfRuns

  • DH_CONTEXT_RECORD The structure CONTEXT is stored here which contains context of thread which triggered error. I think no additional explanations are needed.
  • DH_EXCEPTION_RECORD As in previous, the structure EXCEPTION_RECORD is stored here which contains information (if available) about exception.
  • DH_DUMP_TYPE Here is stored the dword which indicates the dump type - triage, summary of full dump. The values of this field are:

    Код (Text):
    1. // Dump types
    2. #define DUMP_TYPE_TRIAGE                4
    3. #define DUMP_TYPE_SUMMARY               2
    4. #define DUMP_TYPE_FULL                  1
  • DH_CALLBACKS_STATUS - Here is stored NTSTATUS returned by BugcheckCallbacks.
  • DH_PRODUCT_TYPE - Here is stored the system type described by enumeration:

    Код (Text):
    1.  
    2. enum _NT_PRODUCT_TYPE {
    3.   NtProductWinNt = 0x1,
    4.   NtProductLanManNt = 0x2,
    5.   NtProductServer = 0x3,
    6. };
  • DH_SUITE_MASK - Here is stored the field KUSER_SHARED_DATA->SuiteMask and if honestly I don't know what it means.
  • DH_REQUIRED_DUMP_SPACE - Here is stored LARGE_INTEGER which contains full size of dump in bytes.
  • DH_INTERRUPT_TIME - I don't know it's purpose but it is taken from KUSER_SHARED_DATA->InterruptTime
  • DH_SYSTEM_TIME - This field is taken from KUSER_SHARED_DATA->SystemTime and, as far as I know, contains current uptime

Well, that's all about first dump page. Following structure of dump is heavily depends on it's type:

  1. Minidump (triage dump) - this dump type is not very interesting, so I will only provide partial structure TRIAGE_DUMP_HEADER without many comments (I have not finished it's analysis). Just note that it constains offsets of some structures which are followed then in row format.

    Код (Text):
    1.  
    2. // Triage dump header
    3. typedef struct _TRIAGE_DUMP_HEADER {
    4.     ULONG   ServicePackBuild;   // 00
    5.     ULONG   SizeOfDump;         // 04
    6.     ULONG   ValidOffset;        // 08
    7.     ULONG   ContextOffset;      // 0c
    8.     ULONG   ExceptionOffset;    // 10
    9.     ULONG   MmOffset;           // 14
    10.     ULONG   UnloadedDriversOffset; // 18
    11.     ULONG   PrcbOffset;         // 1c
    12.     ULONG   ProcessOffset;      // 20
    13.     ULONG   ThreadOffset;       // 24
    14.     ULONG   Unknown1;           // 28
    15.     ULONG   Unknown2;           // 2c
    16.     ULONG   DriverListOffset;   // 30
    17.     ULONG   DriverCount;        // 34
    18.    
    19.     ...
    20.  
    21.     ULONG   TriageOptions;      // 44
    22.  
    23. } TRIAGE_DUMP_HEADER, *PTRIAGE_DUMP_HEADER;
  2. Kernel memory dump (Summary dump) - second page of dump is beginning with the next structure:

    Код (Text):
    1.  
    2. // Kernel summary dump header
    3. typedef struct _SUMMARY_DUMP_HEADER {
    4.     ULONG   Signature1;         // 00
    5.     ULONG   ValidDump;          // 04
    6.     ULONG   Signature2;         // 08
    7.     ULONG   HeaderSize;         // 0c
    8.     ULONG   BitmapSize;         // 10
    9.     ULONG   Pages;              // 14
    10.     ULONG   Unknown3;           // 18
    11.     ULONG   Unknown4;           // 1c
    12.  
    13. } SUMMARY_DUMP_HEADER, *PSUMMARY_DUMP_HEADER;
    • Signature1, Signature2 - This field contains "SDMP" signature, if to be more precise, the structure is filled with those at the beginning and then this field is leave as is.
    • ValidDump - This field contains "DUMP" signature.
    • HeaderSize - Full size of dump header.
    • BitmapSize - Size of the bitmap which follows right after this structure - we will talk about it later.
    • Pages Number of core's memory pages included in the dump (matches to the number of all set bits in bitmap).

    The bitmap follows right after this structure, the number of bits in it equal to number of physical pages in the system, and setting of bit indicates if corresponding page included in the dump or not. Next structure is designed for working with such bitmap:

    Код (Text):
    1. typedef struct _RTL_BITMAP {
    2.     ULONG SizeOfBitMap;                     // Number of bits in bit map
    3.     PULONG Buffer;                          // Pointer to the bit map itself
    4. } RTL_BITMAP;
    5. typedef RTL_BITMAP *PRTL_BITMAP;
    и функции RtlInitializeBitMap, RtlClearAllBits, RtlSetAllBits, RtlFindClearBits, RtlFindSetBits, RtlCheckBit и другие.

    In the second part of this article I will show example of simple crash dump analyzer and how to work with this map.

  3. Full dump - After first page, dumps of this type contain every physical from all memory runs beginning with 0 and ending with NumberOfRuns-1.

II. Writing of dump analyzer

Now we have enough information to write simple crash dump analyzer.

In our analyzer loaded dump will be shown with the next structure:

Код (Text):
  1. // Mapped crash dump descriptor
  2. struct MappedCrashDump {
  3.     HANDLE hFile;
  4.     HANDLE hMapping;
  5.     union {
  6.         PBYTE lpMapping;
  7.         PDUMP_HEADER DumpHeader;
  8.         ULONG* DataBlocks;
  9.     };
  10.     ULONG *DumpType;
  11.     ULONG *DumpFlags;
  12.     PLARGE_INTEGER DumpSize;
  13.     PPHYSICAL_MEMORY_DESCRIPTOR PhysicalMemoryDescriptor;
  14.     PCONTEXT Context;
  15.     union {
  16.         // Summary dump type
  17.         struct {
  18.             PSUMMARY_DUMP_HEADER pSummaryDumpHeader;
  19.             RTL_BITMAP KernelMemoryMap;
  20.         };
  21.  
  22.         // Triage dump type
  23.         PTRIAGE_DUMP_HEADER pTriageDumpHeader;
  24.     };
  25. };

The plan is: we open a file, create map object and map first 10 pages of dump file. Other will be mapped on demand. To implement this, we will write function MapDumpPage which will map part of file if it is not mapped: already.

Код (Text):
  1. //
  2. // This routine maps dump page at given file offset (DesiredAddress is the sum of BaseMapping and desired offset)
  3. //
  4.  
  5. PVOID MapDumpPage( HANDLE hMapping, PBYTE BaseMapping, PBYTE DesiredAddress )
  6. {
  7.     ULONG FileOffset = (ULONG) ( ( (ULONG_PTR)DesiredAddress-(ULONG_PTR)BaseMapping ) & 0xFFFF0000 );
  8.     PVOID Base = (PVOID)(ULONG_PTR) ( ((ULONG)(ULONG_PTR)DesiredAddress)&0xFFFF0000 );
  9.     MEMORY_BASIC_INFORMATION mbi = {0};
  10.     PVOID Ret = 0;
  11.  
  12.     VirtualQuery( DesiredAddress, &mbi, sizeof(mbi) );
  13.  
  14.     if( mbi.State != MEM_COMMIT ) {
  15.         Ret = MapViewOfFileEx(  hMapping,
  16.                                 FILE_MAP_READ,
  17.                                 0,
  18.                                 FileOffset,
  19.                                 0x10000,
  20.                                 Base
  21.                                 );
  22.         if( Ret == Base ) {
  23.             return DesiredAddress;
  24.         }
  25.  
  26.         // Loaded at different address. Fail
  27.         UnmapViewOfFile( Ret );
  28.         return NULL;
  29.     }
  30.  
  31.     return DesiredAddress;
  32. }

Next we implement function which will get page from summary dump using it's physical address at the time of crash:

Код (Text):
  1. PBYTE GetSummaryDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
  2. {
  3.     PBYTE StartingAddress = (PBYTE)(ULONG_PTR) ( (ULONG)(ULONG_PTR)CrashDump->pSummaryDumpHeader
  4.                             + CrashDump->pSummaryDumpHeader->HeaderSize - 0x1000 );
  5.     ULONG NumberOfPage = (ULONG) (AddressAtCrashTime / 0x1000);
  6.     ULONG OffsetInPage = (ULONG) (AddressAtCrashTime & 0xFFF);
  7.  
  8.     if( NumberOfPage >= CrashDump->KernelMemoryMap.SizeOfBitMap )
  9.         return NULL;
  10.  
  11.     if( RtlCheckBit( &CrashDump->KernelMemoryMap, NumberOfPage ) == 0 )
  12.         return NULL; // not included in dump
  13.  
  14.     // Calculate page number in dump
  15.     ULONG PageNumber = 0;
  16.  
  17.     for( ULONG i=0; i<CrashDump->KernelMemoryMap.SizeOfBitMap; i++ ) {
  18.         if( i == NumberOfPage ) {
  19.             PBYTE Result = StartingAddress + PageNumber*0x1000 + OffsetInPage;
  20.  
  21.             return (PBYTE) MapDumpPage( CrashDump->hMapping, CrashDump->lpMapping, Result );
  22.         }
  23.  
  24.         if( RtlCheckBit( &CrashDump->KernelMemoryMap, i ) ) {
  25.             PageNumber++;
  26.         }
  27.     }
  28.  
  29.     return NULL;
  30. }

To find out if a page is present in the dump and to get it's number, special function was implemented. After we have this information, page is mapped from disk to memory (if not mapped already).

Next we have to define counterpart function for full dump. First, we need to learn how to convert PFN of memory page to PFN of disk page and vice versa. Two following functions will do the job:

Код (Text):
  1. PFN_NUMBER GetPhysicalPFN( PPHYSICAL_MEMORY_DESCRIPTOR ppmd, PFN_NUMBER DumpPFN )
  2. {
  3.     PFN_NUMBER iBuffPage, iPage = iBuffPage = DumpPFN;
  4.  
  5.     if( iBuffPage >= ppmd->NumberOfPages )
  6.         return -1;
  7.  
  8.     // Calculate page in memory
  9.     ULONG NumberOfRunsRequired = 0;
  10.     PFN_NUMBER TotalPageCount = 0;
  11.  
  12.     for( ; NumberOfRunsRequired<ppmd->NumberOfRuns; NumberOfRunsRequired++ )
  13.     {
  14.         PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;
  15.  
  16.         if( iBuffPage >= TotalPageCount &&
  17.             iBuffPage < TotalPageCount + Runs[NumberOfRunsRequired].PageCount )
  18.             break;
  19.  
  20.         TotalPageCount += (Runs[NumberOfRunsRequired].PageCount);
  21.     }
  22.  
  23.     PFN_NUMBER PreviousEnd = 0;
  24.     NumberOfRunsRequired ++;
  25.  
  26.     for( ULONG i=0; i<NumberOfRunsRequired; i++ )
  27.     {
  28.         PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;
  29.  
  30.         iPage += (Runs[i].BasePage - PreviousEnd);
  31.         PreviousEnd = Runs[i].BasePage + Runs[i].PageCount;
  32.     }
  33.  
  34.     return iPage;
  35. }
  36.  
  37. PFN_NUMBER GetDumpPFN( PPHYSICAL_MEMORY_DESCRIPTOR ppmd, PFN_NUMBER PhysicalPFN )
  38. {
  39.     for( int run=0; run<ppmd->NumberOfRuns; run++ )
  40.     {
  41.         PPHYSICAL_MEMORY_RUN Runs = ppmd->Run;
  42.  
  43.         if( PhysicalPFN >= Runs[run].BasePage &&
  44.             PhysicalPFN < (Runs[run].BasePage + Runs[run].PageCount) )
  45.             break;
  46.     }
  47.  
  48.     if( run == ppmd->NumberOfRuns )
  49.         return -1;
  50.  
  51.     PFN_NUMBER iDumpPFN = 0;
  52.  
  53.     for( int i=0; i<run; i++ )
  54.     {
  55.         iDumpPFN += ppmd->Run[i].PageCount;
  56.     }
  57.  
  58.     iDumpPFN += PhysicalPFN - ppmd->Run[i].BasePage;
  59.  
  60.     return iDumpPFN;
  61. }

Next we can define a function to get a page from the dump using it's physical address at the time of the crash, and a general function to get the dump type, and call one of the two functions to get it's address (which is more appropriate):

Код (Text):
  1. PBYTE GetCompleteDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
  2. {
  3.     PBYTE StartingAddress = (PBYTE)(ULONG_PTR)( (ULONG)(ULONG_PTR)CrashDump->lpMapping + 0x1000 );
  4.     PFN_NUMBER PFN = (ULONG) (AddressAtCrashTime >> 12);
  5.     ULONG OffsetInPage = (ULONG) (AddressAtCrashTime & 0xFFF);
  6.  
  7.     // Calculate page number in dump
  8.     for( ULONG i=0; i<CrashDump->PhysicalMemoryDescriptor->NumberOfRuns; i++ ) {    
  9.         PPHYSICAL_MEMORY_RUN Runs = (PPHYSICAL_MEMORY_RUN) &CrashDump->PhysicalMemoryDescriptor->Run;
  10.  
  11.         if( PFN >= Runs[i].BasePage && PFN < (Runs[i].BasePage + Runs[i].PageCount) ) {
  12.        
  13.             PFN_NUMBER DumpPFN = GetDumpPFN( CrashDump->PhysicalMemoryDescriptor, PFN );
  14.             PBYTE Result = StartingAddress + DumpPFN*0x1000 + OffsetInPage;
  15.  
  16.             return (PBYTE) MapDumpPage( CrashDump->hMapping, CrashDump->lpMapping, Result );
  17.         }
  18.     }
  19.     return NULL;
  20. }
  21.  
  22. PBYTE GetDumpPagesByPhysicalAddress( MappedCrashDump *CrashDump, ULONGLONG AddressAtCrashTime )
  23. {
  24.     if( *CrashDump->DumpType == DUMP_TYPE_SUMMARY ) {
  25.  
  26.         return GetSummaryDumpPagesByPhysicalAddress( CrashDump, AddressAtCrashTime );
  27.  
  28.     } else if( *CrashDump->DumpType == DUMP_TYPE_COMPLETE ) {
  29.  
  30.         return GetCompleteDumpPagesByPhysicalAddress( CrashDump, AddressAtCrashTime );
  31.  
  32.     } else {
  33.  
  34.         return NULL;
  35.     }
  36. }

Now we have to learn how to translate virtual adresses to physical ones. Then we will be able to get data from any virtual address of the dump. First, let's define directory structures PDPE, PDE and PTE. We will need them later.

Код (Text):
  1. //
  2. // Page Directory Entry
  3. //
  4.  
  5. struct PDE {
  6.     DWORD  Present:1;
  7.     DWORD  ReadWrite:1;
  8.     DWORD  UserSupervisor:1;
  9.     DWORD  WriteThrough:1;
  10.     DWORD  CacheDisabled:1;
  11.     DWORD  Accessed:1;
  12.     DWORD  Reserved:1;      // Dirty, ignored
  13.     DWORD  PageSize:1;
  14.     DWORD  GlobalPage:1;    // Ignored
  15.     DWORD  Available:3;
  16.     DWORD  Pte:19;
  17. };
  18.  
  19.  
  20. //
  21. // Page Table Entry
  22. //
  23.  
  24. struct PTE {
  25.     DWORD  Present:1;
  26.     DWORD  ReadWrite:1;
  27.     DWORD  UserSupervisor:1;
  28.     DWORD  WriteThrough:1;
  29.     DWORD  CacheDisabled:1;
  30.     DWORD  Accessed:1;
  31.     DWORD  Dirty:1;
  32.     DWORD  PageTableAttributeIndex:1;
  33.     DWORD  GlobalPage:1;
  34.     DWORD  Available:3;
  35.     DWORD  PageFrameNumber:19;
  36. };
  37.  
  38. // Virtual address
  39. struct VIRTUAL_ADDRESS {
  40.     DWORD  Offset:12;
  41.     DWORD  Table:10;
  42.     DWORD  Directory:10;
  43. };
  44.  
  45.  
  46. //
  47. // Page Directory Entry in PAE mode
  48. //
  49.  
  50. #define QWORD ULONGLONG
  51.  
  52. struct LongPDE {
  53.     QWORD  Present:1;
  54.     QWORD  ReadWrite:1;
  55.     QWORD  UserSupervisor:1;
  56.     QWORD  WriteThrough:1;
  57.     QWORD  CacheDisabled:1;
  58.     QWORD  Accessed:1;
  59.     QWORD  Reserved:1;      // Dirty, ignored
  60.     QWORD  PageSize:1;
  61.     QWORD  GlobalPage:1;    // Ignored
  62.     QWORD  Available:3;
  63.     QWORD  Pte:24;
  64.     QWORD  ReservedHigh:28;
  65. };
  66.  
  67.  
  68. //
  69. // Page Table Entry in PAE mode
  70. //
  71.  
  72. struct LongPTE {
  73.     QWORD  Present:1;
  74.     QWORD  ReadWrite:1;
  75.     QWORD  UserSupervisor:1;
  76.     QWORD  WriteThrough:1;
  77.     QWORD  CacheDisabled:1;
  78.     QWORD  Accessed:1;
  79.     QWORD  Dirty:1;
  80.     QWORD  PageTableAttributeIndex:1;
  81.     QWORD  GlobalPage:1;
  82.     QWORD  Available:3;
  83.     QWORD  PageFrameNumber:24;
  84.     QWORD  ReservedHigh:28;
  85. };
  86.  
  87. //
  88. // Page Directory Pointer Table (PAE mode only)
  89. //
  90.  
  91. struct PDPE {
  92.     QWORD  Present:1;
  93.     QWORD  Reserved1:2;
  94.     QWORD  WriteThough:1;
  95.     QWORD  CacheDisabled:1;
  96.     QWORD  Reserved2:4;
  97.     QWORD  Available:3;
  98.     QWORD  Pdt:24;
  99.     QWORD  ReservedHigh:28;
  100. };
  101.  
  102. // Virtual address (PAE)
  103. struct PAE_VIRTUAL_ADDRESS {
  104.     DWORD  Offset:12;
  105.     DWORD  Table:9;
  106.     DWORD  Directory:9;
  107.     DWORD  DirectoryPointer:2;
  108. };

Here is two series of structures defined: with PAE turned off and PAE turned on. I want to remind you, addressing became three-level in PAE mode. CR3 register no more points to PDE, now it points to array of "Page Directory Pointer Entries" or PDPE. There shoud be 4 of them. Every PDPE points to PDE array, and PDE points to PTE. Composition of virtual address also changed a little. One bit of PDE and PTE was cut in favour of two-bit field of PDPE number.

Taking this into account, the function to translate adresses is:

Код (Text):
  1. //
  2. // Translates virtual address to physical address
  3. //
  4.  
  5. ULONGLONG VirtualToPhysical( MappedCrashDump *CrashDump, ULONG VirtualAddress )
  6. {
  7.     ULONG CR3 = CrashDump->DumpHeader->DirectoryTableBase;
  8.  
  9.     CR3 &= 0xFFFFFFF0;   // clear flags in cr3
  10.  
  11.     if( CrashDump->DumpHeader->PaeEnabled )
  12.     {
  13.         // PAE enabled. Use 3-level addressing system: PDPE->PDE->PTE->Page
  14.         PAE_VIRTUAL_ADDRESS va;
  15.  
  16.         *(ULONG*)&va = VirtualAddress;
  17.  
  18.         PDPE* dirptr = (PDPE*)GetDumpPagesByPhysicalAddress( CrashDump, CR3 );
  19.  
  20.         if( dirptr != NULL && dirptr[va.DirectoryPointer].Present )
  21.         {
  22.             LongPDE *dir = (LongPDE*)GetDumpPagesByPhysicalAddress( CrashDump,
  23.                             dirptr[va.DirectoryPointer].Pdt << 12 );
  24.  
  25.             if( dir != NULL && dir[va.Directory].Present )
  26.             {
  27.                 LongPTE* tbl = (LongPTE*)GetDumpPagesByPhysicalAddress( CrashDump,
  28.                                 dir[va.Directory].Pte << 12 );
  29.  
  30.                 if( tbl != NULL && tbl[va.Table].Present )
  31.                 {
  32.                     return ( tbl[va.Table].PageFrameNumber << 12 ) | va.Offset;
  33.                 }
  34.             }
  35.         }
  36.     }
  37.     else
  38.     {
  39.         // PAE disabled. Use 2-level addressing system: PDE->PTE->Page
  40.         VIRTUAL_ADDRESS va;
  41.         *(ULONG*)&va = VirtualAddress;
  42.  
  43.         PDE *dir = (PDE*)GetDumpPagesByPhysicalAddress( CrashDump, CR3 );
  44.        
  45.         if( dir != NULL && dir[va.Directory].Present )
  46.         {
  47.             PTE* tbl = (PTE*)GetDumpPagesByPhysicalAddress( CrashDump, dir[va.Directory].Pte << 12 );
  48.  
  49.             if( tbl != NULL && tbl[va.Table].Present )
  50.             {
  51.                 return ( tbl[va.Table].PageFrameNumber << 12 ) | va.Offset;
  52.             }
  53.         }
  54.     }
  55.  
  56.     return NULL;
  57. }
  58.  
  59. //
  60. // Translates virtual address to physical address and loads that physical pages
  61. //
  62.  
  63. PVOID RetrieveDumpData( MappedCrashDump* CrashDump, PVOID VirtualAddress )
  64. {
  65.     return GetDumpPagesByPhysicalAddress( CrashDump, VirtualToPhysical( CrashDump,
  66.                                         (ULONG)(ULONG_PTR)VirtualAddress ) );
  67. }

Everything is ready. Now we will code a small example for crash dump analyzing.

Код (Text):
  1. struct CONST_DESCRIPTION {
  2.     ULONG   Value;
  3.     LPSTR   Desc;
  4. #define DEFINE_STRING(x) { x, #x }
  5. #define TABLE_END { 0, 0 }
  6. };
  7.  
  8. CONST_DESCRIPTION MachineTypes[] = {
  9.     DEFINE_STRING( IMAGE_FILE_MACHINE_I386 ),
  10.     DEFINE_STRING( IMAGE_FILE_MACHINE_IA64 ),
  11.     TABLE_END
  12. };
  13.  
  14. CONST_DESCRIPTION DumpTypes[] = {
  15.     DEFINE_STRING( DUMP_TYPE_TRIAGE ),
  16.     DEFINE_STRING( DUMP_TYPE_SUMMARY ),
  17.     DEFINE_STRING( DUMP_TYPE_FULL ),
  18.     TABLE_END
  19. };
  20.  
  21. // Bugcheck descriptions
  22. typedef ULONG NTSTATUS;
  23. #include "D:\Progs\driverdev\bcdesc.h"    // codes of bsods, I do not provide
  24.                                           // this file, there is BugCheckDescf
  25.                                           // function which gets bsod name by
  26.                                           // it's number
  27.  
  28. // Get constant's name by it's value
  29. LPSTR LookupConstDesc( CONST_DESCRIPTION* Table, ULONG Value )
  30. {
  31.     while( Table->Desc ) {
  32.         if( Table->Value == Value ) {
  33.             return Table->Desc;
  34.         }
  35.         Table ++;
  36.     }
  37.     return "(unknown)";
  38. }
  39.  
  40. void ExtractDumpHeader( MappedCrashDump* CrashDump, char* saveto )
  41. {
  42.     HANDLE hFile = CreateFile( saveto, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0 );
  43.     DWORD wr;
  44.     WriteFile( hFile, CrashDump->lpMapping, 0x1000, &wr, 0 );
  45.     SetEndOfFile( hFile );
  46.     CloseHandle( hFile );
  47. }
  48.  
  49.  
  50. int AnalyseDump( char* filename )
  51. {
  52.     MappedCrashDump CrashDump = {0};
  53.     CrashDump.hFile = INVALID_HANDLE_VALUE;
  54.  
  55.     __try
  56.     {
  57.         CrashDump.hFile = CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0 );
  58.         if( CrashDump.hFile == INVALID_HANDLE_VALUE )
  59.             return 0;
  60.  
  61.         CrashDump.hMapping = CreateFileMapping( CrashDump.hFile, 0, PAGE_READONLY, 0, 0, NULL );
  62.         if( CrashDump.hMapping == NULL )
  63.             return 0;
  64.  
  65.         CrashDump.lpMapping = (PBYTE) MapViewOfFile( CrashDump.hMapping, FILE_MAP_READ, 0, 0, 0x10000 ); //0x12000 );
  66.         if( CrashDump.lpMapping == NULL )
  67.             return 0;
  68.  
  69.         PDUMP_HEADER hdr = CrashDump.DumpHeader;
  70.  
  71.         if( hdr->ValidDump != 'PMUD' || hdr->Signature != 'EGAP' ) {
  72.             printf("Invalid dump header\n");
  73.             return 0;
  74.         }
  75.  
  76.         printf("Crash dump '%s' analysing started. Version: %d.%d\n\n", filename,
  77.                 hdr->MajorVersion, hdr->MinorVersion);
  78.  
  79.         // Header
  80.         printf("CR3 = 0x%08x                   PfnDatabase = 0x%08x\n"
  81.                "PsLoadedModuleList = 0x%08x    PsActiveProcessHead = 0x%08x\n",
  82.                 hdr->DirectoryTableBase, (ULONG_PTR)hdr->PfnDataBase,
  83.                (ULONG_PTR)hdr->PsLoadedModuleList, (ULONG_PTR)hdr->PsActiveProcessHead );
  84.         printf("Machine type: %s,  NumberProcessors: %d\n",
  85.                 LookupConstDesc( MachineTypes, hdr->MachineImageType ), hdr->NumberProcessors);
  86.         printf("PaeEnabled = %d, KdDebuggerDataBlock = 0x%08x\n", hdr->PaeEnabled,
  87.                 (ULONG_PTR)hdr->KdDebuggerDataBlock);
  88.         printf("\n");
  89.  
  90.         // Bug check
  91.         printf("Bugcheck code %s (0x%08x)\n", BugCheckDescf(hdr->BugCheckCode), hdr->BugCheckCode);
  92.         printf("Arguments[0] = 0x%08x\n", hdr->BugCheckParameter1);
  93.         printf("Arguments[1] = 0x%08x\n", hdr->BugCheckParameter2);
  94.         printf("Arguments[2] = 0x%08x\n", hdr->BugCheckParameter3);
  95.         printf("Arguments[3] = 0x%08x\n", hdr->BugCheckParameter4);
  96.         printf("\n");
  97.  
  98.         // Data blocks
  99.         ULONG* block = CrashDump.DataBlocks;
  100.  
  101.         // Dump type & size
  102.         CrashDump.DumpType  = &block[ DH_DUMP_TYPE     ];
  103.         CrashDump.DumpFlags = &block[ DH_DUMP_TYPE + 1 ];
  104.         CrashDump.DumpSize = (PLARGE_INTEGER) &block[ DH_REQUIRED_DUMP_SPACE ];
  105.         printf( "Dump type: %s, DumpSize: %d bytes (0x%08x)\n",
  106.                 LookupConstDesc( DumpTypes, *CrashDump.DumpType ),
  107.                 CrashDump.DumpSize->LowPart,
  108.                 CrashDump.DumpSize->LowPart );
  109.         printf("\n");
  110.  
  111.         // Physical memory descriptor
  112.         CrashDump.PhysicalMemoryDescriptor = (PPHYSICAL_MEMORY_DESCRIPTOR)( &block[DH_PHYSICAL_MEMORY_BLOCK] );
  113.         if( CrashDump.PhysicalMemoryDescriptor->NumberOfRuns == 'EGAP' )
  114.         {
  115.             printf("PPHYSICAL_MEMORY_DESCRIPTOR: Invalid\n");
  116.         }
  117.         else
  118.         {
  119.             printf( "PPHYSICAL_MEMORY_DESCRIPTOR:\nNumberOfRuns = 0x%08x  NumberOfPages = 0x%08x\n",
  120.                     CrashDump.PhysicalMemoryDescriptor->NumberOfRuns,
  121.                     CrashDump.PhysicalMemoryDescriptor->NumberOfPages );
  122.             for( ULONG i=0;i<CrashDump.PhysicalMemoryDescriptor->NumberOfRuns;i++ )
  123.             {
  124.                 printf( "PPHYSICAL_MEMORY_RUN[%d]: BasePage = 0x%08x   PageCount = 0x%08x\n",
  125.                         i,
  126.                         CrashDump.PhysicalMemoryDescriptor->Run[i].BasePage,
  127.                         CrashDump.PhysicalMemoryDescriptor->Run[i].PageCount );
  128.             }
  129.         }
  130.         printf("\n");
  131.  
  132.         // Context record:
  133.         CrashDump.Context = (PCONTEXT)( &block[DH_CONTEXT_RECORD] );
  134.         printf( "Context record:\nEip = 0x%08x   ESP = 0x%08x   EBP = 0x%08x\n",
  135.                 CrashDump.Context->Eip,
  136.                 CrashDump.Context->Esp,
  137.                 CrashDump.Context->Ebp );
  138.         printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x ESI=%08x EDI=%08x\n",
  139.             CrashDump.Context->Eax,
  140.             CrashDump.Context->Ebx,
  141.             CrashDump.Context->Ecx,
  142.             CrashDump.Context->Edx,
  143.             CrashDump.Context->Esi,
  144.             CrashDump.Context->Edi,
  145.             CrashDump.Context->Eax);
  146.         printf("\n");
  147.  
  148.         // Analyse dump
  149.         if( *CrashDump.DumpType == DUMP_TYPE_TRIAGE )
  150.         {
  151.             //
  152.             // Minidump
  153.             //
  154.  
  155.             CrashDump.pTriageDumpHeader = (PTRIAGE_DUMP_HEADER)( CrashDump.lpMapping + 0x1000 );
  156.  
  157.             printf("Analysing triage dump header\n");
  158.  
  159.             printf("[-] Not implemented\n");
  160.  
  161.             __asm nop;
  162.         }
  163.         else if( *CrashDump.DumpType == DUMP_TYPE_SUMMARY )
  164.         {
  165.             //
  166.             // Kernel summary dump - only kernel address space available
  167.             //
  168.  
  169.             CrashDump.pSummaryDumpHeader = (PSUMMARY_DUMP_HEADER) ( CrashDump.lpMapping + 0x1000 );
  170.             CrashDump.KernelMemoryMap.SizeOfBitMap = CrashDump.pSummaryDumpHeader->BitmapSize;
  171.             CrashDump.KernelMemoryMap.Buffer = (PULONG) ( CrashDump.pSummaryDumpHeader + 1 );
  172.  
  173.             if( CrashDump.pSummaryDumpHeader->ValidDump != 'PMUD' ) {
  174.                 printf("Invalid summary dump header\n");
  175.                 return 0;
  176.             }
  177.  
  178.             printf("Analyzing summary dump header\n");
  179.             printf( "HeaderSize = 0x%08x   BitmapSize = 0x%08x\n",
  180.                     CrashDump.pSummaryDumpHeader->HeaderSize,
  181.                     CrashDump.pSummaryDumpHeader->BitmapSize );
  182.             printf( "Number of kernel pages in dump: 0x%08x\n\n",
  183.                     CrashDump.pSummaryDumpHeader->Pages );
  184.  
  185.  
  186.             ULONG Virtual = CrashDump.Context->Eip;
  187.             ULONGLONG Physical;
  188.             Physical = VirtualToPhysical( &CrashDump, Virtual  );
  189.  
  190.             printf("Translating virtual address [0x%08x]: 0x%08x\n", Virtual, Physical);
  191.  
  192.             PBYTE MappedEIP = (PBYTE)RetrieveDumpData( &CrashDump, (void*)(ULONG_PTR)CrashDump.Context->Eip );
  193.  
  194.             if( MappedEIP )
  195.             {
  196.                 printf("Bytes at [EIP=%08x]: %02x %02x %02x %02x  %02x %02x %02x %02x\n",
  197.                             CrashDump.Context->Eip,
  198.                             MappedEIP[0], MappedEIP[1], MappedEIP[2], MappedEIP[3],
  199.                             MappedEIP[4], MappedEIP[5], MappedEIP[6], MappedEIP[7] );
  200.             }
  201.             else printf("Memory pointed by EIP is not present\n");
  202.  
  203.             __asm nop;
  204.         }
  205.         else if( *CrashDump.DumpType == DUMP_TYPE_COMPLETE )
  206.         {
  207.             //
  208.             // Complete memory dump - full address space available
  209.             //
  210.  
  211.             ULONG Virtual = CrashDump.Context->Eip;
  212.             ULONGLONG Physical;
  213.             Physical = VirtualToPhysical( &CrashDump, Virtual  );
  214.  
  215.             printf("Translating virtual address [0x%08x]: 0x%08x\n", Virtual, Physical);
  216.  
  217.             PBYTE MappedEIP = (PBYTE)RetrieveDumpData( &CrashDump, (void*)(ULONG_PTR)CrashDump.Context->Eip );
  218.  
  219.             printf("Bytes at [EIP=%08x]: %02x %02x %02x %02x  %02x %02x %02x %02x\n", CrashDump.Context->Eip,
  220.                         MappedEIP[0], MappedEIP[1], MappedEIP[2], MappedEIP[3],
  221.                         MappedEIP[4], MappedEIP[5], MappedEIP[6], MappedEIP[7] );
  222.  
  223.             ExtractDumpHeader( &CrashDump, "crashdump.hdr" );
  224.  
  225.             __asm nop;
  226.  
  227.         }
  228.  
  229.  
  230.         printf("\nDump analysis finished\n");
  231.     }
  232.     __finally
  233.     {
  234.         if( CrashDump.lpMapping )
  235.             UnmapViewOfFile( CrashDump.lpMapping );
  236.  
  237.         if( CrashDump.hMapping )
  238.             CloseHandle( CrashDump.hMapping );
  239.  
  240.         if( CrashDump.hFile != INVALID_HANDLE_VALUE )
  241.             CloseHandle( CrashDump.hFile );
  242.  
  243.         Sleep(INFINITE);
  244.     }
  245.  
  246.     return 0;
  247. }
  248.  
  249.  
  250. int main()
  251. {
  252.     AnalyseDump( "C:\\gr8lkd.dmp" );
  253.     //AnalyseDump( "D:\\memory.dmp" );
  254.  
  255.     return 0;
  256. }

Path to my crash dump used by my gr9lkd utility is already defined.

Output will be something like following:

Код (Text):
  1. Crash dump 'C:\gr8lkd.dmp' analysing started. Version: 15.2600
  2.  
  3. CR3 = 0x00373000                   PfnDatabase = 0x80557b48
  4. PsLoadedModuleList = 0x805531a0    PsActiveProcessHead = 0x80559258
  5. Machine type: IMAGE_FILE_MACHINE_I386,  NumberProcessors: 1
  6. PaeEnabled = 1, KdDebuggerDataBlock = 0x80544ce0
  7.  
  8. Bugcheck code KMODE_EXCEPTION_NOT_HANDLED (0x0000001e)
  9. Arguments[0] = 0x80000004
  10. Arguments[1] = 0xf3b21315
  11. Arguments[2] = 0x00000000
  12. Arguments[3] = 0x00000000
  13.  
  14. Dump type: DUMP_TYPE_FULL, DumpSize: 536403968 bytes (0x1ff8e000)
  15.  
  16. PPHYSICAL_MEMORY_DESCRIPTOR:
  17. NumberOfRuns = 0x00000003  NumberOfPages = 0x0001ff8d
  18. PPHYSICAL_MEMORY_RUN[0]: BasePage = 0x00000001   PageCount = 0x0000009e
  19. PPHYSICAL_MEMORY_RUN[1]: BasePage = 0x00000100   PageCount = 0x00000eff
  20. PPHYSICAL_MEMORY_RUN[2]: BasePage = 0x00001000   PageCount = 0x0001eff0
  21.  
  22. Context record:
  23. Eip = 0xf3b21315   ESP = 0xf8ab4928   EBP = 0xf8ab4c1c
  24. EAX=00000000 EBX=816b7000 ECX=00000000 EDX=00004e24 ESI=00000000 EDI=f8ab4c00
  25.  
  26. Translating virtual address [0xf3b21315]: 0x1d9ac315
  27. Bytes at [EIP=f3b21315]: 58 89 85 d0  fd ff ff 9c
  28.  
  29. Dump analysis finished

III. Creating of own dump

Here is the most interesting thing - creating of own dump file.

First, we will need the undocumented structure KdDebuggerDataBlock, because it will be very helpful for this task. So, it contains following items:

Код (Text):
  1. template <class T>
  2. struct PFUNC {
  3.     T  VirtualAddress;
  4.     ULONG  ZeroField;
  5. };
  6.  
  7. typedef struct _KD_DEBUGGER_DATA_BLOCK {
  8.     ULONG  Unknown1[4];
  9.     ULONG  ValidBlock; // 'GBDK'
  10.     ULONG  Size; // 0x290
  11.     PFUNC<PVOID>  _imp__VidInitialize;
  12.     PFUNC<PVOID>  RtlpBreakWithStatusInstruction;
  13.     ULONG  Unknown2[4];
  14.     PFUNC<PVOID>  KiCallUserMode;
  15.     ULONG  Unknown3[2];
  16.     PFUNC<PVOID>  PsLoadedModuleList;
  17.     PFUNC<PVOID>  PsActiveProcessHead;
  18.     PFUNC<PVOID>  PspCidTable;
  19.     PFUNC<PVOID>  ExpSystemResourcesList;
  20.     PFUNC<PVOID>  ExpPagedPoolDescriptor;
  21.     PFUNC<PVOID>  ExpNumberOfPagedPools;
  22.     PFUNC<PVOID>  KeTimeIncrement;
  23.     PFUNC<PVOID>  KeBugCheckCallbackListHead;
  24.     PFUNC<PVOID>  KiBugCheckData;
  25.     PFUNC<PVOID>  IopErrorLogListHead;
  26.     PFUNC<PVOID>  ObpRootDirectoryObject;
  27.     PFUNC<PVOID>  ObpTypeObjectType;
  28.     PFUNC<PVOID>  MmSystemCacheStart;
  29.     PFUNC<PVOID>  MmSystemCacheEnd;
  30.     PFUNC<PVOID>  MmSystemCacheWs;
  31.     PFUNC<PVOID>  MmPfnDatabase;
  32.     PFUNC<PVOID>  MmSystemPtesStart;
  33.     PFUNC<PVOID>  MmSystemPtesEnd;
  34.     PFUNC<PVOID>  MmSubsectionBase;
  35.     PFUNC<PVOID>  MmNumberOfPagingFiles;
  36.     PFUNC<PVOID>  MmLowestPhysicalPage;
  37.     PFUNC<PVOID>  MmHighestPhysicalPage;
  38.     PFUNC<PVOID>  MmNumberOfPhysicalPages;
  39.     PFUNC<PVOID>  MmMaximumNonPagedPoolInBytes;
  40.     PFUNC<PVOID>  MmNonPagedSystemStart;
  41.     PFUNC<PVOID>  MmNonPagedPoolStart;
  42.     PFUNC<PVOID>  MmNonPagedPoolEnd;
  43.     PFUNC<PVOID>  MmPagedPoolStart;
  44.     PFUNC<PVOID>  MmPagedPoolEnd;
  45.     PFUNC<PVOID>  MmPagedPoolInfo;
  46.     PFUNC<PVOID>  Unknown4;
  47.     PFUNC<PVOID>  MmSizeOfPagedPoolInBytes;
  48.     PFUNC<PVOID>  MmTotalCommitLimit;
  49.     PFUNC<PVOID>  MmTotalCommittedPages;
  50.     PFUNC<PVOID>  MmSharedCommit;
  51.     PFUNC<PVOID>  MmDriverCommit;
  52.     PFUNC<PVOID>  MmProcessCommit;
  53.     PFUNC<PVOID>  MmPagedPoolCommit;
  54.     PFUNC<PVOID>  Unknown5;
  55.     PFUNC<PVOID>  MmZeroedPageListHead;
  56.     PFUNC<PVOID>  MmFreePageListHead;
  57.     PFUNC<PVOID>  MmStandbyPageListHead;
  58.     PFUNC<PVOID>  MmModifiedPageListHead;
  59.     PFUNC<PVOID>  MmModifiedNoWritePageListHead;
  60.     PFUNC<PVOID>  MmAvailablePages;
  61.     PFUNC<PVOID>  MmResidentAvailablePages;
  62.     PFUNC<PVOID>  PoolTrackTable;
  63.     PFUNC<PVOID>  NonPagedPoolDescriptor;
  64.     PFUNC<PVOID>  MmHighestUserAddress;
  65.     PFUNC<PVOID>  MmSystemRangeStart;
  66.     PFUNC<PVOID>  MmUserProbeAddress;
  67.     PFUNC<PVOID>  KdPrintCircularBuffer;
  68.     PFUNC<PVOID>  KdPrintWritePointer;
  69.     PFUNC<PVOID>  KdPrintWritePointer2;
  70.     PFUNC<PVOID>  KdPrintRolloverCount;
  71.     PFUNC<PVOID>  MmLoadedUserImageList;
  72.     PFUNC<PVOID>  NtBuildLab;
  73.     PFUNC<PVOID>  Unknown6;
  74.     PFUNC<PVOID>  KiProcessorBlock;
  75.     PFUNC<PVOID>  MmUnloadedDrivers;
  76.     PFUNC<PVOID>  MmLastUnloadedDriver;
  77.     PFUNC<PVOID>  MmTriageActionTaken;
  78.     PFUNC<PVOID>  MmSpecialPoolTag;
  79.     PFUNC<PVOID>  KernelVerifier;
  80.     PFUNC<PVOID>  MmVerifierData;
  81.     PFUNC<PVOID>  MmAllocateNonPagedPool;
  82.     PFUNC<PVOID>  MmPeakCommitment;
  83.     PFUNC<PVOID>  MmTotalCommitLimitMaximum;
  84.     PFUNC<PVOID>  CmNtCSDVersion;
  85.     PFUNC<PPHYSICAL_MEMORY_DESCRIPTOR*>  MmPhysicalMemoryBlock;
  86.     PFUNC<PVOID>  MmSessionBase;
  87.     PFUNC<PVOID>  MmSessionSize;
  88.     PFUNC<PVOID>  Unknown7;
  89.  
  90. } KD_DEBUGGER_DATA_BLOCK, *PKD_DEBUGGER_DATA_BLOCK;

It is very convenient! We need only to get one non-exported address of KdDebuggerDataBlock, and every other required adresses are in our reach. Field "ValidBlock" should contain 'GBDK' signature, and Size should be equal to sizeof(KD_DEBUGGER_DATA_BLOCK).

Getting of KdDebuggerDataBlock address is possible via exported symbol KdDebuggerDataBlock = *(PVOID*)((ULONG)KeCapturePersistentThreadState + *(ULONG*)((ULONG)KeCapturePersistentThreadState + 0xC )+ 0x11);

Not very nice, but powerful: we get a lot of non-exported addresses of innet structures. We will need it to fill a dump header.

Now, as we know everything we need, I will show part of code for filling the first dump page.

Код (Text):
  1. BOOLEAN
  2. InitializeDumpHeader(
  3.     IN  PBYTE HeaderPage
  4.     )
  5. {
  6.     PDUMP_HEADER hdr;
  7.     ULONG* blocks;
  8.     PKD_DEBUGGER_DATA_BLOCK KdDebuggerDataBlock;
  9.     EXCEPTION_RECORD exception;
  10.     PVOID KeCapturePersistentThreadState;
  11.     UNICODE_STRING uKeCapturePersistentThreadState;
  12.     PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock;
  13.     CONTEXT ctx = {CONTEXT_FULL};
  14.     PEXCEPTION_POINTERS pei;
  15.    
  16.     // Get context
  17.     __asm
  18.     {
  19.         // Common registers
  20.         mov [ctx.Eax], eax
  21.         mov [ctx.Ebx], ebx
  22.         mov [ctx.Ecx], ecx
  23.         mov [ctx.Edx], edx
  24.         mov [ctx.Esi], esi
  25.         mov [ctx.Edi], edi
  26.  
  27.         // Control registers
  28.         mov [ctx.Esp], esp
  29.         mov [ctx.Ebp], ebp
  30.  
  31.         call _1
  32.         // This address will appear in kd as crash address:
  33. _1:     pop eax
  34.         mov [ctx.Eip], eax
  35.  
  36.         pushfd
  37.         pop eax
  38.         mov [ctx.EFlags], eax
  39.  
  40.         // Debug registers
  41.         __emit 0x0F
  42.         __emit 0x21
  43.         __emit 0xC0 ; mov eax, dr0
  44.         mov [ctx.Dr0], eax
  45.         __emit 0x0F
  46.         __emit 0x21
  47.         __emit 0xC8 ; mov eax, dr1
  48.         mov [ctx.Dr1], eax
  49.         __emit 0x0F
  50.         __emit 0x21
  51.         __emit 0xD0 ; mov eax, dr2
  52.         mov [ctx.Dr2], eax
  53.         __emit 0x0F
  54.         __emit 0x21
  55.         __emit 0xD8 ; mov eax, dr3
  56.         mov [ctx.Dr3], eax
  57.         __emit 0x0F
  58.         __emit 0x21
  59.         __emit 0xF0 ; mov eax, dr6
  60.         mov [ctx.Dr6], eax
  61.         __emit 0x0F
  62.         __emit 0x21
  63.         __emit 0xF8 ; mov eax, dr7
  64.         mov [ctx.Dr7], eax
  65.  
  66.         // Segment registers
  67.         push cs
  68.         pop eax
  69.         mov [ctx.SegCs], eax
  70.         xor eax,eax
  71.         mov ax, ss
  72.         mov [ctx.SegSs], eax
  73.         mov ax, ds
  74.         mov [ctx.SegDs], eax
  75.         mov ax, es
  76.         mov [ctx.SegEs], eax
  77.         mov ax, fs
  78.         mov [ctx.SegFs], eax
  79.         mov ax, gs
  80.         mov [ctx.SegGs], eax
  81.     }
  82.  
  83.     // Get KeCapturePersistentThreadState address
  84.     RtlInitUnicodeString( &uKeCapturePersistentThreadState, L"KeCapturePersistentThreadState" );
  85.     KeCapturePersistentThreadState = MmGetSystemRoutineAddress( &uKeCapturePersistentThreadState );
  86.  
  87.     // Initialize dump header
  88.     hdr = (PDUMP_HEADER) HeaderPage;
  89.     hdr->ValidDump = 'PMUD';
  90.     hdr->MinorVersion = (USHORT) *NtBuildNumber;
  91.     hdr->MajorVersion = (USHORT) 0xF; // checked/free, ideally we should get
  92.                                          // info from registry; if you look at
  93.                                          // at code in the article attachment -
  94.                                          // it is how it was done there
  95.     hdr->DirectoryTableBase = CR3();
  96.  
  97.     //
  98.     // Capture KdDebuggerDataBlock
  99.     //
  100.    
  101.     __try {
  102.         hdr->KdDebuggerDataBlock = *(PVOID*)((ULONG)KeCapturePersistentThreadState +
  103.                 *(ULONG*)((ULONG)KeCapturePersistentThreadState + 0xC )+ 0x11);
  104.  
  105.     } __except( (pei=GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER ) {
  106.         ULONG i;
  107.  
  108.         DbgPrint("An exception occurred while trying to get KdDebuggerDataBlock address:\n");
  109.         DbgPrint("Exception code: 0x%08x\n", pei->ExceptionRecord->ExceptionCode);
  110.         DbgPrint("Number of arguments: 0x%08x\n", pei->ExceptionRecord->NumberParameters);
  111.  
  112.         for( i = 0; i < pei->ExceptionRecord->NumberParameters; i++ ) {
  113.             DbgPrint("Argument[%d]: 0x%08x\n", i, pei->ExceptionRecord->ExceptionInformation[i]);
  114.         }
  115.  
  116.         return FALSE;
  117.     }
  118.     hdr->MachineImageType = 0x14c;
  119.     hdr->NumberProcessors = 1;
  120.     hdr->BugCheckCode = KMODE_EXCEPTION_NOT_HANDLED;
  121.     hdr->BugCheckParameter1 = STATUS_BREAKPOINT;
  122.     hdr->BugCheckParameter2 = ctx.Eip;
  123.     hdr->BugCheckParameter3 = 0;
  124.     hdr->BugCheckParameter4 = 0;
  125.     hdr->PaeEnabled = (CR4() & PAE_ENABLED) ? TRUE : FALSE;
  126.  
  127.     KdDebuggerDataBlock = (PKD_DEBUGGER_DATA_BLOCK) hdr->KdDebuggerDataBlock;
  128.  
  129.     // Check KdDebuggerDataBlock
  130.     if( KdDebuggerDataBlock->ValidBlock != 'GBDK' || KdDebuggerDataBlock->Size != sizeof(*KdDebuggerDataBlock) )
  131.     {
  132.         // Invalid debugger data block
  133.         DbgPrint(   "KdDebuggerDataBlock is not valid.\nSignature = 0x%08x (should be 0x%08x)\nSize = 0x%08x
  134.                     (should be 0x%08x)\n",
  135.                     KdDebuggerDataBlock->ValidBlock, 'GBDK',
  136.                     KdDebuggerDataBlock->Size, sizeof(*KdDebuggerDataBlock) );
  137.         return FALSE;
  138.     }
  139.  
  140.     DbgPrint("Got valid KdDebuggerDataBlock=0x%08x\n", KdDebuggerDataBlock);
  141.    
  142.     hdr->PfnDataBase = (PULONG) KdDebuggerDataBlock->MmPfnDatabase.VirtualAddress;
  143.     hdr->PsLoadedModuleList = (PLIST_ENTRY) KdDebuggerDataBlock->PsLoadedModuleList.VirtualAddress;
  144.     hdr->PsActiveProcessHead = (PLIST_ENTRY) KdDebuggerDataBlock->PsActiveProcessHead.VirtualAddress;
  145.  
  146.     DbgPrint("PfnDataBase = 0x%08x\n", hdr->PfnDataBase);
  147.     DbgPrint("PsLoadedModuleList = 0x%08x\n", hdr->PsLoadedModuleList);
  148.     DbgPrint("PsActiveProcessHead = 0x%08x\n", hdr->PsActiveProcessHead);
  149.  
  150.     blocks = (ULONG*)(ULONG_PTR)HeaderPage;
  151.  
  152.     //
  153.     // Get physical memory descriptor
  154.     //
  155.  
  156.     MmPhysicalMemoryBlock = *(KdDebuggerDataBlock->MmPhysicalMemoryBlock.VirtualAddress);
  157.  
  158.     DbgPrint("MmPhysicalMemoryBlock = 0x%08x\n", MmPhysicalMemoryBlock);
  159.  
  160.     if( MmPhysicalMemoryBlock->NumberOfRuns == 'EGAP' ) {
  161.         RtlCopyMemory(  &blocks[ DH_PHYSICAL_MEMORY_BLOCK ],
  162.                         MmPhysicalMemoryBlock,
  163.                         sizeof(PHYSICAL_MEMORY_DESCRIPTOR)
  164.                         );
  165.     } else {
  166.         RtlCopyMemory(  &blocks[ DH_PHYSICAL_MEMORY_BLOCK ],
  167.                         MmPhysicalMemoryBlock,
  168.                         sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) +
  169.                         sizeof(PHYSICAL_MEMORY_RUN)*MmPhysicalMemoryBlock->NumberOfRuns
  170.                         );
  171.     }
  172.  
  173.     //
  174.     // Save context record
  175.     //
  176.  
  177.     RtlCopyMemory(  &blocks[ DH_CONTEXT_RECORD ],
  178.                     &ctx,
  179.                     sizeof(CONTEXT)
  180.                     );
  181.     DbgPrint("Context record saved.\n");
  182.     DbgPrint("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx);
  183.     DbgPrint("ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", ctx.Esi, ctx.Edi, ctx.Ebp, ctx.Esp);
  184.     DbgPrint("EIP=%08x CS=%08x FS=%08x DS=%08x\n", ctx.Eip, ctx.SegCs, ctx.SegFs, ctx.SegDs);
  185.  
  186.     //
  187.     // Create & store exception record
  188.     //
  189.  
  190.     exception.ExceptionCode = STATUS_BREAKPOINT;
  191.     exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
  192.     exception.ExceptionRecord = NULL;
  193.     exception.ExceptionAddress = (PVOID) ctx.Eip;
  194.     exception.NumberParameters = 0;
  195.  
  196.     RtlCopyMemory(  &blocks[ DH_EXCEPTION_RECORD ],
  197.                     &exception,
  198.                     sizeof(EXCEPTION_RECORD)
  199.                     );
  200.  
  201.     //
  202.     // Initialize dump type & size
  203.     //
  204.  
  205.     blocks[ DH_DUMP_TYPE ] = DUMP_TYPE_COMPLETE;
  206.  
  207.     ((LARGE_INTEGER*)&blocks[DH_REQUIRED_DUMP_SPACE])->QuadPart = ( MmPhysicalMemoryBlock->NumberOfPages << 12 ) + 0x1000;
  208.  
  209.     DbgPrint("Header page initialized OK\n");
  210.  
  211.     return TRUE;
  212. }

That's all. In the article's attachments you can find more fresh version of gr8lkd and sources of AnalyseCrashDump and gendump driver.

Attachments:

© Great, пер. Aquila

0 3.551
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532