// Содержание
Привет! ? Сегодня мы заглянем в «кухню» операционной системы 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 = §ionHeaders[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-файла как на ладони!
? Заключение
Теперь вы знаете, что загрузчик — это не просто «черный ящик», а сложный механизм, который:
- Проверяет файл
- Выделяет память
- Загружает секции
- Разрешает зависимости
- Исправляет адреса
- Запускает код
Главный секрет: Загрузчик делает программу «домашней» в памяти Windows, прежде чем та начнет работать. Как переезд в новую квартиру: пока не расставишь мебель — жить нельзя! ?
Удачи в исследованиях! Если что-то непонятно — пишите в комментариях, разберем вместе ?
// КОММЕНТАРИИ (0)