Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Исходник [Сурс] Apex Legends — Утилита для дампа PE памяти через Kernel Driver (C++)

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
123
Реакции
3
Выкатили свежий релиз дампера для Apex Legends под DX12.

Очередная паста, но вполне рабочая база для тех, кто хочет поковырять оффсеты самостоятельно, а не клянчить их в каждом треде после очередного обновления игры. Если вы устали от того, что ваш софт отлетает в детект из-за кривых сигнатур или неактуальных адресов — этот инструмент поможет сдампить PE-образ прямо из памяти процесса.

Как это работает:
  1. Ядро: Требуется ваш личный Kernel-драйвер для взаимодействия с памятью (RPM). Без него ничего не взлетит.
  2. Подготовка: Запускаете игру, заходите в стрельбище (Fire Range). Подвигайтесь, постреляйте, поменяйте пушки — это нужно, чтобы игра подгрузила все необходимые модули в память, иначе дамп будет неполным.
  3. Снятие дампа: Запускаете тулзу, она цепляется к r5apex_dx12.exe, вытягивает CR3 и выплевывает файл game_dumped.exe.
  4. Разбор: Полученный файл можно прогнать через профильные ресурсы для автоматического поиска оффсетов.

Техническая инфа:
Сурс написан на C++, юзает read_physical для обхода стандартных ограничений API Windows. В коде реализован фикс IAT (Import Address Table) и релокаций, чтобы полученный дамп можно было адекватно просмотреть в IDA или Ghidra. Также добавлена проверка энтропии секций — если видите высокий показатель, значит, там могут быть упакованные данные или защита от статического анализа.

Код:
Expand Collapse Copy
#define _CRT_SECURE_NO_WARNINGS
 
#include <Windows.h>
#include <winternl.h>
#include <process.h>
#include <TlHelp32.h>
#include <inttypes.h>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <chrono>
#include <sstream>
#include <memory>
#include <string_view>
#include <cstdint>
#include <string>
#include <cmath>
#include <thread>
#include <cassert>
#include <xstring>
#include <dwmapi.h>
#include <vector>
#include <map>
#include <array>
#include <fstream>
#include <direct.h>
#include <set>
#include <stack>
#include <unordered_set>
#include <wininet.h>
#include <random>
#include <winternl.h>
#include <Psapi.h>
#include <urlmon.h>
 
#include "driver.h"
 
#pragma comment(lib, "wininet.lib")
 
// Fallback NT header finder
uint64_t FindNtHeaders(uint64_t base, size_t maxSize = 0x1000000) {
    for (size_t i = 0; i < maxSize; i += 0x1000) {
        DWORD sig = Read<DWORD>(base + i);
        if (sig == IMAGE_NT_SIGNATURE)
            return base + i;
    }
    return 0;
}
 
// Safe memory reader
bool ReadSafe(uint64_t address, void* buffer, size_t size) {
    uint8_t* out = reinterpret_cast<uint8_t*>(buffer);
    for (size_t offset = 0; offset < size; offset += 0x1000) {
        size_t chunk = (size - offset < 0x1000) ? (size - offset) : 0x1000;
        if (read_physical(reinterpret_cast<PVOID>(address + offset), out + offset, (DWORD)chunk)) {
            // Fill unreadable pages with nulls to maintain alignment
            memset(out + offset, 0, chunk);
        }
    }
    return true;
}
 
// Entropy calculation
double CalculateEntropy(const uint8_t* data, size_t size) {
    if (!size) return 0.0;
    int freq[256]{};
 
    for (size_t i = 0; i < size; i++)
        freq[data[i]]++;
 
    double entropy = 0.0;
    for (int i = 0; i < 256; i++) {
        if (freq[i]) {
            double p = static_cast<double>(freq[i]) / size;
            entropy -= p * log2(p);
        }
    }
    return entropy;
}
 
// Relocations
bool FixRelocations(std::vector<uint8_t>& image, IMAGE_NT_HEADERS64& nt, uint64_t runtimeBase) {
    auto& dir = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    if (!dir.VirtualAddress || !dir.Size)
        return true;
 
    uint64_t delta = runtimeBase - nt.OptionalHeader.ImageBase;
    uint8_t* cur = image.data() + dir.VirtualAddress;
    uint8_t* end = cur + dir.Size;
 
    while (cur < end) {
        auto* block = reinterpret_cast<IMAGE_BASE_RELOCATION*>(cur);
        cur += sizeof(*block);
 
        size_t count = (block->SizeOfBlock - sizeof(*block)) / sizeof(WORD);
        WORD* entries = reinterpret_cast<WORD*>(cur);
 
        for (size_t i = 0; i < count; i++) {
            WORD type = entries[i] >> 12;
            WORD off = entries[i] & 0xFFF;
 
            if (type == IMAGE_REL_BASED_DIR64) {
                uint64_t* patch = reinterpret_cast<uint64_t*>(image.data() + block->VirtualAddress + off);
                *patch += delta;
            }
        }
        cur += count * sizeof(WORD);
    }
 
    nt.OptionalHeader.ImageBase = runtimeBase;
    return true;
}
 
