• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья [0x5]Легкий Геймхакинг: Шаг за шагом.

Unseen

(L3) cache
Пользователь
Регистрация
17.01.2022
Сообщения
262
Реакции
246
Author: Unseen
Source: xss.pro

Предыдущая часть:
https://xss.pro/threads/105956/

Вступление…
Доброго времени суток, дорогие обитатели этого форума! Сидел значит и думал, какую тему разобрать в этой статье, и пришел к выводу, что было бы лучше дополнить наши знания, полученные в части №3 https://xss.pro/threads/104820/ , возможно у вас возникнет вопрос, почему это не сделал в части №4, ответ узнаете далее. А вам пожелаю хорошего чтива!

Предыстория…
Как вы помните в части №3 мы разбирали на иллюстрациях, кратко что из себя представляет:
  • модуль игры,
  • виртуальная память,
  • почему меняются адреса,
  • базово разобрались с динамическим выделением памяти(создание объектов во время игры),
  • как они размещаются в выделенной для них памяти(Случайное размещение),
  • разобрали понятие «Указатель» и зачем его используют,
  • Что из себя представляет структура игры(ООП),
  • И смещения(*Оффсеты)
И под конец научились находить эти самые смещения в структурах, чтобы через указатели прийти к искомому адресу(Адрес здоровья, патронов …). Написали софт, который делал вычисления при поиске адреса на основе полученного списка смещений(*оффсетов).

Новая техника…
Но! Все эти смещения мы находили средствами сканера Cheat Engine, а именно в автоматическом режиме. Сегодня я предлагаю разобрать метод ручного поиска, ведь в арсенале хорошего Геймхакера должны быть несколько методов и техник в решении проблемы. Причина по которой мы не разбирали это в той же части или в следующей банальна, а именно:

Во-первых, статья тогда получилась итак огромной. Объем был очень велик и сразу пытаться запихнуть в наш крохотный мозг столько информации за раз было бы не разумно!
Во-вторых, мы тогда не были так близко знакомы с языком ассемблера, как помните мы в прошлой части №4 попытались разобрать поверхностно что он из себя представляет. Этих полученных знаний будет пока достаточно для понимания этой части.

В этой части нам так же приходит на помощь Cheat Engine. Мы так же возьмем для эксперимента игру Assault Cube, потому что это самое простое на чем можно будет попытаться объяснить тему статьи, она весит немного и установка не занимает времени, чтобы каждый из вас смог это испытать! Просто поймите, что нам пока нужно разобрать все базовые моменты и нюансы взлома игр на чем то более простом, только после этого мы сможем сложить все кусочки полученных знаний во что-то единое и в итоге придем к общему понимаю картины! Вот тогда уже нам будет не сложно разбирать такие игры как CS2;CS:GO,Rust и другие популярные игры!

Практическая часть…
Итак для начала запустите нашу игру AssaultCube и подключитесь к нему сканером CheatEngine. Я специально разместил окно игры и сканера рядом с друг другом, для более комфортной работы и вам советую так же!
1.png

Первое, что нам необходимо это создать одиночную игру(SinglePlayer). Потому что начальная сцена игры это демо-режим, и наши найденные оффсеты возможно будут отличаться от настоящей игры. Итак создаете значит одиночную игру и выбираете любую карту.

Далее, нам нужно с чего то начать сканирование адресов. Давайте возьмем для этого наше здоровье. Как в прошлых частях находим адрес здоровья(Надеюсь не забыли, как сканировать?). Итак, просканировав я нашел адрес здоровья. В моем случае 008E04A4.
2.png

Давайте проверим его, действительно ли он является нашим адресом здоровья или же адресом, который используется в графическом интерфейсе для отображения количества здоровья. Попробуйте изменить его на какое нибудь N-ое число и нанести себе урон, например подорвать себя.
3.png
И да это наш реальный адрес в моем случае.

