---
description: Portable Executable
---

# PE

## Introducción

Los <mark style="color:green;">**Portable Executable (PE)**</mark> es el formato de archivo para ejecutables en Windows. Algunos ejemplos de extensiones de archivos PE son `.exe`, `.dll`, `.sys` y `.scr`. En esta página se abordará de forma introductoria la estructura PE, que es importante conocer al construir o realizar ingeniería inversa de malware.

## Estructura de los PE

La siguiente imagen muestra una estructura simplificada de un Portable Executable. Cada encabezado que se muestra en la imagen se define como una estructura de datos que contiene información sobre el archivo PE.

<figure><img src="https://3716593425-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FghTUs02Gr2jdaFqpzAHM%2Fuploads%2Fgit-blob-9075ee409bf123f000ffc3fdcdafd3a0ce977ddd%2Fimage%20(10)%20(2).png?alt=media" alt=""><figcaption></figcaption></figure>

### **DOS Header (IMAGE\_DOS\_HEADER)** <a href="#id-623bcda4-21f5-4dda-8598-06e30235e609" id="id-623bcda4-21f5-4dda-8598-06e30235e609"></a>

El primer encabezado de un archivo PE siempre está precedido por dos bytes, <mark style="color:green;">**0xAD**</mark> y <mark style="color:green;">**0x5A**</mark>, comúnmente conocidos como MZ. Estos bytes representan la firma del encabezado DOS, que se utiliza para confirmar que el archivo que se está analizando o inspeccionando es un archivo PE válido. El encabezado DOS es una estructura de datos definida de la siguiente manera:

{% code overflow="wrap" %}
```c
typedef struct _IMAGE_DOS_HEADER {    // Encabezado DOS de un archivo .EXE
    WORD e_magic;                     // Número mágico
    WORD e_cblp;                      // Bytes en la última página del archivo
    WORD e_cp;                        // Páginas en el archivo
    WORD e_crlc;                      // Relocalizaciones
    WORD e_cparhdr;                   // Tamaño del encabezado en párrafos
    WORD e_minalloc;                  // Párrafos adicionales mínimos necesarios
    WORD e_maxalloc;                  // Párrafos adicionales máximos necesarios
    WORD e_ss;                        // Valor SS inicial (relativo)
    WORD e_sp;                        // Valor SP inicial
    WORD e_csum;                      // Suma de comprobación
    WORD e_ip;                        // Valor IP inicial
    WORD e_cs;                        // Valor CS inicial (relativo)
    WORD e_lfarlc;                    // Dirección de archivo de la tabla de reubicación
    WORD e_ovno;                      // Número de overlay
    WORD e_res[4];                    // Palabras reservadas
    WORD e_oemid;                     // Identificador OEM (para e_oeminfo)
    WORD e_oeminfo;                   // Información OEM; e_oemid específico
    WORD e_res2[10];                  // Palabras reservadas
    LONG e_lfanew;                    // Desplazamiento hacia el encabezado NT
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
```
{% endcode %}

Los miembros más importantes de la estructura son `e_magic` y `e_lfanew`.

* <mark style="color:green;">**e\_magic**</mark> ocupa 2 bytes y tiene un valor fijo de <mark style="color:green;">0x5A4D</mark> o <mark style="color:green;">MZ</mark>.
* <mark style="color:green;">**e\_lfanew**</mark> es un valor de 4 bytes que contiene el desplazamiento al inicio del encabezado NT. Cabe destacar que e\_lfanew siempre se encuentra en un desplazamiento de <mark style="color:green;">0x3C</mark>.

### DOS Stub

Antes de pasar a la estructura del encabezado NT, está el DOS stub, que es un mensaje de error que imprime "Este programa no se puede ejecutar en modo DOS" en caso de que el programa se cargue en modo DOS o "Disk Operating Mode". Vale la pena señalar que el mensaje de error puede ser <mark style="color:green;">modificado por el programador en tiempo de compilación</mark>. Esto no es un encabezado PE, pero es bueno tenerlo en cuenta.

### NT Header (IMAGE\_NT\_HEADERS)

El encabezado NT es esencial ya que incorpora otros dos encabezados de imagen: `FileHeader` y `OptionalHeader`, que incluyen una gran cantidad de información sobre el archivo PE. Al igual que el encabezado DOS, el encabezado NT contiene un miembro de firma que se utiliza para verificarlo. Por lo general, el elemento de firma es igual a la cadena "PE", que está representada por los bytes <mark style="color:green;">**0x50**</mark> y <mark style="color:green;">**0x45**</mark>. Pero como la firma es de tipo DWORD, la firma se representa como 0x50450000, que sigue siendo "PE", excepto que está rellenada con dos bytes nulos. El encabezado NT se puede acceder utilizando el miembro e\_lfanew dentro del encabezado DOS.

