📄 Как Windows оживляет программы? Разбираем загрузчик PE-файлов

Как Windows оживляет программы? Разбираем загрузчик PE-файлов

Portable Executable

Привет! ? Сегодня мы заглянем в «кухню» операционной системы Windows и разберем, как работает загрузчик исполняемых файлов. Это как рецепт волшебного зелья: смешиваем технические детали, код на C++ и понятные аналогии. Поехали!

? Что такое PE-файл?

PE (Portable Executable) — это формат исполняемых файлов в Windows (.exe.dll). Представьте его как «чемодан» для переезда:

  • Код программы — ваши ценные вещи
  • Данные — инструкции по распаковке
  • Системные заголовки — наклейки «Осторожно, стекло!»

? Структура PE-файла (Разобрали в пред. статье)

Ключевой терминRVA (Relative Virtual Address) смещение от базового адреса загрузки.

? Алгоритм работы загрузчика: 6 шагов к запуску

Шаг 1️⃣: Проверка файла

Загрузчик ищет сигнатуру «PE\0\0» (как проверка паспорта):

// Читам заголовки
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)fileData;
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(fileData + dosHeader->e_lfanew);
// Магическая проверка
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
throw "Это не PE-файл!";
}

Шаг 2️⃣: Выделение «жилплощади» в памяти

Пытаемся загрузить по адресу ImageBase:

void* imageBase = VirtualAlloc(
(LPVOID)ntHeaders->OptionalHeader.ImageBase,
ntHeaders->OptionalHeader.SizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE
);
// Если не получилось - ищем свободный адрес
if (!imageBase) {
imageBase = VirtualAlloc(NULL, ...); // Автораспределение ОС
}

Шаг 3️⃣: «Распаковка чемодана» — загрузка секций

Копируем код и данные в память:

// Для каждой секции в таблице:
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER* section = &sectionHeaders[i];
// Копируем из файла в память
memcpy(
(BYTE*)imageBase + section->VirtualAddress, // Адрес в памяти
fileData + section->PointerToRawData, // Данные в файле
section->SizeOfRawData
);
}

Шаг 4️⃣: Игра в «пазл» — разрешение импорта

Загрузчик ищет нужные DLL (например, kernel32.dll) и подключает функции:

// Перебираем записи в таблице импорта
IMAGE_IMPORT_DESCRIPTOR* importDesc = ...;
while (importDesc->Name) {
const char* dllName = (char*)(imageBase + importDesc->Name);
HMODULE dllHandle = LoadLibraryA(dllName);
// Решаем адреса функций
IMAGE_THUNK_DATA* thunk = ...;
while (thunk->u1.AddressOfData) {
FARPROC funcAddr = GetProcAddress(dllHandle, ...);
thunk->u1.Function = (DWORD_PTR)funcAddr; // Подставляем адрес!
thunk++;
}
importDesc++;
}

Шаг 5️⃣: «Переезд» — обработка релокаций

Если загрузка прошла не по ImageBase, нужно поправить адреса:

// Получаем данные релокаций
IMAGE_BASE_RELOCATION* reloc = ...;
while (reloc->SizeOfBlock) {
DWORD* entries = (DWORD*)(reloc + 1);
int numEntries = (reloc->SizeOfBlock - sizeof(*reloc)) / sizeof(WORD);
for (int i = 0; i < numEntries; i++) {
if (entries[i] >> 12 == IMAGE_REL_BASED_HIGHLOW) {
DWORD* patchAddr = (DWORD*)(imageBase + reloc->VirtualAddress + (entries[i] & 0xFFF));
*patchAddr += (DWORD)imageBase - ntHeaders->OptionalHeader.ImageBase; // Корректируем!
}
}
reloc = (IMAGE_BASE_RELOCATION*)((BYTE*)reloc + reloc->SizeOfBlock);
}

Шаг 6️⃣: Старт программы!

Передаём управление на точку входа:

// Тип точки входа: int __stdcall EntryPoint(void)
typedef int (__stdcall *EntryPointFunc)();
EntryPointFunc entry = (EntryPointFunc)(
(BYTE*)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint
);
entry(); // Запуск!

? Почему это важно?

  • Безопасность: Понимание загрузки помогает бороться с вирусами
  • Оптимизация: Знание структуры PE ускоряет отладку
  • Магия: Вы больше не верите, что «программы просто запускаются»

Совет начинающим: Поиграйтесь с утилитой PEView — она покажет «внутренности» любого PE-файла как на ладони!

? Заключение

Теперь вы знаете, что загрузчик — это не просто «черный ящик», а сложный механизм, который:

  1. Проверяет файл
  2. Выделяет память
  3. Загружает секции
  4. Разрешает зависимости
  5. Исправляет адреса
  6. Запускает код

Главный секрет: Загрузчик делает программу «домашней» в памяти Windows, прежде чем та начнет работать. Как переезд в новую квартиру: пока не расставишь мебель — жить нельзя! ?

Удачи в исследованиях! Если что-то непонятно — пишите в комментариях, разберем вместе ?

// КОММЕНТАРИИ (0)

// ОСТАВИТЬ КОММЕНТАРИЙ

// Ваш email не будет опубликован. Обязательные поля помечены *