Делаем чит на КС ГО

Привет, сегодня мы создадим наш первый External-чит для ксго. Создавать мы будем вх, так как вх — идеален, чтобы рассматривать его как основу. Я постараюсь расписать все максимально понятно и по шагам, чтобы вы поняли как что работает, а не просто спастили код и ничему не научились.
Итак, приступим.
1. Создаем простой C++ проект в Visual Studio.
2. Создаем файл main.cpp, здесь и будет весь наш код.
3. Пишем в начало файла следующий код:

#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <thread>

здесь мы подключили нужные библиотеки для работы, если у вас какой-то нет, то скачайте или подключите через параметры проекта (можете посмотреть в интернете уроки, если не знаете, как это делать, их полно).
4. Теперь самое время сделать переменные с оффсетами. Почему именно переменные? Да потому что их легко обновлять, и не нужно лазить по всему коду и вспоминать, какой оффсет для чего и где.

const DWORD dwLocalPlayer = 0xCBD6A4;
const DWORD dwEntityList = 0x4CCDCBC;
const DWORD m_iTeamNum = 0xF4;
const DWORD m_iGlowIndex = 0xA3F8;
const DWORD dwGlowObjectManager = 0x520DAE0;

разберем каждое ключевое слово в коде:
const — мы делаем именно константы (не переменные), чтобы им нельзя было присвоить другие значения, кроме тех, которые мы уже задали.
DWORD — класс. Переменные имеют тип экземляра именно класса DWORD.
5. Сейчас мы определим переменные, с которыми мы будем работать на протяжении всего написания чита.

HANDLE process; // непосредственно сам процесс CSGO
DWORD clientBase; // это короче для работы с client_panorama.dll
DWORD engineBase; // это короче для работы с engine.dll

Мы объявили необходимые переменные, комментарии, обозначающие «что зачем и почему» я написал в самом коде.
6. Что же, теперь приступим к написанию нашего чита непосредственно.
Первое, что нам нужно — это возможность включать или выключать те или иные функции (в нашем случае — вх).
Если вы подумали о переменной типа boolean, определяющей статус функции — вы правы.

ool wh = false;

Мы объявили переменную, которая определяет статус функции, по умолчание вх выключено, значит значение переменной false.
7. Так как мы создаем External-чит, нам нужно читать память и изменять ее.
Добавим следующий код, необходимый нашему читу. Что и зачем расскажу по ходу делал.

DWORD getModuleBaseAddress(DWORD pid, const char* name)
{
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
    MODULEENTRY32 mEntry;
    mEntry.dwSize = sizeof(MODULEENTRY32);
    do
    {
        if (!strcmp(mEntry.szModule, name))
        {
            CloseHandle(snapshot);
            return (DWORD)mEntry.modBaseAddr;
        }
    } while (Module32Next(snapshot, &mEntry));
}

данный код находит в нашей оперативной памяти именно то приложение, которое нам нужно (по его id — pid, который передается функции).

template <typename T>
T readMem(DWORD address)
{
    T buffer;
    ReadProcessMemory(process, (LPVOID)address, &buffer, sizeof(buffer), 0);
    return buffer;
}

template <typename T>
void writeMem(DWORD address, T value)
{
    WriteProcessMemory(process, (LPVOID)address, &value, sizeof(value), 0);
}

эти стандартные функции читают и пишут память, именно с помощью них мы будем взаимодействовать с игрой.
8. Теперь создадим входную точку, без которой не будет работать ни одно приложение C++. Это int main().

 HWND hwnd;
 
        do {
            hwnd = FindWindowA(0, "Counter-Strike: Global Offensive"); // ищем ксго, если находим - выходим из цикла
            Sleep(50); // таймаут (чтобы не грузить процессор)
        } while (!hwnd);

        DWORD pid;
        GetWindowThreadProcessId(hwnd, &pid); // получаем id приложения
        process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // заходим в кс го его id

        std::cout << "Csgo started, pid " << pid << ".\n"; // выводим сообщение о том, что ксго запущена

        do {
            clientBase = getModuleBaseAddress(pid, "client_panorama.dll"); // ищем клиент кс го
            Sleep(50);
        } while (!clientBase);

        do {
            engineBase = getModuleBaseAddress(pid, "engine.dll"); // ищем движок кс го
            Sleep(50);
        } while (!engineBase);

}

весь код с комментариями, его нам еще придется обновлять. Что мы в нем сделали? Мы нашли ксго, интегрировали чит с игрой, получили необходимые базы (клиент и движок).
9. Теперь пора создать сам вх. Создадим метод (пусть будет wallhack())

void wallhack()
{
    while (true) // создаем бесконечный цикл
    {
        Sleep(10); // таймаут 10 мс, чтобы не грузить процессор под 100
        if (!wallhack && !readMem<DWORD>(readMem<DWORD>(clientBase + dwLocalPlayer) + 0xED)) // если вх выключено или не удается прочитать память - выходим из цикла
            continue;

        DWORD glowObj = readMem<DWORD>(clientBase + dwGlowObjectManager); // создаем объект glowObj из модельки игрока
        DWORD myTeam = readMem<DWORD>(readMem<DWORD>(clientBase + dwLocalPlayer) + m_iTeamNum); // создаем объект тиммейтов

        for (int x = 0; x < 32; x++) // сам вх
        {
            DWORD player = readMem<DWORD>(clientBase + dwEntityList + x * 0x10); // обычный игрок
            if (player == 0)
                continue;

            bool dormant = readMem<bool>(player + 0xED); // спектатор
            if (dormant)
                continue;

            DWORD team = readMem<DWORD>(player + m_iTeamNum); // тиммейт
            if (team != 2 && team != 3)
                continue;

            DWORD currentGlowIndex = readMem<DWORD>(player + m_iGlowIndex); // текущий индекс игрока

            if (team != myTeam) // если игрок не тиммейт
            {
                // делаем его обводку красным
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x4, 255); // red
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x8, 0); // green
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0xC, 0); // blue
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x10, 255);
                writeMem<bool>(glowObj + currentGlowIndex * 0x38 + 0x24, true);
                writeMem<bool>(glowObj + currentGlowIndex * 0x38 + 0x25, false);
            }
            else // если игрок тиммейт
            {
                // делаем его обводку синим
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x4, 0); // red
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x8, 0); // green
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0xC, 255); // blue
                writeMem<float>(glowObj + currentGlowIndex * 0x38 + 0x10, 255);
                writeMem<bool>(glowObj + currentGlowIndex * 0x38 + 0x24, true);
                writeMem<bool>(glowObj + currentGlowIndex * 0x38 + 0x25, false);
            }
        }
    }
}

10. Теперь нам нужно где-то вызывать наш метод. Конечно же в main()!
Теперь вызовем наш метод:
std::thread whThread(wallhack)
И создадим в нашем главном методе бесконечный цикл.

while(true)
{

    if (GetAsyncKeyState(VK_F9)) // если нажали f9
    {
 
        wh = !wh; // заменяем значение переменной на противоположное
        if (wh)
            std::cout("wh: on\n"); // если wallhack - true, то пишем, что вх включен
        else
            std::cout("wh: off\n"); // иначе пишем, что вх выключен
         
        Sleep(100); // таймаут, чтобы сбросить нагрузку
 
    }

}

Вроде все, спасибо за внимание

До скачивания осталось сек.