> For the complete documentation index, see [llms.txt](https://ymmfty0.gitbook.io/ymmfty0/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ymmfty0.gitbook.io/ymmfty0/pe-file-format/headers/sections-and-section-header.md).

# Sections and Section Header

## **Sections**

Секции представляют собой логические блоки, которые содержат различные виды информации, необходимые для выполнения программы. Сами секции хранятся после заголовков секций (Section Headers).

Секции имеют специальные названия и предназначены для выполнения определенных функций:

* **`.text`** — исполняемый код программы.
* **`.data`** — инициализированные данные.
* **`.pdata`** — информация об исключениях.
* **`.rdata`** — инициализированные данные, доступные только для чтения.
* **`.reloc`** — данные для переадресации (Relocation).
* **`.rsrc`** — каталог ресурсов, таких как иконки, изображения и строки.

Вы можете узнать больше о секциях, которые вас интересуют, по следующей ссылке: [специальные секции](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#special-sections).

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2FpTdCmiKIItsZ3liSlmKv%2FPasted%20image%2020240928002521.png?alt=media&amp;token=c1102a1d-8fa7-40ba-9fcd-551c360f0251" alt=""><figcaption></figcaption></figure>

## **Section Headers**

Для того чтобы работать с секциями, существует структура `SectionHeader`. Она располагается сразу после `Optional Header`.

`Section Header` — это структура с именем `IMAGE_SECTION_HEADER`, которая имеет следующий вид:

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

* `Name[IMAGE_SIZEOF_SHORT_NAME]` - поля `Name` отведено 8 байт для хранения имени секции. Эти 8 байт представляют собой UTF-8 строку с нулевым заполнением. Макрос `IMAGE_SIZEOF_SHORT_NAME` определяет длину этого поля и равен 8, максимальная длина имени секции без учета завершающего нулевого символа составляет 8 символов. Если имя секции короче 8 символов, оставшиеся байты заполняются нулями. Если же имя секции ровно 8 символов, завершающий нулевой символ отсутствует . Для имен секций, длина которых превышает 8 символов, в поле `Name` указывается косая черта `/` в первом байте. Это представляет собой смещение в таблице строк (String Table), хранящей полные имена секций. Однако исполняемые образы не используют таблицу строк, поэтому ограничение в 8 символов сохраняется и для исполняемых образов.
* `Misc.PhysicalAddress` - Адрес файла.
* `Misc.VirtualSize` - Общий размер секции при загрузке в память. Если это значение больше SizeOfRawData, секция будет заполнена нулями. Это поле действительно только для исполняемых образов и должно быть установлено в ноль для COFF файлов.
* `VirtualAddress` - Для исполняемых образов - адрес первого байта секции относительно базы образа при загрузке секции в память. Для объектных файлов это поле - адрес первого байта до применения перемещения;
* `SizeOfRawData` - Размер данных секции на диске или размер секции (для объектных файлов) . Для исполняемых файла этот размер должен быть кратен FileAlignment из OptionalHeader . Я показывал пример в статье по NT Headers . Если это значение меньше VirtualSize, оставшаяся часть секции заполняется нулями.
* `PointerToRawData` - Файловый указатель на первую страницу секции . Для исполняемых образов это значение должно быть кратно FileAlignment из Optional Header.
* `PointerToRelocations` - Файловый указатель на начало записей о перемещении для раздела. Устанавливается в ноль для исполняемых образов или при отсутствии перемещений.
* `PointerToLinenumbers` - Файловый указатель на начало записей с номерами строк для данного раздела . Это значение равно нулю, если в COFF нет номеров строк. Это значение должно быть нулевым для исполняемых файлов, поскольку отладочная информация COFF устарела.
* `NumberOfRelocations` - Количество записей перемещения для раздела. Для исполняемых образов это значение равно нулю.
* `NumberOfLinenumbers` - Количество записей с номерами строк для секции. Для образа это значение должно быть нулевым, поскольку отладочная информация COFF устарела.
* `Characteristics` - Флаги, описывающие характеристики секции, например, содержит ли секция исполняемый код, содержит ли инициализированные/неинициализированные данные, может ли быть разделена в памяти. Полный список флагов характеристик секции можно найти в официальной документации [Microsoft](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-flags).

Как нам работать с SectionHeader? &#x20;

SectionHeader идет после NT Headers , поэтому нам просто надо сместиться на NT Header и прибавить ее размер , как показано в коде ниже.

```cpp
auto sourceImageSection = 
    reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pBaseAddr + eLfaNew + sizeof(IMAGE_NT_HEADERS)));
```

Мы получаем первый элемент из SectionHeaders.

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2FdllrMKk0NzJ1Ul3AQZdd%2FPasted%20image%2020240928110111.png?alt=media&amp;token=7206bb07-0d18-4e4a-a633-a57b4a81e51e" alt=""><figcaption></figcaption></figure>

Как мы уже знаем, количество секций хранится в `NT Header`, в заголовке `FileHeader`.

`FileHeader` имеет поле `NumberOfSections`, которое содержит количество секций. Это позволяет нам вывести их, используя обычный цикл.

Как показано ниже, в данном примере мы просто выводим имена секций:

```cpp
auto sourceImageSection = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pBaseAddr + eLfaNew + sizeof(IMAGE_NT_HEADERS)));
for (size_t i = 0; i < ntHeader.FileHeader.NumberOfSections; ++i)
{
	std::cout << sourceImageSection[i].Name << std::endl;
}
```

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2FTHQLfpviOSIXTEaP2AfG%2FPasted%20image%2020240928110502.png?alt=media&amp;token=6becb4da-5e73-4c94-884f-0e6fd35c3d28" alt=""><figcaption></figcaption></figure>

Давайте для практики проверим кратность секций на SectionAlignment из OptionalHeader. Напомню *SectionAlignment*\* - это поле хранит в себе значение( в байтах ), кратно которому должна быть равна каждая секция при загрузке в память

```cpp
auto sourceImageSection = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pBaseAddr + eLfaNew + sizeof(IMAGE_NT_HEADERS)));
for (size_t i = 0; i < ntHeader.FileHeader.NumberOfSections; ++i)
{
	std::cout << "Section name: " << sourceImageSection[i].Name << std::endl;
	std::cout << "SectionAlignment : " << sourceImageSection[i].VirtualAddress % ntHeader.OptionalHeader.SectionAlignment << std::endl;
}
```

Как видим, результат вывел 0 , значит все нормально и секция действительно кратна SectionAlignment

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2FdGR01AZVHRf8EI20P2jU%2FPasted%20image%2020240928120226.png?alt=media&amp;token=03e089b9-9138-40c4-8a43-093f5df1a1db" alt=""><figcaption></figcaption></figure>

Так же проверим FileAlignment , просто для примера Напомню ***FileAlignment** - это поле, которое хранит значение в байтах и используется для выравнивания данных секций в **исходном файле на диске**.*

```cpp
auto sourceImageSection = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pBaseAddr + eLfaNew + sizeof(IMAGE_NT_HEADERS)));
for (size_t i = 0; i < ntHeader.FileHeader.NumberOfSections; ++i)
{
	std::cout << "Section name: " << sourceImageSection[i].Name << std::endl;
	std::cout << "FileAlignment : " << sourceImageSection[i].SizeOfRawData % ntHeader.OptionalHeader.FileAlignment << std::endl;

}
```

Результат такой же , значит все работает правильно.

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2Fcx9QIU60QAzAlFikMEJ1%2FPasted%20image%2020240928120429.png?alt=media&amp;token=2fa00345-f2a3-4ca1-a495-ca0814889f53" alt=""><figcaption></figcaption></figure>

Так же если мы сложим **`PointerToRawData`** и **`SizeOfRawData`** , то мы получим конец секции в файле.

```c++
std::cout << "End of section: " << sourceImageSection[i].SizeOfRawData + sourceImageSection[i].PointerToRawData << std::endl;
```

Как видим начала .rdata равна E200

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2F2c6TbR7ZRFRs1iDEMyHp%2FPasted%20image%2020240928122446.png?alt=media&amp;token=9f3f8ddc-aaa2-401e-b19f-0806e7fb57ab" alt=""><figcaption></figcaption></figure>

И вот результат кода , как видим, все работает

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2F0mn9l6kFUV566n7XfC35%2FPasted%20image%2020240928122514.png?alt=media&amp;token=afda684a-dc14-4846-9276-10687788bce4" alt=""><figcaption></figcaption></figure>

Аналогичным образом мы можем поступить с виртуальным размером и адресом.

```cpp
std::cout << "End of section in virtual address space: " 
	<< sourceImageSection[i].VirtualAddress + sourceImageSection[i].Misc.VirtualSize
	<< std::endl;
```

Но если проверить с PE-Bear происходит не состыковка.

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2Fm3w1I4JT1GmhVLgNMaXK%2FPasted%20image%2020240928123523.png?alt=media&amp;token=918c6ac1-6480-4f2f-8b4d-86ed0f08bc62" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2F1IPEA8XNHTJz6kdS5CGz%2FPasted%20image%2020240928123446.png?alt=media&amp;token=9131266f-12e3-413f-9913-f9ec849270d7" alt=""><figcaption></figcaption></figure>

Проблема в том, что адрес перестаёт быть кратным `SectionAlignment`. Поэтому наш адрес нужно округлить, чтобы он стал кратным `SectionAlignment`. Такие манипуляции, как я понял, необходимы только для того, чтобы адреса были читабельными и с ними было проще работать.

```cpp
auto sourceImageSection = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pBaseAddr + eLfaNew + sizeof(IMAGE_NT_HEADERS)));
for (size_t i = 0; i < ntHeader.FileHeader.NumberOfSections; ++i)
{
	std::cout << "Section name: " << sourceImageSection[i].Name << std::endl;

	DWORD virtualEnd = sourceImageSection[i].Misc.VirtualSize + sourceImageSection[i].VirtualAddress;
	if (virtualEnd % ntHeader.OptionalHeader.SectionAlignment == 0)
	{
		std::cout << "Virtual end " << virtualEnd << std::endl;
	}
	else
	{
		DWORD aligedAddr = ((virtualEnd + ntHeader.OptionalHeader.SectionAlignment - 1) / ntHeader.OptionalHeader.SectionAlignment) * ntHeader.OptionalHeader.SectionAlignment;
		std::cout << "Aligned end of section: " << aligedAddr << std::endl;
	}

}
```

Я такое округление нашел на [StackOverflow](https://stackoverflow.com/a/3407392)

<figure><img src="https://2856195150-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGu8YwYKUroiRnA4eB3ld%2Fuploads%2F2cuPfYvx6VVmA1mSQHN7%2FPasted%20image%2020240928133502.png?alt=media&amp;token=6fcb7306-ff05-4966-ba25-2bb1867a52fa" alt=""><figcaption></figcaption></figure>

После этого округления, адреса становятся корректными.

## **Итог**

Мы разобрались с полями Section Header . И не много попрактиковались, как работать с ними и как работают некоторые поля, чтобы глубже понимать устройство PE файла.

Для практического понимания работы с заголовком PE-файла, вы можете ознакомиться с кодом в моем репозитории Github: <https://github.com/ymmfty0/PEFormatTutorial/tree/master/SecionHeader>