Итак, чтобы найти так скажем объект сущности(еще называют PlayerEntity) в данном случае игрока или еще можно сказать структуру игрока(как в части 3) и в нем скорее всего будет находится как раз переменная здоровья. Потому что она возможно так определена в классе Player или LocalPlayer. Мы не можем на все 100% предполагать, что это так и есть, а лишь можем догадываться и строить гипотезу. В геймхакинге многое получается только путем тыка, проб и ошибок. Со временем этот навык оттачивается и ваши гипотезы будут верны в большинстве случаев =)

Если более детальнее, то это выглядит примерно так:

У нас есть какой-то класс Player. И в игре все сущности будут наследованы от него. Допустим у нашего игрока есть имя, количество здоровья, патроны и другие характеристики. И чтобы все это не объявлять для каждого похожего объекта в игре придумали наследование от базового класса. Вот те же боты будут унаследованы именно от этого класса, потому что игрок и боты имеют свои имена, оружие и схожие характеристики. А объект сущности в нашем случае это наш игрок. И поэтому возможно наша переменная здоровья будет находится внутри этого объекта сущности(PlayerEntity) или же можно сказать в экземпляре класса Player.

Но как процессор и ассемблерный код получают доступ к адресу здоровья? Он скорее знает, что есть некая функция с инструкцией, которая меняет количество здоровья. И это делается путем получения адреса объекта сущности игрока. А затем при помощи вычисления размеров полей в структуре доходит до поля с переменной здоровья, проще говоря при помощи оффсетов.

Если забыли эта та самая иллюстрация из 3 части. Как видно чтобы добраться от начала структуры Player до его поля health, нужно посчитать количество байт от char nickname[32] до самого health. То есть char nickname[32] будет иметь 32 байта и плюс int armor будет иметь 4 байта = 36 оффсет для health в десятичном виде.
7.png

Итак, для того чтобы вычислить адрес структуры сущности игрока нужно кликнуть по адресу здоровья ПКМ и выбрать пункт «Find out what accesses this address» или же «Что обращается к этому адресу». Cheat Engine напишет, что будет подключен отладчик к этому процессу, мы соглашаемся.
У нас откроется такое окошко:
4.png

Здесь мы попытаемся поймать те самые инструкции ассемблера, при помощи которых процессор получает доступ к нашему адресу здоровья через объект сущности(Entity). Как выделено на картинке выше, красным это сколько раз вызывались инструкции в нашем случае они вызываются постоянно, а зеленым выделены сами инструкции ассемблера. То есть вызов идет каждый такт процессора. Нам это не нужно и мы попробуем получить урон и посмотреть туда, может сможем вызвать инструкцию, которая получает доступ к здоровью игрока? Итак наносим себе урон:
5.png

И да! Как видим две инструкции вызвались один раз, когда мы получили урон! Как действовать дальше? Давайте рассуждать логично, мы получили две инструкции после получения урона, они же что-то по идеи делают? Да! Воспользовавшись базовыми командами ассемблера из предыдущей части попробуем прочитать инструкцию и понять это.

sub[ebx +04], esi Давайте разберемся, что происходит:
  • SUB: Это аббревиатура от "subtract" (вычитание). Эта инструкция используется для выполнения операции вычитания.
  • [EBX + 04]: Это адрес в памяти, который содержит значение, которое будет вычитано. EBX - это регистр базы, и +04 указывает смещение относительно содержимого регистра EBX. Таким образом, мы обращаемся к ячейке памяти, адрес которой равен значению в регистре EBX, увеличенному на 4 байта. [ ] Квадратные скобки говорят, что нужно взять значение из адреса, а не сам адрес, как вы могли подумать. Запомните это!
  • ESI: Это регистр, содержащий значение, которое будет вычитано из значения, находящегося по адресу [EBX + 04].
  • Таким образом, вся инструкция sub [ebx + 04], esi выполняет вычитание значения, находящегося в регистре esi, из значения, находящегося в памяти по адресу, который вычисляется как значение в регистре ebx, увеличенное на 4 байта.
