PE File DOS Header
The MS-DOS Header is a 64-byte structure at the beginning of a PE file. Along with the DOS stub, the DOS header is responsible for MS-DOS backward compatibility.
The DOS header is what makes the PE file DOS executable.
The Important Parts of the DOS Header
The most important parts of the DOS header are the first and last members: e_magic and e_lfanew.
e_magic
e_magic is the ‘magic number’, and it is the first member of the DOS Header structure. It is a WORD data type, so it is 16 bits (2 bytes) long, and it always has a value of 0x5A4D
or MZ
. It designates the file as DOS executable.
e_lfanew
e_lfanew holds an offset to the beginning of the NT Headers. It is always located at offset 0x3C., and it’s a LONG data type, so it has a size of 32 bits (4 bytes).
The DOS Header Structure
The DOS Header is a 64-byte struct:
typedef struct _IMAGE_DOS_HEADER { // MS-DOS EXE header
WORD e_magic; // Magic number: 0x5A4D or MZ
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header, in paragraphs
WORD e_minalloc; // Min - extra paragraphs needed
WORD e_maxalloc; // Max - extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier
WORD e_oeminfo; // OEM information
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // Offset to NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
Here’s a brief overview of the key components in the DOS Header struct:
- e_magic: The “magic number” 0x5A4D, which is the hexadecimal representation of the ASCII characters “MZ.” This is a signature that identifies the file as a DOS-compatible executable.
- e_cblp: The count of bytes in the last page of the file that is not used. Typically set to 0.
- e_cp: The count of 512-byte pages in the file. This field is also usually set to 0.
- e_crlc: Number of relocation entries in the file. Usually set to 0.
- e_cparhdr: The size of the header in 16-byte paragraphs. For a standard PE file, this field is often set to 4.
- e_minalloc: Minimum number of paragraphs allocated to the program. Typically set to 0x10.
- e_maxalloc: Maximum number of paragraphs allocated to the program. Typically set to 0xFFFF.
- e_ss: Initial stack segment value. This field is typically set to 0.
- e_sp: Initial stack pointer value. It is typically set to 0xB8.
- e_csum: Checksum. It is often set to 0.
- e_ip: Initial instruction pointer (IP) value. Typically set to 0.
- e_cs: Initial code segment value. It is typically set to 0.
- e_lfarlc: The file address of the relocation table. It is usually set to 0x40.
- e_ovno: Overlay number. It is often set to 0.
- e_res: An array reserved for future use.
- e_oemid: OEM identifier. It is typically set to 0.
- e_oeminfo: OEM information. It is often set to 0.
- e_res2: A 10-byte array reserved for future use.
- e_lfanew: A 4-byte field that specifies the file offset to the PE header – the position in the file where the actual PE header begins.
The MS-DOS header is a historical artifact that has its origins in the days of MS-DOS and is retained for compatibility and alignment purposes in modern Windows PE files. However, its content is largely ignored by modern Windows operating systems when executing PE files.