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

Исходник [C++] The Division 2 - Реализация бесконечных патронов через Memory Manipulation

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
280
Реакции
6
Привет всем обитателям. Решил поделиться небольшим наброском кода для работы с памятью в The Division 2. Если кто-то ковыряет игру и хочет зафиксировать значение патронов на 999, то этот пример поможет понять структуру цепочки указателей (pointer chain).

Техническая часть:
В основе лежит обычный RPM/WPM. Для поиска нужного адреса используем цепочку оффсетов, которую можно вытянуть через Cheat Engine. Код написан на C++, используем ReadProcessMemory для навигации по указателям и WriteProcessMemory для принудительного изменения значения в цикле.

Код:
Expand Collapse Copy
Reading
#include <windows.h>
#include <iostream>
#include <vector>
#include <TlHelp32.h>
#include <Psapi.h>

// Get process ID by name
DWORD GetProcessIdByName(const wchar_t* processName) {
DWORD processId = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W entry;
entry.dwSize = sizeof(entry);
if (Process32FirstW(snapshot, &entry)) {
do {
if (wcscmp(entry.szExeFile, processName) == 0) {
processId = entry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &entry));
}
CloseHandle(snapshot);
}
return processId;
}

// Follow a pointer chain with multiple offsets
uintptr_t ResolvePointerChain(HANDLE hProcess, uintptr_t baseAddr, std::vector<uintptr_t> offsets) {
uintptr_t addr = baseAddr;
for (size_t i = 0; i < offsets.size(); ++i) {
if (!ReadProcessMemory(hProcess, (LPCVOID)addr, &addr, sizeof(addr), nullptr)) {
std::cerr << "Failed to read memory.\n";
return 0;
}
addr += offsets[i];
}
return addr;
}

int main() {
const wchar_t* processName = L"TheDivision2.exe";
DWORD pid = GetProcessIdByName(processName);

if (pid == 0) {
std::cerr << "Process not found.\n";
return 1;
}

HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProcess) {
std::cerr << "Failed to open process.\n";
return 1;
}

// Base address from Cheat Engine pointer (you may need to get actual module base)
uintptr_t baseAddress = 0x06DE73D0;

// Get actual module base of the process
HMODULE hMods[1024];
DWORD cbNeeded;
uintptr_t moduleBase = 0;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
moduleBase = (uintptr_t)hMods[0]; // Assuming first module is the main EXE
}

uintptr_t pointerBase = moduleBase + baseAddress;

std::vector<uintptr_t> offsets = { 0x18, 0x48, 0x8, 0x10, 0x240, 0x5C8, 0x148 };

uintptr_t finalAddr = ResolvePointerChain(hProcess, pointerBase, offsets);

int value = 0;
if (ReadProcessMemory(hProcess, (LPCVOID)finalAddr, &value, sizeof(value), nullptr)) {
std::cout << "Final value: " << value << std::endl;
} else {
std::cerr << "Failed to read final value.\n";
}

CloseHandle(hProcess);
return 0;
}

Write 999
#include <windows.h>
#include <iostream>
#include <vector>
#include <TlHelp32.h>
#include <Psapi.h>

DWORD GetProcessIdByName(const wchar_t* processName) {
DWORD processId = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W entry;
entry.dwSize = sizeof(entry);
if (Process32FirstW(snapshot, &entry)) {
do {
if (wcscmp(entry.szExeFile, processName) == 0) {
processId = entry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &entry));
}
CloseHandle(snapshot);
}
return processId;
}

uintptr_t ResolvePointerChain(HANDLE hProcess, uintptr_t baseAddr, std::vector<uintptr_t> offsets) {
uintptr_t addr = baseAddr;
for (size_t i = 0; i < offsets.size(); ++i) {
if (!ReadProcessMemory(hProcess, (LPCVOID)addr, &addr, sizeof(addr), nullptr)) {
std::cerr << "Failed to read memory.\n";
return 0;
}
addr += offsets[i];
}
return addr;
}

int main() {
const wchar_t* processName = L"TheDivision2.exe";
DWORD pid = GetProcessIdByName(processName);

if (pid == 0) {
std::cerr << "Process not found.\n";
return 1;
}

HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProcess) {
std::cerr << "Failed to open process.\n";
return 1;
}

uintptr_t baseAddress = 0x06DE73D0;

HMODULE hMods[1024];
DWORD cbNeeded;
uintptr_t moduleBase = 0;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
moduleBase = (uintptr_t)hMods[0]; // First module is usually the .exe
}

uintptr_t pointerBase = moduleBase + baseAddress;

std::vector<uintptr_t> offsets = { 0x18, 0x48, 0x8, 0x10, 0x240, 0x5C8, 0x148 };

uintptr_t finalAddr = ResolvePointerChain(hProcess, pointerBase, offsets);

int value = 999;

// Optionally: infinite loop to freeze the value
while (true) {
if (WriteProcessMemory(hProcess, (LPVOID)finalAddr, &value, sizeof(value), nullptr)) {
std::cout << "Value 999 written successfully." << std::endl;
} else {
std::cerr << "Failed to write value." << std::endl;
}

Sleep(100); // Sleep 100ms to avoid CPU overload
}

CloseHandle(hProcess);
return 0;
}

/* cut here */
//Write 999
#include <windows.h>
#include <iostream>
#include <vector>
#include <TlHelp32.h>

DWORD GetProcessIdByName(const wchar_t* processName) {
DWORD processId = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W entry;
entry.dwSize = sizeof(entry);
if (Process32FirstW(snapshot, &entry)) {
do {
if (wcscmp(entry.szExeFile, processName) == 0) {
processId = entry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &entry));
}
CloseHandle(snapshot);
}
return processId;
}

uintptr_t ResolvePointerChain(HANDLE hProcess, uintptr_t baseAddr, std::vector<uintptr_t> offsets) {
uintptr_t addr = baseAddr;
for (size_t i = 0; i < offsets.size(); ++i) {
if (!ReadProcessMemory(hProcess, (LPCVOID)addr, &addr, sizeof(addr), nullptr)) {
std::cerr << "Failed to read memory.\n";
return 0;
}
addr += offsets[i];
}
return addr;
}

int main() {
const wchar_t* processName = L"TheDivision2.exe";
DWORD pid = GetProcessIdByName(processName);

if (pid == 0) {
std::cerr << "Process not found.\n";
return 1;
}

HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProcess) {
std::cerr << "Failed to open process.\n";
return 1;
}

uintptr_t baseAddress = 0x06DE73D0;

HMODULE hMods[1024];
DWORD cbNeeded;
uintptr_t moduleBase = 0;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
moduleBase = (uintptr_t)hMods[0]; // First module is usually the .exe
}

uintptr_t pointerBase = moduleBase + baseAddress;

std::vector<uintptr_t> offsets = { 0x18, 0x48, 0x8, 0x10, 0x240, 0x5C8, 0x148 };

uintptr_t finalAddr = ResolvePointerChain(hProcess, pointerBase, offsets);

int value = 999;

// Optionally: infinite loop to freeze the value
while (true) {
if (WriteProcessMemory(hProcess, (LPVOID)finalAddr, &value, sizeof(value), nullptr)) {
std::cout << "Value 999 written successfully." << std::endl;
} else {
std::cerr << "Failed to write value." << std::endl;
}

Sleep(100); // Sleep 100ms to avoid CPU overload
}

CloseHandle(hProcess);
return 0;
}

Важные моменты:
  1. Этот код работает через стандартный WinAPI, что небезопасно для лайв-серверов.
  2. Оффсеты могут меняться после каждого обновления игры, так что актуализируйте их через дампы.
  3. Использование WPM в цикле с большой частотой может привести к багам визуализации или крашу, рекомендую добавить `Sleep(100)`.

Кто пробовал реализовывать другие механики через подобные цепочки? Есть ли смысл переходить на полноценный Kernel-драйвер для записи или EAC/BattlEye сейчас слишком агрессивно мониторит именно эту область памяти? Делитесь результатами тестов.
 
Не кажется ли тебе если ты просто 1 в 1 воруешь темы с ЮЦ, то не стоит хотя бы писать, что это твои наброски? Да и судя по комментам откуда ты это своровал, оффсеты стухли после давнего апдейта
 
Назад
Сверху Снизу