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

# Exceptions

## Введение

В этом разделе мы рассмотрим **Exception Directory** . Этот раздел играет ключевую роль в обработке исключений в приложениях.

**Exception Directory** используется для хранения данных, необходимых системе во время обработки исключений. Эти данные помогают программе корректно обрабатывать ошибки, восстанавливать состояние приложения или завершать выполнение без сбоев.

## Exception Directory

**Exception Directory** — это специальная таблица в PE-файле, которая используется для хранения информации, необходимой при обработке исключений.

Во время компиляции компилятор помещает данные **Exception Directory** в секцию **.pdata**, но она также может находиться, например, в секции **.rdata** (это зависит от настроек линковки или платформы).

**Exception Directory** представляет собой массив структур \_RUNTIME\_FUNCTION. Каждая запись в этом массиве представляет собой конкретную функцию из исполняемого файла. Структура \_RUNTIME\_FUNCTION хранит адреса начала функции и её конца.

Для каждой функции, которая может обрабатывать исключения, создается запись в массиве **Exception Directory**. Даже если функция не содержит явных блоков исключение таких как try-catch, такая запись всё равно создается. Это необходимо для того, чтобы при возникновении исключения в вызываемой функции оно могло быть корректно обработано или завершено.

Необходимо учитывать, что для динамически создаваемых функций (например, в JIT-компиляторах) следует использовать функции RtlInstallFunctionTableCallback или RtlAddFunctionTable для регистрации этих функций в системе. Если этого не сделать, обработка исключений и отладка могут работать некорректно.

Чтобы получить адрес **Exception Directory**, мы можем обратиться к нему через **DataDirectory**:

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

### IMAGE\_RUNTIME\_FUNCTION\_ENTRY (RUNTIME\_FUNCTION)

**IMAGE\_RUNTIME\_FUNCTION\_ENTRY** (или **RUNTIME\_FUNCTION**) — это структура, которая описывает функцию и содержит данные, необходимые для её обработки при возникновении исключений.

Структура **RUNTIME\_FUNCTION** должна быть выровнена по границе в 4 байта (DWORD) в памяти, чтобы обеспечить эффективный доступ к данным.

```cpp
typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
    DWORD BeginAddress;
    DWORD EndAddress;
    union {
        DWORD UnwindInfoAddress;
        DWORD UnwindData;
    } DUMMYUNIONNAME;
} _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;

typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
```

* BeginAddress: RVA на начало функции.
* EndAddress: RVA на конец функции.
* UnwindInfoAddress: RVA на структуру `_UNWIND_INFO`

Я написал небольшую функцию для обработки. Этот код. просто выводит все данные из структуры \_IMAGE\_RUNTIME\_FUNCTION\_ENTRY

```cpp
void AnalyseRuntimeFunctions( const DWORD rva_to_runtime_struct , const PBYTE pe_file )
{
	
	auto runtime_function = reinterpret_cast<PRUNTIME_FUNCTION>(pe_file + rva_to_runtime_struct);
	if (!runtime_function)
	{
		std::cout << "[!] Cannot find runtime function " << std::endl;
		return;
	}

	while (runtime_function->UnwindData != 0)
	{

		std::cout << std::string(8, '*') << std::endl;

		std::cout << " Begin address: " << std::hex << runtime_function->BeginAddress << ";";
		std::cout <<  " End address: " << std::hex << runtime_function->EndAddress <<  ";";
		std::cout <<  " Unwind Data: " << std::hex << runtime_function->UnwindInfoAddress << std::endl;

		std::cout << std::string(8, '*') << std::endl;
		std::cout << std::endl;


		runtime_function++;
	}

}
```

### UNWIND\_INFO ( Информация о раскрутке )

Как было упомянуто ранее, поле UnwindInfoAddress структуры RUNTIME\_FUNCTION содержит адрес структуры UNWIND\_INFO.

```cpp
typedef struct _UNWIND_INFO {
    UBYTE Version         : 3;
    UBYTE Flags           : 5;
    UBYTE SizeOfProlog;
    UBYTE CountOfCodes;  
    UBYTE FrameRegister  : 4;
    UBYTE FrameOffset    : 4;
    UNWIND_CODE UnwindCode[1];
    
} UNWIND_INFO, *PUNWIND_INFO;
```

* Version - версия информации о раскрутке. В настоящий момент последней версией является версия 2.
* Flags — может содержать три флага: **UNW\_FLAG\_EHANDLER**, **UNW\_FLAG\_UHANDLER** и **UNW\_FLAG\_CHAININFO**.
  * UNW\_FLAG\_EHANDLER: указывает на наличие обработчика исключений (exception handler) в функции. Этот обработчик будет вызван во время поиска подходящего обработчика исключений.
  * UNW\_FLAG\_UHANDLER: указывает на наличие обработчика раскрутки (termination handler). Этот обработчик будет вызван во время раскрутки стека, когда происходит выход из текущей функции.
  * UNW\_FLAG\_CHAININFO: означает, что данная структура UNWIND\_INFO не является начальной, а представляет собой продолжение (chained) предыдущей структуры UNWIND\_INFO. Установка этого флага исключает возможность установки флагов UNW\_FLAG\_EHANDLER и UNW\_FLAG\_UHANDLER.
* SizeOfProlog - содержит размер пролога в байтах
* FrameRegister - обозначает номер регистра, который используется в качестве указателя кадра
* FrameOffset - содержит значение в 16-байтных блоках, которое складывается со значением RSP в момент установки указателя кадра функции.

## Итог

Как итог, я хотел бы подытожить содержание этой статьи. В этой части , я лишь рассказал про структуру строения Exception Directory.&#x20;

Для более глубокого понимания процесса обработки исключений в Windows, настоятельно рекомендую ознакомиться с рядом статей:

* [Исключения в Windows x64. Как это работает. Часть 1](https://www.aladdin-rd.ru/company/pressroom/articles/isklucenia_v_windows_x64_kak_eto_rabotaet_cast_1/)
* [Исключения в Windows x64. Как это работает. Часть 2](https://www.aladdin-rd.ru/company/pressroom/articles/isklucenia_v_windows_x64_kak_eto_rabotaet_cast_2/)
* [Исключения в Windows x64. Как это работает. Часть 3](https://www.aladdin-rd.ru/company/pressroom/articles/isklucenia_v_windows_x64_kak_eto_rabotaet_cast_3/)
* [Обработка исключений в 64-разрядных системах | Microsoft Learn](https://learn.microsoft.com/ru-ru/cpp/build/exception-handling-x64?view=msvc-170)
* [Данные раскрутки для обработки исключений и поддержки отладчика | Microsoft Learn](https://learn.microsoft.com/ru-ru/previous-versions/0kd71y96\(v=vs.100\))
* [c++ - Struct RUNTIME\_FUNCTION - Stack Overflow](https://stackoverflow.com/a/55896238)

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