La estructura del encabezado NT varía según la arquitectura de la máquina.

#### Estructura para 32 bits

{% code overflow="wrap" %}
```c
typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature;
  IMAGE_FILE_HEADER       FileHeader;
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
```
{% endcode %}

#### Estructura para 64 bits

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

La única diferencia entre ambas versiones de bits es el `OptionalHeader`, `IMAGE_OPTIONAL_HEADER32` e `IMAGE_OPTIONAL_HEADER64`.

### **File Header (IMAGE\_FILE\_HEADER)**

Se puede tener acceso por medio de la estructura NT con la siguiente estructura:

```c
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;
```

Los campos mas importantes de la estructura son:

* `NumberOfSections` - El número de secciones en el archivo PE (se discutirá más adelante).
* `Characteristics` - Banderas que especifican ciertos atributos sobre el archivo ejecutable, como si es una biblioteca de vínculos dinámicos (DLL) o una aplicación de consola.
* `SizeOfOptionalHeader` - El tamaño del siguiente encabezado opcional.

{% embed url="https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header" %}

### Opcional Header (IMAGE\_OPTIONAL\_HEADER)

El encabezado opcional es importante y aunque se le llama "opcional", es esencial para la ejecución del archivo PE. Se le llama opcional porque algunos tipos de archivo no lo tienen.

El encabezado opcional tiene dos versiones, una para sistemas de 32 bits y otra para sistemas de 64 bits. Ambas versiones tienen miembros casi idénticos en su estructura de datos, con la diferencia principal siendo el tamaño de algunos miembros. Se utiliza ULONGLONG en la versión de 64 bits y DWORD en la versión de 32 bits. Además, la versión de 32 bits tiene algunos miembros que no se encuentran en la versión de 64 bits.

#### Estructura 32 bits

{% code overflow="wrap" %}
```c
typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode;
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint;
  DWORD                BaseOfCode;
  DWORD                BaseOfData;
  DWORD                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;
  DWORD                SizeOfStackReserve;
  DWORD                SizeOfStackCommit;
  DWORD                SizeOfHeapReserve;
  DWORD                SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
```
{% endcode %}

#### Estructura 64 bits

{% code overflow="wrap" %}
```c
typedef struct _IMAGE_OPTIONAL_HEADER64 {
  WORD                 Magic;
  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;
```
{% endcode %}

El encabezado opcional contiene una gran cantidad de información que se puede utilizar. A continuación se muestran algunos de los miembros de la estructura que se utilizan comúnmente:

* <mark style="color:green;">**Magic**</mark>: Describe el estado del archivo de imagen (imagen de 32 o 64 bits).
* <mark style="color:green;">**MajorOperatingSystemVersion**</mark>: El número de versión principal del sistema operativo requerido (por ejemplo, 11, 10).
* <mark style="color:green;">**MinorOperatingSystemVersion**</mark>: El número de versión secundario del sistema operativo requerido (por ejemplo, 1511, 1507, 1607).
* <mark style="color:green;">**SizeOfCode**</mark>: El tamaño de la sección .text (se discutirá más adelante). AddressOfEntryPoint: Desplazamiento al punto de entrada del archivo (normalmente la función principal).
* <mark style="color:green;">**BaseOfCode**</mark>: Desplazamiento al inicio de la sección .text.
* <mark style="color:green;">**SizeOfImage**</mark>: El tamaño del archivo de imagen en bytes.
* <mark style="color:green;">**ImageBase**</mark>: Especifica la dirección preferida en la que se cargará la aplicación en memoria cuando se ejecute. Sin embargo, debido a los mecanismos de protección de memoria de Windows como la Distribución Aleatoria del Espacio de Direcciones (ASLR), es raro ver una imagen mapeada en su dirección preferida, ya que el Cargador PE de Windows asigna el archivo a una dirección diferente. Esta asignación aleatoria realizada por el cargador PE de Windows puede causar problemas en la implementación de técnicas futuras porque algunas direcciones que se consideran constantes fueron modificadas. El cargador PE de Windows luego pasa por la reubicación de PE para corregir estas direcciones.
* <mark style="color:green;">**DataDirectory**</mark>: Uno de los miembros más importantes en el encabezado opcional. Es un arreglo de IMAGE\_DATA\_DIRECTORY, que contiene los directorios en un archivo PE (se discutirán a continuación).

{% embed url="https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_data_directory" %}

### Data Directory

