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

# Resources

Я хотел бы поговорить о таком разделе в PE файлах , как Resources.

Все данные хранятся в секции .rsrc.

Запись IMAGE\_DIRECTORY\_ENTRY\_RESOURCE можно получить при помощи DataDirectory. Про нее вы можете прочитать так же у меня в блоге.

```cpp
IMAGE_DATA_DIRECTORY image_resource_directory = ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
auto resource_table = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pBaseAddr + image_resource_directory.VirtualAddress);
```

По разным причинам ресурсы организованы аналогично файловой системе — с каталогами и конечными узлами.

## MAGE\_RESOURCE\_DIRECTORY

Вот так выглядит структура:

```cpp
typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    WORD    NumberOfNamedEntries;
    WORD    NumberOfIdEntries;
//  IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

#define IMAGE_RESOURCE_NAME_IS_STRING        0x80000000
#define IMAGE_RESOURCE_DATA_IS_DIRECTORY     0x80000000
```

* Characteristics - Теоретически это поле может содержать флаги ресурса, но, похоже, всегда равно 0.
* TimeDateStamp - Отметка времени/даты, описывающая время создания ресурса.
* MajorVersion, MinorVersion - Теоретически эти поля будут содержать номер версии ресурса. Эти поля всегда имеют значение 0.
* NumberOfNamedEntries - Количество элементов массива, которые используют имена в качестве идентификатора в массиве DirectoryEntries
* NumberOfIdEntries - Количество элементов массива, использующих целочисленные идентификаторы в массиве DirectoryEntries
* DirectoryEntries - это не входит в саму структуру. Но в IMAGE\_RESOURCE\_DIRECTORY структуре он указывается как закоментированым , это означает просто то , что этот массив идет после этой структуры. Массив содержит элементы IMAGE\_RESOURCE\_DIRECTORY\_ENTRY.

## IMAGE\_RESOURCE\_DIRECTORY\_ENTRY

Как я говорил выше, структуры IMAGE\_RESOURCE\_DIRECTORY идет массив DirectoryEntries , который состоит из элементов IMAGE\_RESOURCE\_DIRECTORY\_ENTRY Элементы этого массива, у которых идентификатор используется именным располагаются первыми в массиве.

Чтобы понять, это строка или id , для этого проверяется старший бит в первом DWORD значение NameIsString. В этот DWORD устанавливается старший бит, если 1 , то строка , иначе это ID. Далее, я расскажу вам про PIMAGE\_RESOURCE\_DIR\_STRING\_U, так что не переживайте, если вдруг не понятно , зачем она нужна.

```cpp
if (directory_entries[i].NameIsString)
{
	auto dir_string = reinterpret_cast<PIMAGE_RESOURCE_DIR_STRING_U>(reinterpret_cast<PBYTE>(resource_table) + directory_entries[i].NameOffset);
	std::wstring name(dir_string->NameString, dir_string->Length);
	std::wcout << L"Name: " << name << std::endl;
}
else
	std::cout << std::hex << directory_entries[i].Id << std::endl;
```

Количество элементов в массиве — это сумма полей NumberOfNamedEntries и NumberOfIdEntries.

```cpp
size_t directory_entries_size = resource_table->NumberOfIdEntries + resource_table->NumberOfNamedEntries;
```

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

Directory Entrie может либо указывать на подкаталог (то есть на другой IMAGE\_RESOURCE\_DIRECTORY), либо на необработанные данные для ресурса (например, иконки, строки, курсоры и т. д. ).

Как вообще понять, указывает это на подкаталог или на что-то другое ? Для этого существует DWORD DataIsDirectory в структуре, для него устанавливается старший бит , а оставшиеся 31 бит представляет собой смещение относительно структуры IMAGE\_RESOURCE\_DIRECTORY ( прошу подметить это не RVA)

Проверь это просто:

```cpp
if (!directory_entries[i].DataIsDirectory)
```

Когда запись указывает на реальный экземпляр ресурса, в DataIsDirectory ничего не будет. Оставшиеся 31 бит это смещение на сам ресурс ( например: иконка ).

Теперь я более подробно хочу рассказать про каждый элемент структуры Вот так выглядит эта структура:

```cpp
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct {
            DWORD NameOffset:31;
            DWORD NameIsString:1;
        } DUMMYSTRUCTNAME;
        DWORD   Name;
        WORD    Id;
    } DUMMYUNIONNAME;
    union {
        DWORD   OffsetToData;
        struct {
            DWORD   OffsetToDirectory:31;
            DWORD   DataIsDirectory:1;
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
```