// Fix Import Address Table (IAT) by copying original memory addresses
bool FixIAT(uint64_t base, std::vector<uint8_t>& image, IMAGE_NT_HEADERS64* nt) {
    auto& dir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (!dir.VirtualAddress || !dir.Size) return true;
 
    auto* imports = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(image.data() + dir.VirtualAddress);
 
    for (; imports->Name; imports++) {
        uint64_t firstThunkRVA = imports->FirstThunk;
        uint64_t thunkRVA = imports->OriginalFirstThunk ? imports->OriginalFirstThunk : imports->FirstThunk;
 
        for (int i = 0; ; i++) {
            uint64_t funcAddr = 0;
            uint64_t offset = i * sizeof(uint64_t);
 
            // Read the actual resolved address from the live process memory
            if (!read_physical(reinterpret_cast<PVOID>(base + firstThunkRVA + offset), &funcAddr, sizeof(funcAddr)))
                break;
 
            if (funcAddr == 0) break;
 
            // Patch the dump's memory with the live resolved address
            *reinterpret_cast<uint64_t*>(image.data() + firstThunkRVA + offset) = funcAddr;
        }
    }
    return true;
}
 
namespace Utils {
    double CalculateEntropy(const uint8_t* data, size_t size) {
        if (!size) return 0.0;
        int freq[256]{};
        for (size_t i = 0; i < size; i++) freq[data[i]]++;
        double entropy = 0.0;
        for (int i = 0; i < 256; i++) {
            if (freq[i]) {
                double p = static_cast<double>(freq[i]) / size;
                entropy -= p * log2(p);
            }
        }
        return entropy;
    }
}
 
// Dump module
bool DumpModule(uint64_t base, const std::string& outName) {
    IMAGE_DOS_HEADER dos{};
    if (!read_physical((PVOID)base, &dos, sizeof(dos)) || dos.e_magic != IMAGE_DOS_SIGNATURE)
        return false;
 
    IMAGE_NT_HEADERS64 nt{};
    if (!read_physical((PVOID)(base + dos.e_lfanew), &nt, sizeof(nt)))
        return false;
 
    // 1. Prepare Buffer (SizeOfImage is the size in memory)
    size_t imageSize = nt.OptionalHeader.SizeOfImage;
    std::vector<uint8_t> image(imageSize, 0);
 
    // 2. Read Headers
    ReadSafe(base, image.data(), nt.OptionalHeader.SizeOfHeaders);
 
    // 3. Read Sections
    uint64_t sectionHeaderAddr = base + dos.e_lfanew + sizeof(IMAGE_NT_HEADERS64);
    std::vector<IMAGE_SECTION_HEADER> sections(nt.FileHeader.NumberOfSections);
    read_physical((PVOID)sectionHeaderAddr, sections.data(), sections.size() * sizeof(IMAGE_SECTION_HEADER));
 
    for (auto& s : sections) {
        uint64_t sectionVA = base + s.VirtualAddress;
        uint32_t sectionSize = s.Misc.VirtualSize;
 
        std::cout << "[SEC] " << (char*)s.Name << " | VA: 0x" << std::hex << s.VirtualAddress << " | Size: 0x" << sectionSize;
 
        ReadSafe(sectionVA, image.data() + s.VirtualAddress, sectionSize);
 
        // Check for encryption/compression
        double entropy = Utils::CalculateEntropy(image.data() + s.VirtualAddress, sectionSize);
        std::cout << " | Entropy: " << std::dec << entropy << (entropy > 7.4 ? " [ENCRYPTED]" : "") << std::endl;
 
        // CRITICAL: Set PointerToRawData = VirtualAddress and SizeOfRawData = VirtualSize
        // This makes the file "Disk Layout" identical to "Memory Layout"
        auto* headerInDump = reinterpret_cast<IMAGE_SECTION_HEADER*>(image.data() + (sectionHeaderAddr - base) + (&s - &sections[0]) * sizeof(IMAGE_SECTION_HEADER));
        headerInDump->PointerToRawData = s.VirtualAddress;
        headerInDump->SizeOfRawData = s.Misc.VirtualSize;
    }
 
    // 4. Fix IAT (Optional but recommended for analysis)
    FixIAT(base, image, &nt);
 
    // 5. Write to File
    std::ofstream f(outName, std::ios::binary);
    if (f.is_open()) {
        f.write(reinterpret_cast<char*>(image.data()), image.size());
        f.close();
        return true;
    }
    return false;
}
 