6.png
Как видим инструкция sub[ebx+04], esi Если в отладчике посмотреть значение ebx, то оно равно 008E04A0 и прибавив к нему смещение 04 мы получаем 008E04A4, и если теперь посмотреть на адрес изначально, который нашли, то они будут идентичны! А регистр esi содержит количество урона, которое надо вычесть! Эта инструкция не совсем, что нам нужно.

Следующая инструкция cmp dword ptr [esi + 0xEC], 00 выполняет операцию сравнения. Рассмотрим, что происходит по частям:
  • CMP: Эта часть инструкции указывает на операцию сравнения.
  • DWORD PTR: Операнд указывает размер данных, с которыми будет произведено сравнение. В данном случае, DWORD PTR означает, что сравнение будет произведено с 32-битными данными (т.е., 4 байта).
  • [ESI + 0xEC]: Это адрес в памяти, к которому будет обращено сравнение. ESI - это регистр, содержащий адрес в памяти, а + 0xEC представляет собой смещение относительно содержимого регистра ESI. Таким образом, мы обращаемся к 32-битному значению, находящемуся по адресу ESI + 0xEC в памяти.
  • 00: Это значение, с которым будет произведено сравнение. В данном случае, сравнение будет выполнено с нулем.
  • Итак, инструкция cmp dword ptr [esi + 0xEC], 00 сравнивает значение, находящееся в памяти по адресу ESI + 0xEC, с нулем. Результат сравнения влияет на флаги процессора, которые могут использоваться в последующих условных переходах.
7.png

Как видим здесь инструкция cmp dword ptr [esi+000000EC],00 Если посмотрим в отладчике, то увидим, что значение регистра ESI = 008E03B8 и если прибавим смещение 0xEC, то так же получаем адрес количества здоровья и уже при помощи команды cmp идет сравнение ЗНАЧЕНИЯ по этому адресу с числом 0, скорее это идет проверка, жив ли игрок, чтобы выполнить следующую инструкцию по логике игры. Нас здесь интересует значение, которое хранится в регистре ESI, а именно адрес 008E03B8. Мы можем предположить, что это будет началом сущности игрока или еще структуры.
Вот хорошая иллюстрация, о чем идет речь:
8.png

P.S. Это всего лишь иллюстрация, настоящую картину увидите позже!

Если предположим, что 008E03B8 это адрес начала сущности игрока и прибавить к этому адресу смещение 0xEC, то придем как раз к адресу здоровья, который мы находили в самом начале - 008E04A4! Можете зайти в калькулятор и выбрать режим «Программист» и посчитать это в 16-ом формате для убедительности.
9.png
Давайте добавим этот адрес сущности в таблицу сканера, для этого нажимаем «Add Address Manually». У нас откроется окошко, вводим туда этот адрес и даем название понятное вам, допустим Entity Object. Что же такое объект? Объект это экземпляр класса, когда вы программируете вы создаете класс, который просто определяет, что будет содержать объект этого класса, какова его структура и какие функции он имеет. Поэтому я назвал его объектом сущности.
10.png

Теперь когда мы добавили адрес объекта сущности, то его значение будет равно значению первого поля в его структуре. Ну это для нас не особо играет важную роль сейчас.
Предлагаю при помощи инструмента для анализа структур встроенном в CheatEngine, посмотреть на структуру этого объекта. Скопируйте адрес Entity Object и откройте обозреватель памяти в сканере. Далее во вкладке «Tools» нажмите на «Dissect data/structures».
11.png

Далее откроется окошко, в нем указываем адрес Entity Object и нажимаем на вкладку «Structures» и в нем нажимаем на « Define new structure». Далее даем логичное название структуре, пусть будет PlayerEntity. Нажимаем Oк.
У нас далее открывается окошко:
14.png
Здесь можем увидеть все поля/переменные, которые есть в этом объекте Entity. Это все, те характеристики, которые имеет наш объект игрока. Координаты, имя, патроны, здоровье и т.д. Красным выделены смещения, относительно самого адреса объекта PlayerEntity. Теперь давайте попробуем спуститься до того поля, где будет наш оффсет 0xEC для здоровья игрока.
16.png