* NameIsString - битовое поле, в него устанавливается старший бит, является это именем или id
* NameOffset - младшие 31 , которые хранят в себе смещение ( это не rva , а смещение от относительно .rsrc ) на структуру IMAGE\_RESOURCE\_DIR\_STRING\_U
* Name - Хранит ID , если это ID и offset на структуру IMAGE\_RESOURCE\_DIR\_STRING\_U, если это имя строки
* Id - Хранит в себе просто ID и усе
* OffsetToData - хранит смещение до каталога. если это конечно же дата на ресурсы ( смещение относительно .rsrc)
* DataIsDirectory - битовое поле , которое указывает это подкаталог или дата ресурсов
* OffsetToDirectory - смещение на подкаталог ( смещение относительное .rsrc)

Начнем с того, что вообще директория ресурсов строится как древо.

Есть главный каталог, корень , с которого начинается вся директория ресурсов. Она находится начале секции .rsrc . В нём могут быть различные каталоги, каждый из которых представляет собой отдельную категорию ресурсов. Например, если PE-файл включает диалоги, таблицы строк и меню, будет три каталога: каталог диалогов, каталог таблиц строк и каталог меню.

В каждом каталоге, может находится подкаталог. Каждый из этих подкаталогов может содержать другие подкаталоги или ресурсы с уникальными ID. Например, в каталоге диалогов могут быть подкаталоги для каждого диалогового окна, например, с ID `1`, `2`, `3`, и т. д.

Например, это можно представить в таком виде

```
- Главный каталог ресурсов
  - Каталог диалогов (Dialog)
    - Подкаталог ID 1 (например, диалоговое окно с ID 1)
    - Подкаталог ID 2 (диалоговое окно с ID 2)
    - Подкаталог ID 3 (диалоговое окно с ID 3)
  - Каталог таблиц строк (String Table)
    - Подкаталог ID 1 (например, таблица строк с ID 1)
    - Подкаталог ID 2 (таблица строк с ID 2)
  - Каталог меню (Menu)
    - Подкаталог ID 1 (например, меню с ID 1)
    - Подкаталог ID 2 (меню с ID 2)
```

Код для обработки Resource Directory в таком же формате:

```cpp
void OutputName(const PIMAGE_RESOURCE_DIRECTORY_ENTRY directoryEntrie , const PBYTE resourceSectionAddr)
{

	if (directoryEntrie->NameIsString)
	{
		auto dir_string = reinterpret_cast<PIMAGE_RESOURCE_DIR_STRING_U>(
			reinterpret_cast<PBYTE>(resourceSectionAddr) + directoryEntrie->NameOffset
			);
		std::wstring name(dir_string->NameString, dir_string->Length);
		std::wcout << L"name: " << name << std::endl;
	}
	else
		std::cout << std::hex << "Id: " << directoryEntrie->Id << std::endl;
}

void EnumerateResources(const PBYTE resourceSectionAddr , const PIMAGE_RESOURCE_DIRECTORY entry , int depth = 0)
{
	if (resourceSectionAddr == nullptr || entry == nullptr)
	{
		std::cerr << "Invalid pointer to resource section or directory!" << std::endl;
		return;
	}

	auto directory_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(
		reinterpret_cast<PBYTE>(entry) + sizeof(IMAGE_RESOURCE_DIRECTORY)
		);
	
	if (directory_entries == nullptr)
	{
		std::cerr << "Invalid pointer to directory entries!" << std::endl;
		return;
	}

	int  entries_size = (entry->NumberOfIdEntries + entry->NumberOfNamedEntries) - 1;
	for (int  i = 0; i <= entries_size  ; ++i)
	{
		if (directory_entries[i].DataIsDirectory)
		{
			std::cout << std::string(depth * 2, ' ') << " - " << "Directory ";
			OutputName(&directory_entries[i], resourceSectionAddr);

			auto data_entry_resource_directory = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(
				resourceSectionAddr + directory_entries[i].OffsetToDirectory
				);
			EnumerateResources(resourceSectionAddr, data_entry_resource_directory, depth + 1);
		}
		else
		{
			std::cout << std::string(depth * 2, ' ') << " - " << "Resource ";
			OutputName(&directory_entries[i], resourceSectionAddr);
		}
	}
	
	return;
}
```

## Итог:

Мы разобрались , как работать с директорий ресурса. Так же попрактиковалось с кодом, чтобы понять как он работает.

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