Начинающий
- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 123
- Реакции
- 3
Выкатили свежий релиз дампера для Apex Legends под DX12.
Очередная паста, но вполне рабочая база для тех, кто хочет поковырять оффсеты самостоятельно, а не клянчить их в каждом треде после очередного обновления игры. Если вы устали от того, что ваш софт отлетает в детект из-за кривых сигнатур или неактуальных адресов — этот инструмент поможет сдампить PE-образ прямо из памяти процесса.
Как это работает:
Техническая инфа:
Сурс написан на C++, юзает read_physical для обхода стандартных ограничений API Windows. В коде реализован фикс IAT (Import Address Table) и релокаций, чтобы полученный дамп можно было адекватно просмотреть в IDA или Ghidra. Также добавлена проверка энтропии секций — если видите высокий показатель, значит, там могут быть упакованные данные или защита от статического анализа.
Важные замечания:
Пока остальные ловят баны от кривых бесплатных инжекторов с ютуба или горят от потных киберспортсменов, юзеры YouGame юзают проверенные решения, копаются в сурсах, доминируют на сервере и сохраняют свои аккаунты.
Ссылка на гитхаб проекта:
Очередная паста, но вполне рабочая база для тех, кто хочет поковырять оффсеты самостоятельно, а не клянчить их в каждом треде после очередного обновления игры. Если вы устали от того, что ваш софт отлетает в детект из-за кривых сигнатур или неактуальных адресов — этот инструмент поможет сдампить PE-образ прямо из памяти процесса.
Как это работает:
- Ядро: Требуется ваш личный Kernel-драйвер для взаимодействия с памятью (RPM). Без него ничего не взлетит.
- Подготовка: Запускаете игру, заходите в стрельбище (Fire Range). Подвигайтесь, постреляйте, поменяйте пушки — это нужно, чтобы игра подгрузила все необходимые модули в память, иначе дамп будет неполным.
- Снятие дампа: Запускаете тулзу, она цепляется к r5apex_dx12.exe, вытягивает CR3 и выплевывает файл game_dumped.exe.
- Разбор: Полученный файл можно прогнать через профильные ресурсы для автоматического поиска оффсетов.
Техническая инфа:
Сурс написан на C++, юзает read_physical для обхода стандартных ограничений API Windows. В коде реализован фикс IAT (Import Address Table) и релокаций, чтобы полученный дамп можно было адекватно просмотреть в IDA или Ghidra. Также добавлена проверка энтропии секций — если видите высокий показатель, значит, там могут быть упакованные данные или защита от статического анализа.
Код:
#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 - §ions[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;
}
Важные замечания:
- Безопасность: Юзайте только на твинках. Любой прямой доступ к памяти через кастомный драйвер — это потенциальный триггер для античита, если ваш драйвер не андетект (UD) или вы не используете DMA-карту.
- Зависимости: Не забудьте подкинуть актуальные заголовки и убедиться, что у вас установлен VCRedist.
- Профит: Не надейтесь на кнопки "сделать все за меня". Это лишь инструмент для исследования, а не готовый чит.
Пока остальные ловят баны от кривых бесплатных инжекторов с ютуба или горят от потных киберспортсменов, юзеры YouGame юзают проверенные решения, копаются в сурсах, доминируют на сервере и сохраняют свои аккаунты.
Ссылка на гитхаб проекта:
Пожалуйста, авторизуйтесь для просмотра ссылки.