Как можем видеть, по смещению 0xEC(Красное выделение) относительно адреса 008E03B8 объекта PlayerEntity находиться наша переменная здоровья с адресом 008E04A4(который находили в начале) и значением 55306, как в игре. Мы можем заметить, что оффсетом для адреса патронов будет 0x140(зеленое выделение), а нашими запасами патронов будет оффсет 0x11C(Синее выделение). А если еще внимательнее пробежаться по всем полям объекта, можем заметить оффсет 0x205, а значением его будет наш никнейм в игре.
nick.png
Значит наше предположение верное и перед нами находится тот самый объект игрока! То есть это дает нам сразу много пользы при написании чита, а именно все оффсеты лежат в этой структуре и их не нужно будет вычислять для каждого поля отдельно! Достаточно будет найти эту структуру. Предлагаю для более лучшего понимания сделать эксперимент, давайте откроем главное окно CheatEngine и нажмем на «Add address Manually» и впишем туда адрес структуры PlayerEntity и знаком +(плюс) прибавим оффсет патронов 0x140.

Как это выглядит:
17.png
18.png

Как можем увидеть, мы вычислили адрес(008E04F8) магазина патронов для оружия-автомат, не думайте, что этот адрес будет хранить в себе значения патронов для всех оружий! Оно лишь для определенного оружия, а именно оружие автомат!

Следующим шагом я предлагаю найти статический указатель на наш объект PlayerEntity. Исходя от полученных знаний в части 3, мы знаем, что в играх многие вещи создаются динамически, чтобы эффективно расходовать память ОЗУ. А вот статический адрес всегда относится к модулю игры, ведь мы можем его получить при помощи запросов(WINAPI) к самой ОС.

Как нам известно, указатель это тоже некая переменная, которая хранит другой адрес, то есть указывает на другой адрес. Значит мы можем сделать сканирование адреса(008E03B8) объекта PlayerEntity и вычислить тот самый указатель, который будет хранить адрес нашего объекта!
19.png

Открываем главное окошко Cheat Engine и нажимаем на «New Scan», далее ставим галочку напротив «HEX», ведь будем искать 16-ое число, а в строку поиска вбиваем тот самый адрес Entity.
Нажимаем «First scan».
20.png

Слева можем увидеть просто переменные-указатели, которые хранят наш адрес PlayerEntity, но эти указатели являются динамическими. То что выделено зеленым это наши статические указатели. Давайте выделим их и перенесем в таблицу адресов нижней части сканера.
21.png
Если дважды щелкнем по одному из этих адресов, то откроется окошко:
22.png
То что выделено зеленым мы знаем, что это адрес расположенного модуля(ac_client.exe) в памяти и то что выделено красным это смещение относительно этого модуля, который в итоге приводит нас к структуре PlayerEntity. Если посмотрим на все три найденные статические адреса, то обнаружим, что все они одноуровневые указатели, которые указывают на объект PlayerEntity. Но чтобы проверить, что найденные указатели реально рабочие, мы можем закрыть игру и заново открыть, затем подключится к игре сканером. Только сначала перепишите найденные смещения в блокнот.