void SetColor(WORD color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
 
// ==================== Main ====================
int main() {
    SetConsoleTitleA("PE Memory Dumper");
 
    const WORD COLOR_SUCCESS = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    const WORD COLOR_ERROR = FOREGROUND_RED | FOREGROUND_INTENSITY;
    const WORD COLOR_INFO = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // cyan
 
    // Driver
    SetColor(COLOR_INFO);
    std::cout << "[*] Searching for driver...\n";
    if (!find_driver()) {
        SetColor(COLOR_ERROR);
        std::cerr << "[-] Failed to find driver. Make sure it is loaded!\n";
        system("pause"); return 1;
    }
    SetColor(COLOR_SUCCESS);
    std::cout << "[+] Driver connected!\n";
 
    // Process
    SetColor(COLOR_INFO);
    std::wcout << L"[*] Searching for process: r5apex_dx12.exe\n";
    if (!find_process("r5apex_dx12.exe")) {
        SetColor(COLOR_ERROR);
        std::cerr << "[-] Failed to find process. Is the game running?\n";
        system("pause"); return 1;
    }
    SetColor(COLOR_SUCCESS);
    std::cout << "[+] Process found!\n";
 
    // CR3
    SetColor(COLOR_INFO);
    std::cout << "[*] Getting CR3...\n";
    uint64_t cr3 = CR3();
    if (!cr3) { SetColor(COLOR_ERROR); std::cerr << "[-] Failed to get CR3!\n"; system("pause"); return 1; }
    SetColor(COLOR_SUCCESS);
    std::cout << "[+] CR3 obtained: 0x" << std::hex << cr3 << std::dec << "\n";
 
    // Module base
    SetColor(COLOR_INFO);
    std::cout << "[*] Finding module base...\n";
    uint64_t moduleBase = find_image();
    if (!moduleBase) { SetColor(COLOR_ERROR); std::cerr << "[-] Failed to find module base!\n"; system("pause"); return 1; }
 
    IMAGE_DOS_HEADER dos{};
    if (!read_physical((PVOID)moduleBase, &dos, sizeof(dos)) || dos.e_magic != IMAGE_DOS_SIGNATURE) {
        SetColor(COLOR_ERROR); std::cerr << "[-] Failed to read DOS header!\n"; system("pause"); return 1;
    }
    IMAGE_NT_HEADERS64 nt{};
    if (!read_physical((PVOID)(moduleBase + dos.e_lfanew), &nt, sizeof(nt)) || nt.Signature != IMAGE_NT_SIGNATURE) {
        SetColor(COLOR_ERROR); std::cerr << "[-] Failed to read NT headers!\n"; system("pause"); return 1;
    }
    uint64_t moduleSize = nt.OptionalHeader.SizeOfImage;
 
    SetColor(COLOR_SUCCESS);
    std::cout << "[+] Module base: 0x" << std::hex << moduleBase
        << "  Size: 0x" << moduleSize << std::dec << "\n";
 
    // Dump module
    std::vector<uint8_t> dumpedImage;
    SetColor(COLOR_INFO);
    std::cout << "[*] Dumping module...\n";
    if (!DumpModule(moduleBase, "game_dumped.exe")) {
        SetColor(COLOR_ERROR); std::cerr << "[-] Dump failed!\n"; system("pause"); return 1;
    }
    SetColor(COLOR_SUCCESS);
    std::cout << "[+] Dump completed successfully!\n";
 
    SetColor(COLOR_SUCCESS);
    std::cout << "[*] Press Enter to exit...\n";
    std::cin.get();
 
    SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    return 0;
}

Важные замечания:
  1. Безопасность: Юзайте только на твинках. Любой прямой доступ к памяти через кастомный драйвер — это потенциальный триггер для античита, если ваш драйвер не андетект (UD) или вы не используете DMA-карту.
  2. Зависимости: Не забудьте подкинуть актуальные заголовки и убедиться, что у вас установлен VCRedist.
  3. Профит: Не надейтесь на кнопки "сделать все за меня". Это лишь инструмент для исследования, а не готовый чит.

Пока остальные ловят баны от кривых бесплатных инжекторов с ютуба или горят от потных киберспортсменов, юзеры YouGame юзают проверенные решения, копаются в сурсах, доминируют на сервере и сохраняют свои аккаунты.

Ссылка на гитхаб проекта:
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
1774285761783.png
мне кажется это чутка не так работает
ты же тупо с соседнего форума перезалил тему
 
Последнее редактирование:
Назад
Сверху Снизу