Загрузка данных
#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>
// Функция для чтения файла с диска во временный буфер
std::vector<BYTE> ReadFileToBuffer(const std::string& filePath) {
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "[!] Не удалось открыть файл: " << filePath << std::endl;
return {};
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<BYTE> buffer(size);
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
std::cerr << "[!] Ошибка при чтении файла в память." << std::endl;
return {};
}
return buffer;
}
int main() {
// Для демонстрации возьмём стандартную системную библиотеку
std::string targetPath = "C:\\Windows\\System32\\user32.dll";
std::cout << "[*] Загрузка файла: " << targetPath << std::endl;
// ШАГ 1: Считывание файла во временный буфер
std::vector<BYTE> fileBuffer = ReadFileToBuffer(targetPath);
if (fileBuffer.empty()) return 1;
BYTE* pRawData = fileBuffer.data();
// ШАГ 2: Разбор DOS-заголовка (IMAGE_DOS_HEADER)
PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pRawData);
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { // 'MZ'
std::cerr << "[!] Ошибка: Неверная сигнатура DOS (ожидалось 'MZ')." << std::endl;
return 1;
}
// Переход к NT-заголовкам по смещению e_lfanew
PIMAGE_NT_HEADERS64 pNtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS64>(pRawData + pDosHeader->e_lfanew);
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { // 'PE\0\0'
std::cerr << "[!] Ошибка: Неверная сигнатура PE (ожидалось 'PE\\0\\0')." << std::endl;
return 1;
}
// Проверка архитектуры (x64)
if (pNtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
std::cerr << "[!] Ошибка: Поддерживаются только x64 PE-файлы." << std::endl;
return 1;
}
std::cout << "[+] PE-заголовки успешно разобраны." << std::endl;
std::cout << " - Предпочтительный Base Address: 0x" << std::hex << pNtHeaders->OptionalHeader.ImageBase << std::endl;
std::cout << " - Размер образа в памяти (SizeOfImage): 0x" << pNtHeaders->OptionalHeader.SizeOfImage << std::endl;
// ШАГ 3: Выделение память под образ образа (SizeOfImage)
// Сначала пробуем выделить по предпочтительному адресу ImageBase
BYTE* pImageBase = reinterpret_cast<BYTE*>(VirtualAlloc(
reinterpret_cast<LPVOID>(pNtHeaders->OptionalHeader.ImageBase),
pNtHeaders->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
));
// Если предпочтительный адрес занят, выделяем память по абсолютно любому доступному адресу
if (!pImageBase) {
std::cout << "[!] Предпочтительный адрес занят. Выделяем случайный адрес памяти..." << std::endl;
pImageBase = reinterpret_cast<BYTE*>(VirtualAlloc(
NULL,
pNtHeaders->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
));
}
if (!pImageBase) {
std::cerr << "[!] Ошибка выделения виртуальной памяти." << std::endl;
return 1;
}
std::cout << "[+] Память выделена по адресу: 0x" << std::hex << (ULONG_PTR)pImageBase << std::endl;
// ШАГ 4: Копирование заголовков и секций
// 4.1 Копируем заголовки PE (SizeOfHeaders)
memcpy(pImageBase, pRawData, pNtHeaders->OptionalHeader.SizeOfHeaders);
// 4.2 Копируем секции с учетом SectionAlignment
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
std::cout << "[*] Копирование секций (" << pNtHeaders->FileHeader.NumberOfSections << " шт.):" << std::endl;
for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i) {
if (pSectionHeader[i].SizeOfRawData == 0) continue; // Пропускаем пустые секции (например, .bss)
BYTE* pDest = pImageBase + pSectionHeader[i].VirtualAddress;
BYTE* pSrc = pRawData + pSectionHeader[i].PointerToRawData;
// Копируем сырые данные секции из файла в ее виртуальный адрес
memcpy(pDest, pSrc, pSectionHeader[i].SizeOfRawData);
std::cout << " - Секция [" << pSectionHeader[i].Name << "]"
<< " -> VirtualAddress: 0x" << pSectionHeader[i].VirtualAddress
<< " (Размер: 0x" << pSectionHeader[i].SizeOfRawData << " байт)" << std::endl;
}
// ШАГ 5: Разрешение таблицы импорта (IAT — Import Address Table)
IMAGE_DATA_DIRECTORY importDir = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (importDir.Size > 0) {
std::cout << "[*] Настройка Таблицы Адресов Импорта (IAT)..." << std::endl;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
pImageBase + importDir.VirtualAddress
);
// Перебираем дескрипторы пока не встретим нулевой дескриптор
while (pImportDesc->Name != 0) {
LPCSTR szDllName = reinterpret_cast<LPCSTR>(pImageBase + pImportDesc->Name);
HMODULE hImportDll = LoadLibraryA(szDllName);
if (!hImportDll) {
std::cerr << "[!] Не удалось загрузить зависимую DLL: " << szDllName << std::endl;
pImportDesc++;
continue;
}
// OriginalFirstThunk указывает на имена/ординалы функций (Import Lookup Table)
// FirstThunk указывает на IAT, куда мы запишем реальные адреса функций
PIMAGE_THUNK_DATA64 pThunk = reinterpret_cast<PIMAGE_THUNK_DATA64>(
pImageBase + (pImportDesc->OriginalFirstThunk ? pImportDesc->OriginalFirstThunk : pImportDesc->FirstThunk)
);
PIMAGE_THUNK_DATA64 pIAT = reinterpret_cast<PIMAGE_THUNK_DATA64>(
pImageBase + pImportDesc->FirstThunk
);
while (pThunk->u1.AddressOfData != 0) {
FARPROC pFuncAddress = NULL;
// Проверяем, импортируется ли функция по ординалу или по имени
if (IMAGE_SNAP_BY_ORDINAL64(pThunk->u1.Ordinal)) {
ULONGLONG ordinal = IMAGE_ORDINAL64(pThunk->u1.Ordinal);
pFuncAddress = GetProcAddress(hImportDll, reinterpret_cast<LPCSTR>(ordinal));
} else {
PIMAGE_IMPORT_BY_NAME pImportByName = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
pImageBase + pThunk->u1.AddressOfData
);
pFuncAddress = GetProcAddress(hImportDll, pImportByName->Name);
}
// Записываем реальный адрес найденной функции в IAT
pIAT->u1.Function = reinterpret_cast<ULONG_PTR>(pFuncAddress);
pThunk++;
pIAT++;
}
pImportDesc++;
}
std::cout << "[+] Таблица импорта (IAT) успешно заполнена." << std::endl;
}
// ШАГ 6: Применение базовых релокаций (Base Relocations)
// Разница между фактическим базовым адресом и предпочтительным адресом компиляции
INT_PTR delta = reinterpret_cast<INT_PTR>(pImageBase) - static_cast<INT_PTR>(pNtHeaders->OptionalHeader.ImageBase);
if (delta != 0) {
std::cout << "[*] Фактический адрес отличается от предпочтительного. Применение Base Relocations..." << std::endl;
std::cout << " - Delta (смещение): 0x" << std::hex << delta << std::endl;
IMAGE_DATA_DIRECTORY relocDir = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (relocDir.Size > 0) {
PIMAGE_BASE_RELOCATION pReloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
pImageBase + relocDir.VirtualAddress
);
while (pReloc->VirtualAddress > 0) {
DWORD relocCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PWORD pRelocData = reinterpret_cast<PWORD>(pReloc + 1);
for (DWORD i = 0; i < relocCount; ++i) {
WORD type = pRelocData[i] >> 12; // Старшие 4 бита — тип релокации
WORD offset = pRelocData[i] & 0x0FFF; // Младшие 12 бит — смещение внутри страницы
// Для x64 главной релокацией является IMAGE_REL_BASED_DIR64 (абсолютный 64-битный адрес)
if (type == IMAGE_REL_BASED_DIR64) {
ULONG_PTR* pPatchAddress = reinterpret_cast<ULONG_PTR*>(
pImageBase + pReloc->VirtualAddress + offset
);
*pPatchAddress += delta; // Корректируем указатель на величину delta
}
}
// Переход к следующему блоку релокаций
pReloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
reinterpret_cast<BYTE*>(pReloc) + pReloc->SizeOfBlock
);
}
std::cout << "[+] Релокация адресов завершена успешно." << std::endl;
} else {
std::cout << "[!] Предупреждение: Образ смещен, но таблица релокаций (Base Relocations) отсутствует." << std::endl;
}
} else {
std::cout << "[+] Образ загружен по предпочтительному адресу. Релокация не требуется." << std::endl;
}
std::cout << "\n[=== УСПЕХ ===] Образ полностью загружен в оперативную память процесса!" << std::endl;
// Освобождаем выделенную виртуальную память перед завершением
VirtualAlloc(pImageBase, 0, MEM_RELEASE, PAGE_NOACCESS);
return 0;
}