В итоге получается:
23.png
Я перезапустил игру и сканер. Нажал на «Add Address Manually» и вбил туда модуль игры(ac_client.exe) + оффсет 17E254 найденный ранее. В итоге мы получаем указатель, который будет хранить динамический адрес 0063ECF8 объекта PlayerEntity! И если прибавим наш оффсет для здоровья 0xEC, то получим сам динамический адрес здоровья! Попробуйте проделать тоже самое с патронами, для закрепления полученных знаний! Попробуйте внести изменения в программу MemoryManager, который мы переписывали в части №3, чтобы посмотреть на работоспособность этой техники, этих указателей и оффсетов! Если возникнут вопросы – смело пишите в комменты. А готовый исходный код программы MemoryManager и комментарии к нему скрою в спойлере, чтобы вы сначала попробовали сами переписать наш софт и смогли свериться со своим решением. Ведь без практики далеко не уедешь! Экспериментируйте и задавайте вопросы!
C++:
/*
    Author Unseen
    Source xss.pro
*/
//Стандартная библиотека для работы с потоками ввода и вывода С++
#include <iostream>

/*
специфичный заголовочный файл, в котором объявляются функции,
предоставляющие интерфейс доступа к Windows API
*/
#include <Windows.h>

/*
Заголовочный файл в Windows API, который предоставляет набор функций
для работы с информацией о процессах и потоках.
Он используется для выполнения операций, связанных с манипуляцией процессов и их состояний,
в том числе получения списка запущенных процессов и
информации о модулях в адресном пространстве процесса.
*/
#include <TlHelp32.h>

#include <vector>
#include <thread>
#include <chrono>

using namespace std;

/*Глобальные переменные, чтобы ссылать на них, где угодно в коде*/
HANDLE targetProc = NULL;
HWND targetWindow = NULL;
DWORD pID = 0;
DWORD baseModuleAddress = 0;

void GetAccessProcess(char* targetName)
{
    targetWindow = FindWindowA(0, targetName);
    if (targetWindow == NULL)
    {
        cout << "Не удалось найти окно игры!" << endl;
    }
    else
    {
        GetWindowThreadProcessId(targetWindow, &pID);
        if (pID == 0)
        {
            cout << "Не удалось получить идентификатор процесса!" << endl;
        }
        else
        {
            targetProc = OpenProcess(PROCESS_ALL_ACCESS, false, pID);

            if (!targetProc)
            {
                cout << "Не удалось открыть процесс!" << endl;
            }
     

        }
    }

}

void GetModuleBaseAddress(const wchar_t* moduleName)
{
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pID);
 

    if (hSnap != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 modEntry;
        modEntry.dwSize = sizeof(modEntry);
        if (Module32First(hSnap, &modEntry))
        {
            do
            {
                if (!_wcsicmp(modEntry.szModule, moduleName))
                {
                    baseModuleAddress = (uintptr_t)modEntry.modBaseAddr;
                    break;
                }
            } while (Module32Next(hSnap, &modEntry));
        }

    }
    CloseHandle(hSnap);
}

uintptr_t FindTargetAddress(vector<unsigned int>offsets)
{
    uintptr_t address = baseModuleAddress;
    for (unsigned int i = 0; i < offsets.size(); ++i)
    {
        ReadProcessMemory(targetProc, (BYTE*)address, &address, sizeof(address), 0);
        address += offsets[i];
    }
    return address;
}

template<class dataType>
void memWrite(DWORD address, dataType value)
{
    WriteProcessMemory(targetProc, (PVOID)address, &value, sizeof(dataType), 0);
}

template <class dataType>
dataType memRead(DWORD address)
{
    dataType buffer;
    ReadProcessMemory(targetProc, (PVOID)address, &buffer, sizeof(dataType), 0);
    return buffer;
}

