Dump file¶
Level: 201 · for anyone with a hex editor open
One line: The dump loader reads one little-endian integer at offset 0 and matches it against four constants that are all four ASCII letters — MDMP, PAGE, USER and Prob, the last being the first four letters of ProblemType:, the first line of an Ubuntu apport report — so one text file is recognised exactly as three binary ones are, and inside a minidump every string is a byte length followed by UTF-16.
What the loader reads¶
Ghidra's Dump File Loader is one loader for four crash-dump formats: Windows minidumps (.dmp from Task Manager or procdump), Windows kernel dumps (MEMORY.DMP), the old Windows user dumps, and Ubuntu's apport reports — the .crash text files under /var/crash, which carry the core dump base64-encoded in a field. Three have a binary header; the fourth is text that begins with the line ProblemType: Crash.
A minidump, the common one, is documented in Microsoft's MINIDUMP_HEADER ↗:
| offset | field | width | what it says |
|---|---|---|---|
| 0 | Signature |
4 | MDMP |
| 4 | Version |
4 | low word 0xA793, MINIDUMP_VERSION; high word the writer's |
| 8 | NumberOfStreams |
4 | |
| 12 | StreamDirectoryRva |
4 | where the directory is: 32, usually |
| 16 | CheckSum, TimeDateStamp |
4 each | |
| 24 | Flags |
8 | which MINIDUMP_TYPE bits the writer used |
The directory is twelve bytes per stream — a type, a size and an RVA — and the streams are the content: threads, modules, memory ranges, and stream 7, SystemInfo, whose first 16-bit field is the processor architecture. Every string in the file is a MINIDUMP_STRING: a 32-bit byte length, UTF-16LE, and a 16-bit NUL that the length does not count.
In Python¶
Verified output of dump_file_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. FOUR SIGNATURES, READ AS ONE LITTLE-ENDIAN INTEGER
------------------------------------------------------------------------
Minidump constant 0x504d444d bytes 4d 44 4d 50 spells 'MDMP'
Pagedump constant 0x45474150 bytes 50 41 47 45 spells 'PAGE'
Userdump constant 0x52455355 bytes 55 53 45 52 spells 'USER'
Apport constant 0x626f7250 bytes 50 72 6f 62 spells 'Prob'
Ghidra reads a 32-bit little-endian integer at offset 0 and switches
on it. Three are the first four bytes of a binary header -- MDMP,
PAGE (then DUMP or DU64), USER (then DUMP). The fourth is the first
four letters of 'ProblemType:', the first line of an Ubuntu apport
crash report, which is a text file recognised as if it were binary.
50 41 47 45 44 55 4d 50 first int 0x45474150 -> Pagedump second int 0x504d5544 spells 'DUMP' PAGEDUMP, a 32-bit kernel dump
50 41 47 45 44 55 36 34 first int 0x45474150 -> Pagedump second int 0x34365544 spells 'DU64' PAGEDU64, a 64-bit one
ef bb bf 50 72 6f 62 6c first int 0x50bfbbef -> no loader second int 0x6c626f72 spells 'robl' a BOM in front of ProblemType
The BOM row is the text file's hazard: three invisible bytes and
the integer compare misses. And the second word of a kernel dump
is DUMP, 0x504d5544, or DU64, 0x34365544 -- Ghidra's source holds
0x504d5444 for both, which spells 'DTMP'; a comment says DUMP.
2. THE MINIDUMP HEADER IS 32 BYTES, THEN A DIRECTORY
------------------------------------------------------------------------
4d 44 4d 50 93 a7 34 12 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Signature b'MDMP'
Version 0x1234a793 low word 0xa793 = MINIDUMP_VERSION; high word 0x1234 is the writer's
Streams 2 at RVA 32 Flags 0x0
directory[0] type 7 SystemInfo 48 bytes at RVA 56
directory[1] type 4 ModuleList 112 bytes at RVA 104
An RVA here is a plain file offset -- the dump is not mapped -- and
every stream is found through the directory, never by position.
3. THE ARCHITECTURE IS A 16-BIT NUMBER IN THE SYSTEMINFO STREAM
------------------------------------------------------------------------
ProcessorArchitecture 9 = AMD64 level 6 revision 0x3f03 8 processors Windows 10.0 build 22631
This is the field Ghidra's Minidump.getMachineType() returns, as a
decimal string, to pick a language: 0 x86, 5 ARM, 9 AMD64, 12 ARM64.
4. A MINIDUMP_STRING IS A BYTE LENGTH, THEN UTF-16LE, THEN A NUL
------------------------------------------------------------------------
1 module base 0x7ff600000000 size 0x20000 ModuleNameRva 216
at 216: Length 16 bytes 63 00 61 00 66 00 e9 00 2e 00 64 00 6c 00 6c 00
-> 'café.dll' (8 code points, 8 UTF-16 units)
at 238: Length 12 bytes 3d d8 00 de 2e 00 64 00 6c 00 6c 00
-> '😀.dll' (5 code points, 6 UTF-16 units)
Length counts BYTES of UTF-16, excluding the terminator: café.dll
is 8 characters and 16 bytes, and the emoji name is 5 code points,
6 units, 12 bytes. Three numbers for one name, and the field holds
the one a C++ wchar_t buffer needs.
A text file, tested as a number¶
Section 1 is the reason this page is in the chapter. Ghidra's loader does not have four signature tests; it has one integer read and a switch, and the fourth case is 0x626F7250 — the bytes 50 72 6f 62, which spell Prob. That is the beginning of ProblemType:, the first line of every apport report, and so a UTF-8 text file is recognised by the same arithmetic as a binary header. It works because the first line of that format is fixed, and it fails in the way text files fail: put a byte-order mark in front and the integer is 0x50BFBBEF, which matches nothing. The kernel-dump signature is text too, eight bytes of it, PAGEDUMP or PAGEDU64 — and the constant Ghidra's source holds for the second word spells DTMP, one letter off from DUMP, beside a comment that says DUMP. Section 1 computes both; whether the loader's second check ever matches a real kernel dump is a question for the loader, and this page does not claim an answer.
Three lengths for one name, again¶
A MINIDUMP_STRING is section 4. Its length field counts bytes of UTF-16, not characters and not code points: café.dll is 8 characters and 16 bytes; an emoji in a module name is 1 code point, 2 UTF-16 units, 4 bytes. DEX stores the unit count; the Java class file the MUTF-8 byte count; a minidump the UTF-16 byte count — three formats, three of the five rulers from A code point is not a character, each one the number its own runtime wanted without decoding. The terminator is outside the count, and a reader that adds two for it and a reader that does not disagree about where the next string begins.
What Ghidra checks¶
DumpFileLoader ↗, in the FileFormats module, reads readInt(0) little-endian and switches on Pagedump.SIGNATURE, Userdump.SIGNATURE, Minidump.SIGNATURE and Apport.SIGNATURE; each class then reads its own header to name a machine type, which for a minidump is the ProcessorArchitecture of the SystemInfo stream rendered as a decimal string — 9 for AMD64 — and matched against Ghidra's language table. The name it shows is Dump File Loader, the only entry on the list named after the loader rather than a format.
If you are coming from Python or ABAP¶
Python. struct.unpack_from('<4sIIIIIQ', data, 0) is the header, and '<III' twelve bytes at a time is the directory. For a MINIDUMP_STRING, read the length, slice that many bytes, and decode('utf-16-le') — never 'utf-16', which would look for a BOM the field does not have, and never divide by two and hope. The apport format is plain text with Key: value lines and a base64 CoreDump field; email.parser will not read it, but line.split(': ', 1) will.
ABAP. (Not machine-checked — CI cannot run ABAP.) A UTF-16 byte length is the number an ABAP string does not give you directly: strlen counts units, and the byte count is 2 * strlen on a Unicode system — which is exactly the arithmetic a minidump writer did. The apport case is the more useful pattern: a text format with a binary payload armoured in base64 is what cl_http_utility=>encode_base64 produces, and the first-line signature test is what an inbound interface does when it reads four bytes and branches. Keep the BOM out of the first line, or the branch is never taken.
Try it¶
- On Windows, make a minidump of any process from Task Manager and
xxd -l 32it. Read the version's low word and the stream count. - Follow
StreamDirectoryRvaand list the twelve-byte entries. Find type 7, then read the first two bytes of that stream. - Find a module name: stream 4, the
ModuleNameRvafield, then the length and the UTF-16. Count bytes, units and characters. - On Ubuntu,
head -c 4 /var/crash/*.crash | xxd.50 72 6f 62. Thenhead -1. - Prepend a BOM to a copy of a
.crashfile and offer both to a tool that reads them. One of the two is not a crash report any more.
See also¶
- Byte order and the BOM — the three bytes that break a first-line signature
- UTF-16 and surrogates — why an emoji is two units and four bytes in a module name
- A code point is not a character — the five rulers; a minidump's length field is the fourth
- PE — the modules a minidump lists are PE images, and the loader maps them back
- Binary to text — base64, which is how an apport report carries a core dump inside a text file
MINIDUMP_HEADER↗ andMINIDUMP_STRING↗ — the two structures this page was checked against