minidump

Process explorer has a pretty cool functionality about generating process dumps. It has two types of dumps: mini and full. Now the full dump generates a massive dmp file and encodes all the process memory, but often that is not really required (for various reasons), but more importantly the helpful information is about threads and handles.

Microsoft provides the API for generating dumpfiles programmatically, MiniDumpWriteDump . Now the key to write a dump is to OpenProcess with appropriate access.

To generate a dump file, then all you need is to:

  1. open process with appropriate access
  2. specify the minidump options

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    HANDLE hFile = CreateFile(L"d:\\memory.dmp", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hFile == INVALID_HANDLE_VALUE) 
    {
        printf("Can't create memory.dmp. Exiting (%ld)\n", GetLastError());
        CloseHandle(hProc);
        ExitProcess(0);
    }
    
    bool bSuccess = MiniDumpWriteDump(hProc, PID, hFile, dumpOpts, nullptr, nullptr, nullptr);
    printf("Process Completed (%d)(%ld)", (DWORD)bSuccess, GetLastError());
    

In all of this key thing is to specify the dumpOpts correctly. One of the ways to ascertain the options is to look at what process explorer generated. For that open the dumpfile in windbg (windbg -z memory.dmp) and execute command .dumpdebug. It will produce following output (trimmed in this case).

The header just contains the flags as provided to MiniDumpWriteDump, this does not necessarily means that requested data is written in the dump file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
0:000> .dumpdebug
----- User Mini Dump Analysis

MINIDUMP_HEADER:
Version         A793 (A063)
NumberOfStreams 15
Flags           1105
                0001 MiniDumpWithDataSegs
                0004 MiniDumpWithHandleData
                0100 MiniDumpWithProcessThreadData
                1000 MiniDumpWithThreadInfo
                
                

Followed by various streams as captured in dump. The stream types are defined in DbgHelp.h/minidumpapiset.h

The size of streams seems like it is in bytes, 364 vs 18 threads.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Streams:
Stream 0: type ThreadListStream (3), size 00000364, RVA 00000660
  18 threads
  RVA 00000664, ID 4554, Teb:000000CB58643000
  RVA 00000694, ID 3A78, Teb:000000CB5864B000
  ...
Stream 1: type ThreadInfoListStream (17), size 0000048C, RVA 000009C4
  RVA 000009D0, ID 4554
  RVA 00000A10, ID 3A78
  ...
Stream 2: type ModuleListStream (4), size 0000A198, RVA 00000E50
  383 modules
  RVA 00000E54, 00007ff6`0e940000 - 00007ff6`0e94d000: 'd:\sample.exe', 8160
  RVA 00000EC0, 00007ff9`85150000 - 00007ff9`8533d000: 'C:\Windows\System32\ntdll.dll', 4160
  RVA 00000F2C, 00007ff9`82370000 - 00007ff9`82423000: 'C:\Windows\System32\kernel32.dll', 4160
  RVA 00000F98, 00007ff9`81e30000 - 00007ff9`820c3000: 'C:\Windows\System32\KERNELBASE.dll', 4160
  RVA 00001004, 00007ff9`81370000 - 00007ff9`8146a000: 'C:\Windows\System32\ucrtbase.dll', 4160
  ...
Stream 3: type MemoryListStream (5), size 0000DE34, RVA 0002371E
  3555 memory ranges
  range#    RVA      Address             Size
       ...
  Total memory: 196bdd5
Stream 4: type SystemInfoStream (7), size 00000038, RVA 000000D4
...
Stream 5: type MiscInfoStream (15), size 00000554, RVA 0000010C
Stream 6: type HandleDataStream (12), size 00008A30, RVA 019A65AD
  884 descriptors, header size is 16, descriptor size is 40
    Handle(0000000000000004,"Event","")
    Handle(0000000000000008,"Key","\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options")
    Handle(000000000000000C,"Key","\REGISTRY\USER\S-1-5-21-954228201-601818101-482762101-192574")
    Handle(0000000000000010,"Event","")
    Handle(0000000000000014,"WaitCompletionPacket","")
    Handle(0000000000000018,"IoCompletion","")
    Handle(000000000000001C,"TpWorkerFactory","")
    Handle(0000000000000020,"IRTimer","")
    Handle(0000000000000024,"WaitCompletionPacket","")
    Handle(0000000000000028,"IRTimer","")
    Handle(000000000000002C,"WaitCompletionPacket","")
    Handle(0000000000000030,"","")
    Handle(0000000000000034,"","")
    Handle(0000000000000038,"","")
    Handle(000000000000003C,"Directory","\KnownDlls")
    Handle(0000000000000040,"Event","")
    Handle(0000000000000044,"Event","")
    ...

