Загрузка данных


#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;
}