Introduction

The previous module discussed storing payloads in the .data and .rdata sections which are intended for program data.

The .text section, however, contains the executable machine code produced by the compiler.

Embedding a byte array in .text requires explicit compiler directives because ordinary data declarations are not placed there automatically. The array then becomes part of a section that is normally readable and executable but not writable.

Object Files

This module will use the term object file when explaining how data is placed into the .text section.

Before producing the final executable, MSVC compiles each source file into an intermediate object file with the .obj extension. These files contain compiled code, data, section contributions, symbols, and other information required by the linker.

The compiler directives demonstrated in this module initially affect sections within the generated .obj file. The linker later combines these section contributions and places them into the appropriate sections of the final PE file.

For now, it is only necessary to understand that .obj files are created between the compilation and linking stages. Their internal structure and other COFF-related details will be covered in later modules.

Creating a .text Subsection

Placing a data object in .text requires more than declaring it as a normal global variable. By default, MSVC selects a suitable section based on the variable's declaration. Therefore, the compiler must be explicitly instructed to create an object-file section and place the payload inside it.

The pragma directive provides compiler-specific instructions while a source file is being compiled. In this example, the section pragma declares an object-file section named .text$payload:

#pragma section(".text$payload")

The section pragma accepts one or more optional attributes that control how the section is treated. These attributes can specify whether the section is readable, writable, executable, shared, discardable, or subject to other memory-related behavior.

When no attributes are specified, MSVC assigns the section the read and write attributes by default. In case the payload is to be read and executed without being modified, .text$payload should be explicitly declared with the read and execute attributes:

#pragma section(".text$payload", read, execute)

The read attribute allows the section's contents to be read, while the execute attribute allows instructions stored in the section to be executed. The write attribute is omitted because the payload bytes are not modified after the image is loaded.

Once declared, the section remains available for the remainder of the current source file's compilation. However, #pragma section only defines the section and its attributes. It does not automatically place any code or data inside it.

MSVC's __declspec keyword applies a Microsoft-specific attribute to a declaration. The allocate attribute specifies the object-file section in which a data object should be emitted:

__declspec(allocate(".text$payload"))
const unsigned char Text_RawData[] = {
    // Payload bytes
};