El Data Directory o Directorio de Datos se puede acceder desde el último miembro del encabezado opcional. Es un arreglo del tipo de dato IMAGE\_DATA\_DIRECTORY que tiene la siguiente estructura de datos:

{% code overflow="wrap" %}
```c
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
```
{% endcode %}

El arreglo de Directorios de Datos tiene un tamaño de `IMAGE_NUMBEROF_DIRECTORY_ENTRIES`, que es un valor constante de <mark style="color:green;">**16**</mark>. Cada elemento en el arreglo representa un directorio de datos específico que incluye información sobre una sección PE o una Tabla de Datos (el lugar donde se guarda información específica sobre el PE).

Se puede acceder a un directorio de datos específico utilizando su índice en el arreglo.

{% code overflow="wrap" %}
```c
#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
```
{% endcode %}

#### Directorio de Exportación

El directorio de exportación de un PE es una estructura de datos que contiene información sobre las funciones y variables que se exportan desde el ejecutable. Contiene las direcciones de las funciones y variables exportadas, que pueden ser utilizadas por otros archivos ejecutables para acceder a las funciones y datos. El directorio de exportación generalmente se encuentra en las DLL que exportan funciones (por ejemplo, `kernel32.dll` exportando `CreateFileA`).

#### Tabla Importación de Direcciones

La tabla de importación de direcciones es una estructura de datos en un PE que contiene información sobre las direcciones de las funciones importadas desde otros archivos ejecutables. Las direcciones se utilizan para acceder a las funciones y datos en los otros ejecutables (por ejemplo, Application.exe importando CreateFileA desde kernel32.dll).

## Secciones PE

Las secciones PE contienen el código y los datos utilizados para crear un programa ejecutable. Cada sección PE tiene un nombre único y generalmente contiene código ejecutable, datos o información de recursos. No hay un número constante de secciones PE, ya que diferentes compiladores pueden agregar, eliminar o fusionar secciones según la configuración. Algunas secciones también se pueden agregar posteriormente de forma manual, por lo que es dinámico y el campo IMAGE\_FILE\_HEADER.NumberOfSections ayuda a determinar ese número.

Las siguientes secciones o segmentos PE son las más importantes y existen en casi todos los PE:

* <mark style="color:green;">**.text**</mark>: Contiene el código ejecutable escrito.
* <mark style="color:green;">**.data**</mark>: Contiene datos inicializados, es decir, variables inicializadas en el código.
* <mark style="color:green;">**.rdata**</mark>: Contiene datos de solo lectura. Estas son variables constantes precedidas por "const".
* <mark style="color:green;">**.idata**</mark>: Contiene las tablas de importación. Estas son tablas de información relacionadas con las funciones llamadas desde el código. Esto es utilizado por el Cargador PE de Windows para determinar qué archivos DLL cargar en el proceso, junto con qué funciones se utilizan en cada DLL.
* <mark style="color:green;">**.reloc**</mark>: Contiene información sobre cómo corregir las direcciones de memoria para que el programa pueda cargarse en la memoria sin errores.
* <mark style="color:green;">**.rsrc**</mark>: Se utiliza para almacenar recursos como iconos y mapas de bits.

Cada sección PE tiene una estructura de datos [<mark style="color:green;">**IMAGE\_SECTION\_HEADER**</mark>](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header) que contiene información valiosa sobre ella. Estas estructuras se guardan bajo los encabezados NT en un archivo PE y se apilan una encima de la otra, donde cada estructura representa una sección.

Recordemos que la estructura IMAGE\_SECTION\_HEADER es la siguiente:

```c
typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
```

Al analizar los elementos, cada uno de ellos es altamente valioso e importante:

* `Name`: El nombre de la sección (por ejemplo, .text, .data, .rdata).
* `PhysicalAddress` o `VirtualSize`: El tamaño de la sección cuando está en memoria.
* `VirtualAddress`: Desplazamiento del inicio de la sección en la memoria.

## Referencias

{% embed url="https://0xrick.github.io/win-internals/pe2/" %}

* PE Overview - [https://0xrick.github.io/win-internals/pe2/](https://0xrick.github.io/win-internals/pe2/)
* DOS Header, DOS Stub and Rich Header - [https://0xrick.github.io/win-internals/pe3/](https://0xrick.github.io/win-internals/pe3/)
* NT Headers - [https://0xrick.github.io/win-internals/pe4/](https://0xrick.github.io/win-internals/pe4/)
* Data Directories, Section Headers and Sections - [https://0xrick.github.io/win-internals/pe5/](https://0xrick.github.io/win-internals/pe5/)
* PE Imports (Import Directory Table, ILT, IAT) - [https://0xrick.github.io/win-internals/pe6/](https://0xrick.github.io/win-internals/pe6/)
