WELCOME TO INFOCHEATS.NET

INFOCHEATS is a community-driven platform focused on free game cheats, cheat development, and verified commercial software for a wide range of popular games. We provide a large collection of free cheats shared by the community. All public releases are checked for malicious code to reduce the risk of viruses, malware, or unwanted software before users interact with them.

Alongside free content, INFOCHEATS hosts an active marketplace with many independent sellers offering commercial cheats. Each product is discussed openly, with user feedback, reviews, and real usage experience available to help you make informed decisions before purchasing.

Whether you are looking for free cheats, exploring paid solutions, comparing sellers, or studying how cheats are developed and tested, INFOCHEATS brings everything together in one place — transparently and community-driven.

Guide [Dump] Apex Legends — PE Memory Dumper with Kernel Driver Support (C++)

byte_corvus

Newbie
Newbie

byte_corvus

Newbie
Newbie
Status
Offline
Joined
Mar 3, 2026
Messages
95
Reaction score
7
Fresh release just hit the boards for the Apex crowd, and honestly, if you're still manually scanning memory regions for offsets like it's 2019, you're just wasting your time.

What is this?
This is a PE Memory Dumper designed for r5apex_dx12.exe. Instead of scraping around with basic tools, this uses a kernel driver to pull the process memory directly. It performs a full module dump, handles section alignment by setting PointerToRawData to match the virtual layout, and attempts to fix the IAT so you can actually load the dump into IDA or ReClass without the headers screaming at you.

Technical Breakdown:
  1. Kernel-Level Access: Relies on a driver for RPM, bypassing standard user-mode handle restrictions.
  2. Memory Alignment: Automatically corrects the file structure for disk compatibility.
  3. Entropy Check: Flags encrypted sections—handy if you're trying to figure out which modules are packed by the AC.
  4. IAT Patching: Reads resolved addresses from live process memory to fix imports, making your dump significantly more readable for reverse engineering.

Usage Notes:
Before you fire this up, make sure your kernel driver is already loaded and stable. Get the game running, jump into the firing range, and move around to ensure the memory is mapped correctly—if you dump while idling in the main menu, half your offsets will be null or uninitialized. Once the dumper spits out game_dumped.exe, you can feed it into your analysis pipeline.

Warnings & Security:
  1. Driver Safety: If you're using a public or leaked driver to facilitate this, assume it's already flagged. Use a custom mapper or a proven private driver if you care about your hardware ID.
  2. Anti-Cheat Awareness: EAC checks for unexpected handles and driver communications. This is a tool for RE and offset hunting, not something you should leave running while playing "legit" or in a ranked session.
  3. VM/Sandbox:** If you're pulling this off GitHub, always verify the imports. If it looks like it's doing more than just reading memory, it’s probably a RAT.


Source & Tooling:
The source code provided below is a solid base for anyone looking to automate their offset collection for the new engine.

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

Find the full project and latest updates here:
You cant view this link please login.


While skids are waiting for some P2C dev to update their public offsets after every minor patch, the real ones are using automated dumpers to keep their own internal tables updated in real-time. Keep your local builds clean and stop relying on others to do the heavy lifting.
 
Top