int main() {
 
    setlocale(LC_ALL, "Russian");

    GetAccessProcess((char*)"AssaultCube");
    cout << "[+]ac_client.exe PID: " << pID << endl;

    GetModuleBaseAddress(L"ac_client.exe");
    cout << "[+]Адрес базового модуля ac_client.exe: "<< hex << baseModuleAddress << endl;

    int health_offset = 0xEC;
    int playerEntity_static_offset = 0x17E254;
    DWORD StaticPtrToPlayerEntity = baseModuleAddress + playerEntity_static_offset;
    cout << "[+]Статический указатель на объект PlayerEntity:" << hex << StaticPtrToPlayerEntity << endl;

    DWORD dynamicAddressPlayerEntity = memRead<DWORD>(StaticPtrToPlayerEntity);
    cout << "[+]Динамический адрес PlayerEntity(Начальный адрес структуры), хранящейся в указатели StaticPtrToPlayerEntity: " << hex << dynamicAddressPlayerEntity << endl;
 
    DWORD health_address = dynamicAddressPlayerEntity + health_offset;
    cout << "[+]Адрес здоровья: " << health_address << endl;
    cout << "[!]Нажмите на Enter для запуска цикла мониторинга значения здоровья" << endl;
    cin.get();


    while(1)
    {
        cout << "[+]Значение здоровья: " << dec << memRead<int>(health_address);
        this_thread::sleep_for(chrono::seconds(1));
        system("cls");
    }
 

    CloseHandle(targetProc);
    cin.get();
    return 0;
}
Здесь изменения у нас только в главной функции int main() {}.

Выводим для большего понимания происходящего адрес базового модуля ac_client.exe в памяти:
C++:
GetModuleBaseAddress(L"ac_client.exe");
cout << "[+]Адрес базового модуля ac_client.exe: "<< hex << baseModuleAddress << endl;

Объявили две переменные для хранения смещений:
- для указателя, который хранит адрес PlayerEntity.
- для адреса здоровья игрока.
C++:
int playerEntity_static_offset = 0x17E254;
int health_offset = 0xEC;

Объявили переменную для хранения статического адреса указателя на объект PlayerEntity. То есть получается складываем адрес модуля игры и смещение для указателя. Выводим ответ в консоль для иллюстрации.
C++:
DWORD StaticPtrToPlayerEntity = baseModuleAddress + playerEntity_static_offset;
cout << "[+]Статический указатель на объект PlayerEntity:" << hex << StaticPtrToPlayerEntity << endl;

Объявили переменную для хранения динамического адреса самого объекта PlayerEntity. Мы его получаем путем чтения значения статического указателя для этого объекта PlayerEntity. Как помним указатель хранит в себе адрес другой переменной. Так же выведем информацию в консоль.
C++:
DWORD dynamicAddressPlayerEntity = memRead<DWORD>(StaticPtrToPlayerEntity);
cout << "[+]Динамический адрес PlayerEntity(Начальный адрес структуры), хранящейся в указатели StaticPtrToPlayerEntity: " << hex << dynamicAddressPlayerEntity << endl;
Здесь объявили переменную для хранения адреса здоровья. Получаем его путем сложения динамического адреса PlayerEntity и самого оффсета для здоровья!
C++:
DWORD health_address = dynamicAddressPlayerEntity + health_offset;
cout << "[+]Адрес здоровья: " << health_address << endl;

Здесь у нас после нажатия на клавишу Enter начинается цикл, который будет читать значение из адреса здоровья и обновляться каждую секунду.
C++:
cout << "[!]Нажмите на Enter для запуска цикла мониторинга значения здоровья" << endl;
cin.get();


while(1)
{
    cout << "[+]Значение здоровья: " << dec << memRead<int>(health_address);
    this_thread::sleep_for(chrono::seconds(1));
    system("cls");
}

Более хорошую демонстрацию происходящего можно увидеть на картинке:
24.png

Вы спросите почему при сложении базового адреса модуля со смещением 17E254 получаем 57E254? Ответ очивиден! Большинство программ обычно загружаются в память именно по адресу 0x400000. Это даже можно увидеть если отроете модуль игры при помощи программы для анализа исполняемых файлов CFF Explorer Ссылка
После делаем следующие действия:
Запускаем CFF Explorer:

1-1.png

Далее нажимаем Open и выбираем модуль игры ac_client.exe
1-2.png
Далее у нас откроется окно с разными данными об исполняемом файле. Нам нужна категория NT-Headers -->>> Optional Header и здесь в поле ImageBase можем увидеть тот самый адрес 0x400000. Это предпочитаемое место куда будет загружен модуль.
1-3.png

