Task 392: .MDMP File Format

Task 392: .MDMP File Format

File Format Specifications for .MDMP

The .MDMP file format is the Windows Minidump format, used to store compressed data dumps from program crashes for debugging purposes. It is a container format consisting of a header followed by a directory of streams, each containing specific types of data (e.g., system info, threads, memory regions, exceptions). The format is defined in Microsoft documentation and can be parsed using binary structures. The signature is always "MDMP" followed by the bytes 0x93 0xA7.

  1. List of all the properties of this file format intrinsic to its file system:
  • Signature: A 4-byte string "MDMP" identifying the file as a Minidump.
  • ImplementationVersion: 2 bytes fixed as 0x93 0xA7, part of the magic identifier.
  • Version: A 2-byte unsigned integer representing the format version (low word of the overall version field).
  • NumberOfStreams: A 4-byte unsigned integer indicating the count of data streams in the file.
  • StreamDirectoryRva: A 4-byte unsigned integer RVA (relative virtual address) pointing to the start of the stream directory.
  • CheckSum: A 4-byte unsigned integer checksum of the file (may be zero if not used).
  • TimeDateStamp: A 4-byte unsigned integer timestamp in UNIX time_t format, recording when the dump was created.
  • Flags: An 8-byte unsigned integer bitfield from the MINIDUMP_TYPE enumeration, specifying the type of data included (e.g., MiniDumpNormal, MiniDumpWithFullMemory).

These properties form the fixed header structure (32 bytes total) and are essential for parsing the file's layout and integrity.

Two direct download links for files of format .MDMP:

Ghost blog embedded HTML JavaScript for drag and drop .MDMP file dump:

MDMP Properties Dumper

Drag and Drop .MDMP File

Drop .MDMP file here

Python class for .MDMP handling:

import struct
import os
import time

class MdmpHandler:
    HEADER_FORMAT = '<4s2B H I I I I Q'  # Signature(4s), ImplVer(2B), Ver(H), NumStreams(I), DirRva(I), Checksum(I), Timestamp(I), Flags(Q)
    HEADER_SIZE = 32

    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}

    def read_and_decode(self):
        with open(self.filepath, 'rb') as f:
            header_data = f.read(self.HEADER_SIZE)
            if len(header_data) < self.HEADER_SIZE:
                raise ValueError("File too small for MDMP header")
            unpacked = struct.unpack(self.HEADER_FORMAT, header_data)
            self.properties = {
                'Signature': unpacked[0].decode('ascii'),
                'ImplementationVersion': f'0x{unpacked[1]:02x} 0x{unpacked[2]:02x}',
                'Version': unpacked[3],
                'NumberOfStreams': unpacked[4],
                'StreamDirectoryRva': unpacked[5],
                'CheckSum': unpacked[6],
                'TimeDateStamp': unpacked[7],
                'Flags': unpacked[8]
            }

    def print_properties(self):
        if not self.properties:
            print("No properties loaded. Call read_and_decode first.")
            return
        for key, value in self.properties.items():
            if key == 'TimeDateStamp':
                print(f"{key}: {value} (Date: {time.ctime(value)})")
            else:
                print(f"{key}: {value}")

    def write(self, new_filepath=None):
        if not self.properties:
            raise ValueError("No properties to write. Call read_and_decode first.")
        if new_filepath is None:
            new_filepath = self.filepath + '.modified'
        with open(self.filepath, 'rb') as f_in:
            original_data = f_in.read()
        # Re-pack header with possibly modified properties
        packed_header = struct.pack(
            self.HEADER_FORMAT,
            self.properties['Signature'].encode('ascii'),
            int(self.properties['ImplementationVersion'].split()[0][2:], 16),
            int(self.properties['ImplementationVersion'].split()[1][2:], 16),
            self.properties['Version'],
            self.properties['NumberOfStreams'],
            self.properties['StreamDirectoryRva'],
            self.properties['CheckSum'],
            self.properties['TimeDateStamp'],
            self.properties['Flags']
        )
        with open(new_filepath, 'wb') as f_out:
            f_out.write(packed_header + original_data[self.HEADER_SIZE:])
        print(f"Written to {new_filepath}")

# Example usage:
# handler = MdmpHandler('example.mdmp')
# handler.read_and_decode()
# handler.print_properties()
# handler.properties['CheckSum'] = 0  # Modify example
# handler.write()

Java class for .MDMP handling:

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.util.Date;

public class MdmpHandler {
    private static final int HEADER_SIZE = 32;
    private String filepath;
    private ByteBuffer buffer;
    private String signature;
    private String implementationVersion;
    private short version;
    private int numberOfStreams;
    private int streamDirectoryRva;
    private int checkSum;
    private int timeDateStamp;
    private long flags;

    public MdmpHandler(String filepath) {
        this.filepath = filepath;
    }

    public void readAndDecode() throws IOException {
        byte[] headerBytes = Files.readAllBytes(Paths.get(filepath));
        if (headerBytes.length < HEADER_SIZE) {
            throw new IOException("File too small for MDMP header");
        }
        buffer = ByteBuffer.wrap(headerBytes, 0, HEADER_SIZE).order(ByteOrder.LITTLE_ENDIAN);
        byte[] sigBytes = new byte[4];
        buffer.get(sigBytes);
        signature = new String(sigBytes);
        byte impl1 = buffer.get();
        byte impl2 = buffer.get();
        implementationVersion = String.format("0x%02x 0x%02x", impl1, impl2);
        version = buffer.getShort();
        numberOfStreams = buffer.getInt();
        streamDirectoryRva = buffer.getInt();
        checkSum = buffer.getInt();
        timeDateStamp = buffer.getInt();
        flags = buffer.getLong();
    }

    public void printProperties() {
        System.out.println("Signature: " + signature);
        System.out.println("ImplementationVersion: " + implementationVersion);
        System.out.println("Version: " + version);
        System.out.println("NumberOfStreams: " + numberOfStreams);
        System.out.println("StreamDirectoryRva: " + streamDirectoryRva);
        System.out.println("CheckSum: " + checkSum);
        System.out.println("TimeDateStamp: " + timeDateStamp + " (Date: " + new Date((long) timeDateStamp * 1000));
        System.out.println("Flags: " + flags);
    }

    public void write(String newFilepath) throws IOException {
        if (buffer == null) {
            throw new IllegalStateException("No data loaded. Call readAndDecode first.");
        }
        if (newFilepath == null) {
            newFilepath = filepath + ".modified";
        }
        // Update buffer with possibly modified values (for demo, assume modifications via setters)
        buffer.position(0);
        buffer.put(signature.getBytes());
        String[] implParts = implementationVersion.split(" ");
        buffer.put((byte) Integer.parseInt(implParts[0].substring(2), 16));
        buffer.put((byte) Integer.parseInt(implParts[1].substring(2), 16));
        buffer.putShort(version);
        buffer.putInt(numberOfStreams);
        buffer.putInt(streamDirectoryRva);
        buffer.putInt(checkSum);
        buffer.putInt(timeDateStamp);
        buffer.putLong(flags);

        byte[] originalData = Files.readAllBytes(Paths.get(filepath));
        try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
            fos.write(buffer.array(), 0, HEADER_SIZE);
            fos.write(originalData, HEADER_SIZE, originalData.length - HEADER_SIZE);
        }
        System.out.println("Written to " + newFilepath);
    }

    // Setters for modification (example)
    public void setCheckSum(int checkSum) { this.checkSum = checkSum; }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     MdmpHandler handler = new MdmpHandler("example.mdmp");
    //     handler.readAndDecode();
    //     handler.printProperties();
    //     handler.setCheckSum(0);
    //     handler.write(null);
    // }
}

JavaScript class for .MDMP handling:

class MdmpHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = {};
    }

    async readAndDecode() {
        // Note: In Node.js, use fs module
        const fs = require('fs');
        const data = fs.readFileSync(this.filepath);
        const dataView = new DataView(data.buffer);
        this.properties = {
            Signature: String.fromCharCode(dataView.getUint8(0), dataView.getUint8(1), dataView.getUint8(2), dataView.getUint8(3)),
            ImplementationVersion: `0x${dataView.getUint8(4).toString(16).padStart(2, '0')} 0x${dataView.getUint8(5).toString(16).padStart(2, '0')}`,
            Version: dataView.getUint16(6, true),
            NumberOfStreams: dataView.getUint32(8, true),
            StreamDirectoryRva: dataView.getUint32(12, true),
            CheckSum: dataView.getUint32(16, true),
            TimeDateStamp: dataView.getUint32(20, true),
            Flags: dataView.getBigUint64(24, true)
        };
    }

    printProperties() {
        for (const [key, value] of Object.entries(this.properties)) {
            if (key === 'TimeDateStamp') {
                console.log(`${key}: ${value} (Date: ${new Date(value * 1000).toUTCString()})`);
            } else {
                console.log(`${key}: ${value}`);
            }
        }
    }

    write(newFilepath) {
        if (Object.keys(this.properties).length === 0) {
            throw new Error('No properties loaded. Call readAndDecode first.');
        }
        if (!newFilepath) {
            newFilepath = this.filepath + '.modified';
        }
        const fs = require('fs');
        const originalData = fs.readFileSync(this.filepath);
        const headerBuffer = new ArrayBuffer(32);
        const dataView = new DataView(headerBuffer);
        const sigBytes = this.properties.Signature.split('').map(c => c.charCodeAt(0));
        dataView.setUint8(0, sigBytes[0]);
        dataView.setUint8(1, sigBytes[1]);
        dataView.setUint8(2, sigBytes[2]);
        dataView.setUint8(3, sigBytes[3]);
        const implParts = this.properties.ImplementationVersion.split(' ');
        dataView.setUint8(4, parseInt(implParts[0].substring(2), 16));
        dataView.setUint8(5, parseInt(implParts[1].substring(2), 16));
        dataView.setUint16(6, this.properties.Version, true);
        dataView.setUint32(8, this.properties.NumberOfStreams, true);
        dataView.setUint32(12, this.properties.StreamDirectoryRva, true);
        dataView.setUint32(16, this.properties.CheckSum, true);
        dataView.setUint32(20, this.properties.TimeDateStamp, true);
        dataView.setBigUint64(24, this.properties.Flags, true);
        const newData = Buffer.concat([Buffer.from(headerBuffer), originalData.slice(32)]);
        fs.writeFileSync(newFilepath, newData);
        console.log(`Written to ${newFilepath}`);
    }
}

