- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 505
- Реакции
- 12
Народ, кто плотно сидит на DMA-сетапах в Apex, решил поделиться наработками. Видел кучу нубских вопросов по интеграции, так что выкладываю базу для простого внешнего радара. Никакого aimbot-мусора, только чтение памяти через MemProcFS.
Техническая база:
В основе лежит работа с M-драйвом, который поднимает MemProcFS. Скрипт подтягивает базовый адрес через vads или memmap и долбится в memory.vmem.
Нюансы реализации:
По поводу работы с нейросетками, о которых спрашивали в источнике — не советую городить огород на пустом месте. Для обычного радара достаточно простого парсинга структур. Если лезете в DMA, то сразу помните про прошивку карты и кастомный софт, иначе отлететь по железу — вопрос времени.
Код рабочий, тестил на актуальной версии игры. Главное — убедитесь, что правильно выставили путь к M-диску и у вас есть права на чтение memory.vmem.
Кто уже пробовал допиливать этот сурс под наложение оверлея поверх игры, есть какие-то просадки по фреймам?
Техническая база:
В основе лежит работа с M-драйвом, который поднимает MemProcFS. Скрипт подтягивает базовый адрес через vads или memmap и долбится в memory.vmem.
Код:
//
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <cmath>
#include <Windows.h>
using namespace std;
// OFFSETS - April 14, 2026
#define OFF_ENTITY_LIST 0x612c5e8
#define OFF_LOCAL_PLAYER 0x2563888
#define OFF_HEALTH 0x324
#define OFF_MAX_HEALTH 0x468
#define OFF_SHIELD 0x1a0
#define OFF_MAX_SHIELD 0x1a4
#define OFF_POSITION 0x17c
#define OFF_TEAM 0x33c
#define OFF_LIFE_STATE 0x690
string g_ProcFolder;
string g_MemPath;
HANDLE g_MemFile = INVALID_HANDLE_VALUE;
uint64_t g_Base = 0;
// ============================================================================
// READ FROM MEMORY FILE
// ============================================================================
template<typename T>
T Read(uint64_t addr) {
T val = {};
if (g_MemFile == INVALID_HANDLE_VALUE || addr < 0x10000) return val;
LARGE_INTEGER pos;
pos.QuadPart = (LONGLONG)addr;
if (SetFilePointerEx(g_MemFile, pos, NULL, FILE_BEGIN)) {
DWORD bytesRead = 0;
ReadFile(g_MemFile, &val, sizeof(T), &bytesRead, NULL);
}
return val;
}
bool IsValid(uint64_t p) { return p > 0x10000 && p < 0x7FFFFFFFFFFF; }
// ============================================================================
// READ TEXT FILE
// ============================================================================
string ReadTextFile(const string& path) {
ifstream f(path);
if (!f.is_open()) return "";
string content;
getline(f, content);
f.close();
return content;
}
// ============================================================================
// FIND BASE ADDRESS
// ============================================================================
uint64_t FindBaseAddress(const string& procFolder) {
cout << "\n[*] Searching for base address..." << endl;
// Method 1: Check modules folder
string modulesPath = procFolder + "\\modules";
cout << "[*] Checking: " << modulesPath << endl;
WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA((modulesPath + "\\*").c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE) {
cout << "[*] Modules found:" << endl;
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string modName = fd.cFileName;
if (modName != "." && modName != "..") {
cout << " - " << modName << endl;
// Check if it's r5apex
if (modName.find("r5apex") != string::npos) {
string basePath = modulesPath + "\\" + modName + "\\base.txt";
string baseStr = ReadTextFile(basePath);
if (baseStr.empty()) {
// Try without .txt
basePath = modulesPath + "\\" + modName + "\\base";
baseStr = ReadTextFile(basePath);
}
if (!baseStr.empty()) {
uint64_t base = strtoull(baseStr.c_str(), nullptr, 16);
if (base > 0) {
cout << "[+] Found base in " << modName << ": 0x" << hex << base << dec << endl;
return base;
}
}
}
}
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
// Method 2: Read from map file (memmap)
cout << "[*] Trying memmap..." << endl;
string memmapPath = procFolder + "\\memmap\\memmap.txt";
ifstream mapFile(memmapPath);
if (mapFile.is_open()) {
string line;
while (getline(mapFile, line)) {
if (line.find("r5apex") != string::npos && line.find(".exe") != string::npos) {
// Parse: "00007FF600000000-00007FF600001000 ... r5apex_dx12.exe"
size_t dash = line.find('-');
if (dash != string::npos) {
string addrStr = line.substr(0, dash);
uint64_t base = strtoull(addrStr.c_str(), nullptr, 16);
if (base > 0x10000) {
cout << "[+] Found base in memmap: 0x" << hex << base << dec << endl;
mapFile.close();
return base;
}
}
}
}
mapFile.close();
}
// Method 3: Read from vads
cout << "[*] Trying vads..." << endl;
WIN32_FIND_DATAA vfd;
HANDLE vFind = FindFirstFileA((procFolder + "\\files\\vads\\*.txt").c_str(), &vfd);
if (vFind != INVALID_HANDLE_VALUE) {
do {
string vadName = vfd.cFileName;
if (vadName.find("r5apex") != string::npos) {
// Filename format: 00007FF600000000-r5apex_dx12.exe.txt
size_t dash = vadName.find('-');
if (dash != string::npos) {
string addrStr = vadName.substr(0, dash);
uint64_t base = strtoull(addrStr.c_str(), nullptr, 16);
if (base > 0x10000) {
cout << "[+] Found base in vads: 0x" << hex << base << dec << endl;
FindClose(vFind);
return base;
}
}
}
} while (FindNextFileA(vFind, &vfd));
FindClose(vFind);
}
return 0;
}
// ============================================================================
// STRUCTURES
// ============================================================================
struct Vec3 {
float x, y, z;
float Dist(const Vec3& o) const {
float dx = x - o.x, dy = y - o.y, dz = z - o.z;
return sqrtf(dx*dx + dy*dy + dz*dz) / 39.62f;
}
};
// ============================================================================
// MAIN
// ============================================================================
int main() {
cout << "=== APEX RADAR v2 ===" << endl << endl;
// Find Apex process folder
cout << "[*] Searching M:\\name\\ ..." << endl;
WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA("M:\\name\\r5apex*", &fd);
if (hFind == INVALID_HANDLE_VALUE) {
cout << "[!] Apex not found in M:\\name\\" << endl;
cout << "[!] Error code: " << GetLastError() << endl;
// Check if M: exists
if (GetFileAttributesA("M:\\") == INVALID_FILE_ATTRIBUTES) {
cout << "[!] M: drive does not exist!" << endl;
cout << "[!] Make sure MemProcFS is running" << endl;
}
system("pause");
return 1;
}
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
g_ProcFolder = string("M:\\name\\") + fd.cFileName;
cout << "[+] Found: " << fd.cFileName << endl;
break;
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
// Find base address
g_Base = FindBaseAddress(g_ProcFolder);
if (g_Base == 0) {
cout << "\n[!] Could not auto-detect base address" << endl;
cout << "[*] Enter base address manually (from MemProcFS):" << endl;
cout << "[*] Look in M:\\name\\r5apex...\\modules\\ for the base" << endl;
cout << "\nEnter base (hex, e.g. 7FF600000000): ";
string input;
cin >> input;
g_Base = strtoull(input.c_str(), nullptr, 16);
cin.ignore();
}
if (g_Base == 0) {
cout << "[!] Invalid base address" << endl;
system("pause");
return 1;
}
cout << "\n[+] Using base: 0x" << hex << g_Base << dec << endl;
// Open memory file
g_MemPath = g_ProcFolder + "\\memory.vmem";
cout << "[*] Opening: " << g_MemPath << endl;
g_MemFile = CreateFileA(
g_MemPath.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (g_MemFile == INVALID_HANDLE_VALUE) {
cout << "[!] Cannot open memory.vmem" << endl;
cout << "[!] Error: " << GetLastError() << endl;
system("pause");
return 1;
}
cout << "[+] Memory file opened!" << endl;
// Test offsets
cout << "\n=== TESTING ===" << endl;
uint64_t localPlayer = Read<uint64_t>(g_Base + OFF_LOCAL_PLAYER);
cout << "LocalPlayer ptr: 0x" << hex << localPlayer << dec << endl;
if (IsValid(localPlayer)) {
Vec3 pos = Read<Vec3>(localPlayer + OFF_POSITION);
int hp = Read<int>(localPlayer + OFF_HEALTH);
int shield = Read<int>(localPlayer + OFF_SHIELD);
cout << "Position: " << pos.x << ", " << pos.y << ", " << pos.z << endl;
cout << "Health: " << hp << " | Shield: " << shield << endl;
if (hp > 0 && hp <= 150) {
cout << "\n[+] SUCCESS! Offsets are working!" << endl;
} else {
cout << "\n[?] Health value unusual - may need to update offsets" << endl;
}
} else {
cout << "[!] LocalPlayer is NULL - make sure you're in a match!" << endl;
}
cout << "\nPress Enter to start radar (or Ctrl+C to exit)...";
cin.get();
// Radar loop
while (true) {
system("cls");
uint64_t lp = Read<uint64_t>(g_Base + OFF_LOCAL_PLAYER);
if (!IsValid(lp)) {
cout << "Waiting for match..." << endl;
Sleep(1000);
continue;
}
Vec3 myPos = Read<Vec3>(lp + OFF_POSITION);
int myTeam = Read<int>(lp + OFF_TEAM);
int myHp = Read<int>(lp + OFF_HEALTH);
int myShield = Read<int>(lp + OFF_SHIELD);
cout << "=== APEX RADAR ===" << endl;
cout << "HP: " << myHp << " | Shield: " << myShield << " | Team: " << myTeam << endl;
cout << "Pos: " << (int)myPos.x << ", " << (int)myPos.y << endl;
cout << "==================" << endl;
int enemies = 0;
for (int i = 0; i < 70; i++) {
uint64_t ent = Read<uint64_t>(g_Base + OFF_ENTITY_LIST + ((uint64_t)(i & 0xFFFF) << 5));
if (!IsValid(ent) || ent == lp) continue;
int maxHp = Read<int>(ent + OFF_MAX_HEALTH);
if (maxHp < 100 || maxHp > 150) continue;
int hp = Read<int>(ent + OFF_HEALTH);
int team = Read<int>(ent + OFF_TEAM);
int life = Read<int>(ent + OFF_LIFE_STATE);
if (life != 0 || hp <= 0) continue; // Dead
if (team == myTeam) continue; // Teammate
Vec3 pos = Read<Vec3>(ent + OFF_POSITION);
int shield = Read<int>(ent + OFF_SHIELD);
float dist = myPos.Dist(pos);
printf("[ENEMY] HP: %d+%d | Dist: %.0fm\n", hp, shield, dist);
enemies++;
}
if (enemies == 0) {
cout << "No enemies found" << endl;
}
cout << "\nTotal enemies: " << enemies << endl;
Sleep(150);
}
CloseHandle(g_MemFile);
return 0;
}
Нюансы реализации:
- Скрипт требует запущенного MemProcFS, который мапит память в папку M:\.
- Функция поиска базы (FindBaseAddress) имеет три метода, если не подхватило автоматом — чекайте модули в папке процесса вручную.
- Логика чтения (Read<T>) максимально примитивная — через SetFilePointerEx, что для радара более чем достаточно.
- Расчет дистанции идет с учетом множителя 39.62f, не забудьте поправить под актуальные тики, если прыгает.
По поводу работы с нейросетками, о которых спрашивали в источнике — не советую городить огород на пустом месте. Для обычного радара достаточно простого парсинга структур. Если лезете в DMA, то сразу помните про прошивку карты и кастомный софт, иначе отлететь по железу — вопрос времени.
Код рабочий, тестил на актуальной версии игры. Главное — убедитесь, что правильно выставили путь к M-диску и у вас есть права на чтение memory.vmem.
Кто уже пробовал допиливать этот сурс под наложение оверлея поверх игры, есть какие-то просадки по фреймам?