Дальше думаю вы поняли логику, что при сложении модуля и смещения получается указатель и в этом указатели будет адрес начала объекта PlayerEntity. И уже при помощи оффсетов можем обращаться к тем или иным характеристикам игрока, например здоровье по оффсету 0xEC.

Как вы могли заметить этот метод намного проще, чем в части №3. Здесь мы можем получить сразу все оффсеты для полей объекта, в данном случае игрока. Если вы поисследуете ту структуру PlayerEntity, то обнаружите кучу полезных полей-переменных, при помощи которых можно сделать очень много функций для нашего софта. Но пока это оставим на будущие части! В следующей части возможно изучим более глубже структуры при помощи нового инструмента, нюансы в них и может даже возьмем в пример всеми любимый CS2 :) (может не любимый?)

Спасибо за уделенное время и надеюсь эта статья помогла узнать вам что-то новое для себя! Буду благодарен, если поддержите статью своим лайком. Это очень мотивирует!
До следующей части!
 
Последнее редактирование:
UPD: Скрины что-то пропали в некоторых спойлерах. Поэтому выложу тут.

1.png
2.png
13.png
 
Можешь пожалуйста залить все свои темы в pdf чтобы я постоянно на форум не заходил
Как будет время - сделаю!
 
Это конечно всё база для новичков. Если тебе не сложно и если ты шаришь мог бы ты пожалуйста рассказать про AOB сканер. Я вот свой написал, но очень медленно работает. И под условную мафию2 я нормальную сигнатуру не нашёл статичную.

Искал я указатели на мафию. В чем суть, после перезапуска ВСЁ указатели при глубине поиска 5 пропадают. ВСЁ. Это еще что, но люди как то пишут.

Видел я метод защиты у Цивилизации 6. Привожу пример. Бабки находятся по одному адресу- ты тратишь, -- они улетают на другой адрес. Это наверное единичный прикол, но всё равно хер его знает. И чел один популярный с андеграунда пишет. Как-то через AOB scaner
 
И возьми хотя бы плз более современные игры. Мафию 2 хотя бы. Либо если для примера Earn to Die2 на ПК(это прям вообще понятно будет всем новичкам). А то ломать игру на двигле 2002 года такое.
 
Это конечно всё база для новичков. Если тебе не сложно и если ты шаришь мог бы ты пожалуйста рассказать про AOB сканер. Я вот свой написал, но очень медленно работает. И под условную мафию2 я нормальную сигнатуру не нашёл статичную.

Искал я указатели на мафию. В чем суть, после перезапуска ВСЁ указатели при глубине поиска 5 пропадают. ВСЁ. Это еще что, но люди как то пишут.

Видел я метод защиты у Цивилизации 6. Привожу пример. Бабки находятся по одному адресу- ты тратишь, -- они улетают на другой адрес. Это наверное единичный прикол, но всё равно хер его знает. И чел один популярный с андеграунда пишет. Как-то через AOB scaner
Ну как ты правильно отметил, статья рассчитана для новичков и я стараюсь охватить все важные моменты в этом деле, показать/проиллюстрировать на более простых вещах. Когда уже у читателя все пазлы сложатся в голове, то плавно перейдем на более современные игры и уже там будем использовать знания, полученные ранее.
А про AOB сканер скоро будет статья.
В Мафию не играл и не могу что либо сказать. Как будет время возможно чекну.
 
Ну как ты правильно отметил, статья рассчитана для новичков и я стараюсь охватить все важные моменты в этом деле, показать/проиллюстрировать на более простых вещах. Когда уже у читателя все пазлы сложатся в голове, то плавно перейдем на более современные игры и уже там будем использовать знания, полученные ранее.
А про AOB сканер скоро будет статья.
В Мафию не играл и не могу что либо сказать. Как будет время возможно чекну.
Да. Круто. интересно чекнуть в твоем исполнении.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх