> 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/relocation-table.md).

# Relocation Table

## Введение

В данный статье я хотел бы пробежать о том , как устроен механизм релокации в исполняемых файлах, что это такое и описать его. Разобраться структуру, которые используются в этом механизме и какие данные требуются загрузчику Windows для работы с механизмом релокации.

## Relocation

Когда программа компилируется, компилятор предполагает, что исполняемый файл будет загружен по определённому базовому адресу. Когда исполнимый файл (или библиотека) загружается в память, он должен быть размещен по определенному адресу. Программа может использовать этот базовый адрес для вычисления других адресов и хранить их напрямую в исполняемом файле Однако по ряду причин маловероятно, что исполняемый файл будет загружен именно по желаемому базовому адресу, и все захардкоженные адреса становятся недействительными.

## Relocation Table

Когда Windows загружает исполняемый файл, она проверяет фактический базовый адрес изображения в памяти, в который она загружена, затем сравнивается с тем, что указан в заголовке исполняемого файла в поле OptionalHeader.ImageBase. Этот адрес — предпочтительный для загрузки изображения. Стоит также отметить, что в OptionalHeader есть флаг, указывающий, что файл может быть загружен по любому адресу. Этот флаг можно найти в поле DLLCharacteristics заголовка.

<figure><img src="/files/nVGRVsM5QPGJDxOxLFlq" alt=""><figcaption></figcaption></figure>

Если базовые адреса совпадают, загрузка продолжается без дополнительных операций.

В случае различия базовых адресов, загрузчик Windows обращается к специальной таблице Relocation Table, которая хранится в PE-файле. Эта таблица содержит адреса, которые требуют исправления, если загруженный образ был размещен по другому базовому адресу.

Relocation Table можно получить через Data Directory:

```cpp
auto pe_data_directory = static_cast<IMAGE_DATA_DIRECTORY>(pe_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
```

Этот каталог данных расположен в секции **.reloc** и разделен на блоки.

Каждый блок содержит информацию о релокациях для одной 4k страницы. То есть, в одном блоке , описаны все релокации, которые находятся в пределах размера одно страницы памяти.

Каждый блок таблица релокации, должен начинать с 32-битной границы. Каждый блок выравнивается по адресу, который кратен 4 байтам.

Каждый блок начинается с структуры **IMAGE\_BASE\_RELOCATION**.

```cpp
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
```

* VirtualAddress - RVA на место в странице, где нужно производить релокацию.
* SizeOfBlock - это размер всего блока, вместе с размеров структуры.

После IMAGE\_BASE\_RELOCATION идут двухбайтовые значение. Количество значение , которое идет после структуры равно размеру SizeOfBlock.

Эти двух байтовые значения можно представить в таком виде

```cpp
typedef struct BASE_RELOCATION_ENTRY
{
	WORD Offset : 12;
	WORD Type : 4;
} BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;
```

* Type - старшие 4 бита , это тип релокаций , который определяет , каким образом нужно произвести релокацию
* Offset - это смещение внутри страницы , то есть, мы получили RVA из IMAGE\_BASE\_RELOCAION , прибавляем offset и получаем точное значение в странице, где нужно произвести исправления.

Вот список всех типов релокаций .

```cpp
#define IMAGE_REL_BASED_ABSOLUTE              0
#define IMAGE_REL_BASED_HIGH                  1
#define IMAGE_REL_BASED_LOW                   2
#define IMAGE_REL_BASED_HIGHLOW               3
#define IMAGE_REL_BASED_HIGHADJ               4
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5    5
#define IMAGE_REL_BASED_RESERVED              6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7    7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8    8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9    9
#define IMAGE_REL_BASED_DIR64                 10
```

Описание каждого типа ты можешь получить тут. [PE Format - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#base-relocation-types)

Я написал небольшую функцию, которая обрабатывает все это

```cpp
void AnalyzeRelocation(const PBYTE peFile, DWORD offsetToRelocTable, const DWORD relocTableSize)
{
	DWORD offset = 0; 

	for (int i = 0; offset < relocTableSize; ++i)
	{
		auto relocation_table = reinterpret_cast<PIMAGE_BASE_RELOCATION>(peFile + offsetToRelocTable + offset);

		size_t entries_count = (relocation_table->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(BASE_RELOCATION_ENTRY);

		std::cout << "#" << i << " Relocation Block; " << std::endl;
		std::cout << " " << "Virtual address : " << std::hex << relocation_table->VirtualAddress << std::endl;
		std::cout << " " << "SizeOfBlock: " << std::hex << relocation_table->SizeOfBlock << std::endl;
		std::cout << " " << "Entries Count: " << entries_count << std::endl;

		auto reloc_entry = reinterpret_cast<PBASE_RELOCATION_ENTRY>(reinterpret_cast<PBYTE>(relocation_table) + sizeof(IMAGE_BASE_RELOCATION));

		for (size_t j = 0; j < entries_count; ++j)
		{

			std::cout << "  " << "#" << j << " Relocation Entry; " << std::endl;
			std::cout << "  " << " Type: " << std::dec << reloc_entry[j].Type << std::endl;
			std::cout << "  " << " Offset: " << std::hex << reloc_entry[j].Offset << std::endl;

		}

		offset += relocation_table->SizeOfBlock;
	}
}
```

Код в целом не сложный. Первое , что мы делаем это объявляем переменную offset .

```cpp
DWORD offset = 0; 
```

Это переменную служит на будущее, чтобы мы могли перейти к следующем итерации и использовали другую структуру IMAGE\_BASE\_RELOCATION.

Далее я использовал цикл for, вместо while.

```cpp
for (int i = 0; offset < relocTableSize; ++i)
```

Я это сделал, чтобы можно было выводить номер блока . Просто, чтобы было видно и красиво.

Внутри тела цикла мы делаем некоторые вещи.

Получаем IMAGE\_BASE\_RELOCATION структуру, она идет в самом начале .

```cpp
auto relocation_table = reinterpret_cast<PIMAGE_BASE_RELOCATION>(peFile + offsetToRelocTable + offset);
```

После получение структуры, мы должны почитать количество entries этого блока.

```cpp
size_t entries_count = (relocation_table->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(BASE_RELOCATION_ENTRY);
```

Упустим момент, с выводом данных ,потому-что особо там интересного ничего нет Далее , мы получаем все entries этого блока

```cpp
auto reloc_entry = 
	reinterpret_cast<PBASE_RELOCATION_ENTRY>(
	reinterpret_cast<PBYTE>(relocation_table) + sizeof(IMAGE_BASE_RELOCATION)
);

```

Ну и собственного , мы просто выводим данные этих entries

```cpp
for (size_t j = 0; j < entries_count; ++j)
{

	std::cout << "  " << "#" << j << " Relocation Entry; " << std::endl;
	std::cout << "  " << " Type: " << std::dec << reloc_entry[j].Type << std::endl;
	std::cout << "  " << " Offset: " << std::hex << reloc_entry[j].Offset << std::endl;
}
```

Считаем смещение

```cpp
offset += relocation_table->SizeOfBlock;
```

И начинаем заново с полученным offset , до тех пор, пока не пройдем все данные.

## Итог

В этом посте мы разобрали, что такое Relocation Table , для чего она нужна , как она работает и поработали с кодом . В целом я думаю этого достаточно для понимания о том , как работает таблица релокации.

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