The flags passed are 0x1105. Now if you have just open the process with PROCESS_ALL_ACCESS (0x1FFFF), you will get the handle data in dumpfile. Once you have generated the dump file open in windbg again and check with !handle command. It should list handles from dump if it could capture else it will just provide you with some error message.

1
2
3
0:000> !handle
ERROR: !handle: extension exception 0x80004002.
    "Unable to read handle information"

more on this in next post


Entire code is along these lines:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <windows.h>


BOOL typedef (*MiniDumpWriteDump_fn_t)
(
    HANDLE      hProcess,
    DWORD       ProcessId,
    HANDLE      hFile,
    DWORD       DumpType,
    VOID*       ExceptionParam,
    VOID*       UserStreamParam,
    VOID*       CallbackParam
);

//-------------------------------------------------------------------------------------------
static std::string processIdToName(HANDLE hProc)
{
    std::string ret;
    if (handle)
    {
        DWORD buffSize = 1024;
        CHAR buffer[1024];
        
        if (QueryFullProcessImageNameA(hProc, 0, buffer, &buffSize))
            ret = buffer;
        else
            std::cout << "Error GetModuleBaseNameA : (" << GetLastError() << ")" << std::endl;
    }
    
    return ret;
}

//-------------------------------------------------------------------------------------------

typedef enum _MINIDUMP_TYPE
{
    MiniDumpNormal                          = 0x00000000,
    MiniDumpWithDataSegs                    = 0x00000001,
    MiniDumpWithFullMemory                  = 0x00000002,
    MiniDumpWithHandleData                  = 0x00000004,
    MiniDumpFilterMemory                    = 0x00000008,
    MiniDumpScanMemory                      = 0x00000010,
    MiniDumpWithUnloadedModules             = 0x00000020,
    MiniDumpWithIndirectlyReferencedMemory  = 0x00000040,
    MiniDumpFilterModulePaths               = 0x00000080,
    MiniDumpWithProcessThreadData           = 0x00000100,
    MiniDumpWithPrivateReadWriteMemory      = 0x00000200,
    MiniDumpWithoutOptionalData             = 0x00000400,
    MiniDumpWithFullMemoryInfo              = 0x00000800,
    MiniDumpWithThreadInfo                  = 0x00001000,
    MiniDumpWithCodeSegs                    = 0x00002000,
    MiniDumpWithoutManagedState             = 0x00004000,
} MINIDUMP_TYPE;

//-------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{

    //  MINIDUMP_TYPE dumpOpts = (MINIDUMP_TYPE) ((int)MiniDumpNormal | (int)MiniDumpWithDataSegs | (int)MiniDumpWithHandleData
    //          | (int)MiniDumpWithProcessThreadData | (int)MiniDumpWithThreadInfo);
    auto dumpOpts = 0x1105;

    static MiniDumpWriteDump_fn_t minDmpFn = (MiniDumpWriteDump_fn_t)GetProcAddress(LoadLibrary(L"Dbghelp.dll"), "MiniDumpWriteDump");
    if (minDmpFn == nullptr)
    {
        std::cout << "could not find dbghelp.dll" << std::endl;
        return 0;
    }

    if (argc != 2)
    {
        std::cout << "incorrect usage of command" << std::endl;
        return 0;
    }

    unsigned int PID = std::atoi(argv[1]);
    if (PID == -1)
    {
        std::cout << "invalid PID provided" << std::endl;
        return 0;
    }

    auto accessVal = PROCESS_ALL_ACCESS;
    HANDLE hProc = OpenProcess(accessVal, FALSE, PID);
    
    if (hProc == nullptr)
    {
        std::cout << "HANDLE is nullptr. Exiting (" << GetLastError() << ")" << std::endl;
        return 0;
    }
    std::cout << "generating minidump for " << processIdToName(PID) << " (pid: "<<  PID<< ", handle: " << hProc << ") " << std::endl;

    HANDLE hFile = CreateFile(L"d:\\memory.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    std::cout << "memory.dmp HANDLE " << std::hex << hFile << std::endl;

    if (hFile == INVALID_HANDLE_VALUE) 
    {
        std::cout << " Can't create memory.dmp. Exiting (" << GetLastError() << ")" << std::endl;
        CloseHandle(hProc);
        ExitProcess(0);
    }

    bool bSuccess = minDmpFn(hProc, PID, hFile, dumpOpts, nullptr, nullptr, nullptr);
    std::cout << "Process Completed (" <<  (DWORD)bSuccess << ")(" << GetLastError() << ")" << std::endl;

    CloseHandle(hFile);
    CloseHandle(hProc);

    return 0;
}