// Example usage (Node.js):
// const handler = new MdmpHandler('example.mdmp');
// await handler.readAndDecode();
// handler.printProperties();
// handler.properties.CheckSum = 0; // Modify
// handler.write();

C class for .MDMP handling:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

typedef struct {
    char signature[5]; // Null-terminated
    uint8_t implVer1;
    uint8_t implVer2;
    uint16_t version;
    uint32_t numberOfStreams;
    uint32_t streamDirectoryRva;
    uint32_t checkSum;
    uint32_t timeDateStamp;
    uint64_t flags;
} MdmpProperties;

typedef struct {
    const char* filepath;
    MdmpProperties props;
    int loaded;
} MdmpHandler;

void initMdmpHandler(MdmpHandler* handler, const char* filepath) {
    handler->filepath = filepath;
    handler->loaded = 0;
    memset(&handler->props, 0, sizeof(MdmpProperties));
}

int readAndDecode(MdmpHandler* handler) {
    FILE* f = fopen(handler->filepath, "rb");
    if (!f) return -1;

    uint8_t header[32];
    if (fread(header, 1, 32, f) != 32) {
        fclose(f);
        return -2;
    }
    fclose(f);

    memcpy(handler->props.signature, header, 4);
    handler->props.signature[4] = '\0';
    handler->props.implVer1 = header[4];
    handler->props.implVer2 = header[5];
    handler->props.version = *(uint16_t*)(header + 6);
    handler->props.numberOfStreams = *(uint32_t*)(header + 8);
    handler->props.streamDirectoryRva = *(uint32_t*)(header + 12);
    handler->props.checkSum = *(uint32_t*)(header + 16);
    handler->props.timeDateStamp = *(uint32_t*)(header + 20);
    handler->props.flags = *(uint64_t*)(header + 24);
    handler->loaded = 1;
    return 0;
}

void printProperties(MdmpHandler* handler) {
    if (!handler->loaded) {
        printf("No properties loaded.\n");
        return;
    }
    printf("Signature: %s\n", handler->props.signature);
    printf("ImplementationVersion: 0x%02x 0x%02x\n", handler->props.implVer1, handler->props.implVer2);
    printf("Version: %u\n", handler->props.version);
    printf("NumberOfStreams: %u\n", handler->props.numberOfStreams);
    printf("StreamDirectoryRva: %u\n", handler->props.streamDirectoryRva);
    printf("CheckSum: %u\n", handler->props.checkSum);
    time_t ts = (time_t)handler->props.timeDateStamp;
    printf("TimeDateStamp: %u (Date: %s)\n", handler->props.timeDateStamp, ctime(&ts));
    printf("Flags: %llu\n", handler->props.flags);
}

int write(MdmpHandler* handler, const char* newFilepath) {
    if (!handler->loaded) return -1;

    FILE* f_in = fopen(handler->filepath, "rb");
    if (!f_in) return -2;

    fseek(f_in, 0, SEEK_END);
    long size = ftell(f_in);
    fseek(f_in, 0, SEEK_SET);

    uint8_t* data = malloc(size);
    if (fread(data, 1, size, f_in) != size) {
        free(data);
        fclose(f_in);
        return -3;
    }
    fclose(f_in);

    // Update header
    memcpy(data, handler->props.signature, 4);
    data[4] = handler->props.implVer1;
    data[5] = handler->props.implVer2;
    *(uint16_t*)(data + 6) = handler->props.version;
    *(uint32_t*)(data + 8) = handler->props.numberOfStreams;
    *(uint32_t*)(data + 12) = handler->props.streamDirectoryRva;
    *(uint32_t*)(data + 16) = handler->props.checkSum;
    *(uint32_t*)(data + 20) = handler->props.timeDateStamp;
    *(uint64_t*)(data + 24) = handler->props.flags;

    char* outputPath = newFilepath ? (char*)newFilepath : strcat(strdup(handler->filepath), ".modified");
    FILE* f_out = fopen(outputPath, "wb");
    if (!f_out) {
        free(data);
        if (!newFilepath) free(outputPath);
        return -4;
    }
    fwrite(data, 1, size, f_out);
    fclose(f_out);
    free(data);
    if (!newFilepath) free(outputPath);
    printf("Written to %s\n", outputPath ? outputPath : newFilepath);
    return 0;
}

// Example usage:
// int main() {
//     MdmpHandler handler;
//     initMdmpHandler(&handler, "example.mdmp");
//     readAndDecode(&handler);
//     printProperties(&handler);
//     handler.props.checkSum = 0; // Modify
//     write(&handler, NULL);
//     return 0;
// }