Всем привет, в этой статье было такое упоминание, как "Защищенные указатели", или что-то таке...
Мне стало интересно почитать, что это такое.
Вот что раскопал:
Что такое защищенные регионы?
Защищенные регионы — это, по сути, указатели, которые указывают на адрес, который не действителен. Чаще всего адрес начинается с 0x800000 и т.д. Однако игра их считывает, и они получают действительные результаты. Как, вы можете спросить? Дело в том, что stub.dll регистрирует обработчик исключений, который перенаправляет охраняемый указатель на реальный указатель после успешного выполнения нескольких проверок. И чтобы выполнить все проверки, мы собираемся найти заглушку, которую мы собираемся использовать для чтения адреса, который мы хотим прочитать.
Какие указатели используют защищенные регионы в играх ? Вот некоторые из них:
- Мир
- Корневой компонент актера
- Здоровье актера
- Контроллер игрока
- Постоянный уровень
- Экземпляр игры
Чтобы прочитать охраняемый адрес, вам нужно будет читать из игры, потому что их обработчик исключений перенаправит вас на реальный адрес.
Вот примерный код с комментариями, как это можно сделать:
C:
#include <windows.h>
#include <iostream>
// Предположим, что External::HANDLE и External::Base уже определены
// и инициализированы в другом месте программы.
uint64_t FindPattern(uint64_t startAddress, const char* pattern, size_t length, char wildcard) {
// Реализация функции поиска паттерна в памяти процесса.
// Должна возвращать адрес найденного паттерна или 0, если паттерн не найден.
// Данная функция должна найти заглушку, которую мы собираемся использовать для чтения адреса, который мы хотим прочитать.
// Реализуйте функцию сами.)
return 0; // Заглушка для примера.
}
uint64_t ReadAddressFromGame(uint64_t AddressToRead) {
static uint64_t RandomPageLocation = 0x10000; // Начальный адрес для поиска
if (!RandomPageLocation) {
MEMORY_BASIC_INFORMATION Info;
while (VirtualQueryEx(External::HANDLE, (void*)RandomPageLocation, &Info, sizeof(Info))) {
RandomPageLocation = (uint64_t)Info.BaseAddress + Info.RegionSize;
if (Info.State == MEM_COMMIT && Info.Protect == PAGE_EXECUTE_READWRITE) {
chunk_t A;
if (ReadProcessMemory(External::HANDLE, (void*)RandomPageLocation, &A, sizeof(A), nullptr)) {
bool Good = true;
for (size_t i = 0; i < sizeof(A.Buff); ++i) {
if (A.Buff[i] != 0 && A.Buff[i] != 0xCC) {
Good = false;
break;
}
}
if (Good) break;
}
}
}
}
static uint64_t MemeStub = 0;
if (!MemeStub) MemeStub = FindPattern(0, "\x48\x8B\x01\xC3", 4, 0xCC);
if (!MemeStub) return 0;
// Вместо 0xDEADBEEF нужно получить адрес
// Это указатель функции .data, который вызывается каждый кадр.
uint64_t DataPtr = External::Base + 0xDEADBEEF;
static uint64_t OrigAddr = 0;
if (!OrigAddr) ReadProcessMemory(External::HANDLE, (void*)DataPtr, &OrigAddr, sizeof(OrigAddr), 0);
unsigned char ReadStub[] = {
// Ассемблерные инструкции для чтения памяти...
0x51, // push rcx
0x52, // push rdx
0x48, 0xB9, // mov rcx, AddressToRead (перезаписывается ниже)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xD1, // call rcx (вызов MemeStub, перезаписывается ниже)
0x5A, // pop rdx
0x59, // pop rcx
0xC3 // ret
};
// Заполнение адресов в ReadStub
*(uint64_t*)&ReadStub[4] = AddressToRead; // Адрес для чтения
*(uint64_t*)&ReadStub[14] = MemeStub; // Адрес MemeStub
// Запись ReadStub в процесс
WriteProcessMemory(External::HANDLE, (void*)RandomPageLocation, &ReadStub, sizeof(ReadStub), nullptr);
// Изменение указателя на ReadStub в целевом процессе
WriteProcessMemory(External::HANDLE, (void*)DataPtr, &RandomPageLocation, sizeof(RandomPageLocation), nullptr);
// Чтение результата
uint64_t ReadBuff = 0;
while (true) {
Sleep(100); // Краткая пауза для обеспечения выполнения кода в процессе
if (ReadProcessMemory(External::HANDLE, (void*)(RandomPageLocation + sizeof(ReadStub)), &ReadBuff, sizeof(ReadBuff), nullptr)) {
if (ReadBuff != 0) break;
}
}
// Восстановление исходного адреса
WriteProcessMemory(External::HANDLE, (void*)DataPtr, &OrigAddr, sizeof(OrigAddr), nullptr);
// Очистка памяти
unsigned char NullBuff[sizeof(ReadStub)] = {0};
WriteProcessMemory(External::HANDLE, (void*)RandomPageLocation, &NullBuff, sizeof(NullBuff), nullptr);
return ReadBuff; // Возвращаем прочитанное значение
}
int main() {
uint64_t addressToRead = 0x0050F4; // Пример адреса для чтения
uint64_t readValue = ReadAddressFromGame(addressToRead);
std::cout << "Read value: " << readValue << std::endl;
return 0;
}
Этот код делает следующее:
- Инициализирует ReadStub с инструкциями для выполнения чтения из памяти.
- Записывает ReadStub в найденный codecave.
- Изменяет указатель функции в целевом процессе на ReadStub.
- Читает значение из указанного адреса.
- Восстанавливает исходный указатель функции.
- Очищает использованную память в codecave.
Что такое External::HANDLE и External::Base и где их можно получить ?
Пример получения HANDLE процесса:
C:
// Функция для получения HANDLE по имени процесса
HANDLE GetProcessHandle(const wchar_t* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return NULL;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &pe)) {
do {
if (wcscmp(pe.szExeFile, processName) == 0) {
CloseHandle(hSnapshot);
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return NULL;
}
External::Base относится к базовому адресу загружаемого модуля (обычно исполняемого файла процесса) в адресном пространстве процесса. Этот адрес используется как отправная точка для чтения или записи определенных данных в памяти процесса.
Пример получения базового адреса модуля:
C:
#include <psapi.h>
DWORD_PTR GetModuleBaseAddress(DWORD dwProcessId, const wchar_t* moduleName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwProcessId);
if (hSnapshot != INVALID_HANDLE_VALUE) {
MODULEENTRY32 ModuleEntry32 = {0};
ModuleEntry32.dwSize = sizeof(MODULEENTRY32);
if (Module32First(hSnapshot, &ModuleEntry32)) {
do {
if (wcscmp(ModuleEntry32.szModule, moduleName) == 0) {
CloseHandle(hSnapshot);
return (DWORD_PTR)ModuleEntry32.modBaseAddr;
}
} while (Module32Next(hSnapshot, &ModuleEntry32));
}
CloseHandle(hSnapshot);
}
return 0;
}
Пример использования:
C:
int main() {
const wchar_t* processName = L"valorant.exe"; // Имя процесса
HANDLE processHandle = GetProcessHandle(processName);
if (processHandle != NULL) {
DWORD processId = GetProcessId(processHandle); // Получаем PID для дальнейшего использования
DWORD_PTR baseAddress = GetModuleBaseAddress(processId, processName);
std::wcout << L"Handle: " << processHandle << L", Base Address: " << std::hex << baseAddress << std::endl;
} else {
std::wcout << L"Process not found." << std::endl;
}
// Дальнейшие действия...
return 0;
}
Последнее редактирование: