Всем привет, в этой статье было такое упоминание, как "Защищенные указатели", или что-то таке...
Мне стало интересно почитать, что это такое.
Вот что раскопал:
Что такое защищенные регионы?
Защищенные регионы — это, по сути, указатели, которые указывают на адрес, который не действителен. Чаще всего адрес начинается с 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;
}
	
	