A journey into PE executables - Part 1 - Introduction to PE

PE Executables

The PE stands for Portable Executable. PE is a modified version of the Common Object file format introduced in the previous post. For this reason, it is also known as PE/COFF. It's used in windows executables, drivers, dynamic libraries, etc. Like other executables, a PE file has a structure to store some features for executing in the operating system. Let's Dive into it.

Structure

The above picture shows a schematic of a PE executable structure defined in WinNT.h

DOS Header and DOS Stub

The first part of the PE structure is called DOS/MS-DOS header. It starts with a magic value (4D 5A), which consists of the ASCII characters 'M', and 'Z'. The "MZ" stands for "Mark Zbikowski". He is one of the original DOS developers. For this reason, it's also known as the MZ header.

This header also has a stub area. An example of when the code in this sub-area is executed is when a user attempts to run the file under DOS. The following message would display: "This program cannot be run in DOS mode". As I mentioned, you can find the codes in WinNT.h, and the following code is for the DOS header:
typedef struct _IMAGE_DOS_HEADER {
    WORD  e_magic;      /* 00: MZ Header signature */
    WORD  e_cblp;       /* 02: Bytes on the last page of file */
    WORD  e_cp;         /* 04: Pages in file */
    WORD  e_crlc;       /* 06: Relocations */
    WORD  e_cparhdr;    /* 08: Size of header in paragraphs */
    WORD  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
    WORD  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
    WORD  e_ss;         /* 0e: Initial (relative) SS value */
    WORD  e_sp;         /* 10: Initial SP value */
    WORD  e_csum;       /* 12: Checksum */
    WORD  e_ip;         /* 14: Initial IP value */
    WORD  e_cs;         /* 16: Initial (relative) CS value */
    WORD  e_lfarlc;     /* 18: File address of relocation table */
    WORD  e_ovno;       /* 1a: Overlay number */
    WORD  e_res[4];     /* 1c: Reserved words */
    WORD  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
    WORD  e_oeminfo;    /* 26: OEM information; e_oemid specific */
    WORD  e_res2[10];   /* 28: Reserved words */
    DWORD e_lfanew;     /* 3c: Offset to extended header */
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

NT Headers

The NT header is a data structure used in Windows executable files that specifies the file format and other important information about the executable. It is located at the beginning of the file and contains information such as the file signature, the size of various sections of the file, and the entry point for the program.
typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature;
  IMAGE_FILE_HEADER       FileHeader;
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_NT_HEADERS64 {
  DWORD                   Signature;
  IMAGE_FILE_HEADER       FileHeader;
  IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

As you can see in the above code, We can divide NT headers into three parts:
  1. PE Signature
  2. PE File Header
  3. PE Optional Header

PE Signature

The PE signature is a 4-byte (DWORD) value that is located at a fixed offset in the NT header of a Windows executable file. Its purpose is to identify the file as a valid PE file, which is the standard file format for executables, DLLs, and other binary files in Windows. The signature is represented as the ASCII characters 'P', 'E', '\0', and '\0', which are followed by two bytes indicating the machine type for which the file was compiled. This format allows Windows to determine whether the file can be executed on the current system.

PE File Header

PE file header is a data structure that contains information about the structure and content of the file, including the location and size of various sections, as well as information about how the file should be loaded and executed. The PE file header is used by the operating system to load and execute the program.

typedef struct _IMAGE_FILE_HEADER {
    WORD  Machine;
    WORD  NumberOfSections;
    DWORD TimeDateStamp;
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD  SizeOfOptionalHeader;
    WORD  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

  1. Machine Type: The machine type field specifies the target architecture for which the object file was compiled. Examples include x86, x64, ARM, MIPS, and others. This field is essential for the operating system to properly load and execute the code within the PE file.

  2. Number of Sections: The number of sections field indicates the number of sections in the PE file. Each section contains a different part of the executable code or data, and the operating system uses this information to properly load the file into memory.

  3. Time and Date Stamp: This field contains the time and date when the PE file was created. This information is useful for debugging and version control purposes.

  4. Pointer to Symbol Table: This field points to the file's symbol table, which contains information about the file's functions, variables, and other symbols. This information is used by the linker to resolve external references and by debuggers to display symbol information during debugging sessions.

  5. Number of Symbols: This field indicates the number of symbols in the file's symbol table.

  6. Size of Optional Header: The size of the optional header field specifies the size of the optional header section that follows the COFF header. The optional header contains additional information about the PE file's properties and is required for executable files but not for object files.

  7. Characteristics: The characteristics field contains a set of flags that specify various attributes of the PE file, such as whether it is a DLL (dynamic-link library) or an executable file, whether it is a system file, and whether it can be relocated.

PE Optional Header

The Optional Header is a data structure that contains additional information about the PE file. The Optional Header is located immediately after the file header in the PE file, and its size and contents vary depending on the target architecture and operating system. It contains information such as the target architecture, the entry point of the program, the size of the code and data sections, the location of the import and export tables, the base address where the program will be loaded into memory, and other important details about the executable file.

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD  Magic; /* 0x20b */
    BYTE MajorLinkerVersion;
    BYTE MinorLinkerVersion;
    DWORD SizeOfCode;
    DWORD SizeOfInitializedData;
    DWORD SizeOfUninitializedData;
    DWORD AddressOfEntryPoint;
    DWORD BaseOfCode;
    ULONGLONG ImageBase;
    DWORD SectionAlignment;
    DWORD FileAlignment;
    WORD MajorOperatingSystemVersion;
    WORD MinorOperatingSystemVersion;
    WORD MajorImageVersion;
    WORD MinorImageVersion;
    WORD MajorSubsystemVersion;
    WORD MinorSubsystemVersion;
    DWORD Win32VersionValue;
    DWORD SizeOfImage;
    DWORD SizeOfHeaders;
    DWORD CheckSum;
    WORD Subsystem;
    WORD DllCharacteristics;
    ULONGLONG SizeOfStackReserve;
    ULONGLONG SizeOfStackCommit;
    ULONGLONG SizeOfHeapReserve;
    ULONGLONG SizeOfHeapCommit;
    DWORD LoaderFlags;
    DWORD NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
Some of the most important fields in the Optional Header include:
  • Magic number: A 2-byte field that identifies the target architecture of the executable file (e.g., 0x10b for 32-bit x86, 0x20b for 64-bit x86-64, etc.).
  • Address of Entry Point: A 4-byte field that specifies the memory address of the program's entry point.
  • Image Base: A 4-byte field that indicates the preferred base address where the program will be loaded into memory.
  • Size of Image: A 4-byte field that specifies the size of the entire executable file, including all headers, sections, and resources.
  • Subsystem: A 2-byte field that specifies the type of executable (e.g., console, GUI, driver, etc.).
Overall, the Optional Header provides essential information to the operating system and the loader about how to load and execute the program, making it a critical component of the PE file format.

Resources

Comments

Popular posts from this blog

A journey into PE executables - Part 0 - Basic file format concepts

My Experiences in WorldSkills Competition 2022 Special Edition