Автор: UNSEEN
Источник: https://xss.pro
Хо-хо-хо Форумчане! До Нового года остается совсем немного и планировал как можно скорее написать вторую нашу часть про мир геймхакинга! Если статья вышла до НГ, значит я выполнил свою поставленную задачу! А вам пожелаю хорошего чтива
Немного предыстории…
В прошлой части мы разобрали, что из себя представляет специальный софт(Читы) для игр, и какие бывают способы заработка с этого направления. Рынок очень хорошо развивается в последние годы и умельцы делают на написании этого софта огромные состояния!
Из технической части вспомним, что мы базово прошлись по основам, а именно что из себя представляет оперативная память(ячейки памяти с уникальными адресами для доступа к ним) и что происходит после загрузки туда исполняемой программы операционной системой. Немного разобрались, как и в каком виде хранится информация в современных компьютерных системах(8бит = 1байт).
Коснулись верхушки типов данных, а именно разобрали 1 и 4 байтовые данные. Далее рассмотрим подробно.
Поговорили немного про сканеры памяти и как ими пользоваться. И поэкспериментировали с нашей написанной самими игрой. Произвели поиск и замену значения по найденному адресу в памяти процесса нашей игры при помощи инструмента Cheat Engine.
Начало…
Давайте углубимся в немного в программирование и разберемся с типами данных.
В программировании типы данных являются фундаментальными понятиями, которые определяют, какие виды данных могут быть использованы в программе и как они могут быть обработаны.
Типы данных – это способ хранить данные в памяти. Тип определяет диапазон его возможных значение, список допустимых операций. Как было продемонстрировано в нашей игре, мы хранили значение жизни в 4байтах . Обычно мы можем группировать числа в 1, 2, 4, 8 байтов. То есть, чем больше байтов = тем больше число можем хранить в ячейке(ах). Зная тип данных искомого значения, мы можем легко и правильно сканировать память. Типы данных в программировании определяются с помощью языка программирования. Думаю для примера возьмем язык Си.
В языке программирования C стандартно используются следующие типы данных:
char (символьный тип):
Пример: Игрок находится на земле? Если значение >(больше) 0 то истинна, иначе ложь. Это используется при проверки на различные варианты обработки событий. Допустим есть переменная onGround, которая служит как флаг истинности, которая подразумевает, находится ли игрок на земле или в воздухе? В игре вы делаете прыжок и не можете прыгать до тех пор, пока ваш персонаж не приземлился на землю. Как раз в этот момент игра обрабатывает это все с помощью переменной onGround.
Подробнее можете почитать про типы данных и их модификаторах Здесь
Системы счисления...
При работе с памятью больше всего нам нужно иметь дело с большими числами и для решения этой проблемы была придумана шестнадцатеричная система счисления(HEX).
Шестнадцатеричная система счисления широко используется в программировании и отображении байт кода по нескольким причинам:
Краткость и удобство:
Байт, в основном, представляет собой 8 бит. Шестнадцатеричная система прекрасно сочетается с байтами, так как каждая цифра шестнадцатеричной системы представляет 4 бита (16 возможных значений).
Пример:
В двоичной системе:
Байт 10101010 может быть представлен в шестнадцатеричной системе как AA.
Таким образом, шестнадцатеричная система обеспечивает компромисс между компактностью представления данных и удобочитаемостью, что делает ее удобной для использования в программировании и анализе байтового кода.
Более подробно можете прочитать Здесь
Шестнадцатеричная начинается с 0x, чтобы сообщить компилятору что это hex формат.
Например: 0x123456 0xABCDEF 0x123ABC
Разновидность...
Теперь когда разобрались с базовыми моментами из программирования, думаю стоит поговорить про виды софта.
Их в основном два вида, а именно External и Internal. Различия у этих читов:
External – в переводе на русский, означает “Внешний”. Extrenal чит являясь исполняемой программой, может из вне читать/изменять память игры при помощи запросов(WinApi) к ОС. Данный тип читов труднее обнаружить из-за своего нахождения вне процесса игры. Тут очень важно, записываете ли вы что то в память игры? Если да, то плохо, если нет, то такой чит гораздо труднее обнаружить. Из-за того, что чит находится вне процесса игры , он будет ограничен по своему функционалу. Ведь мы не можем всегда напрямую перезаписывать данные в памяти игры или вызывать какие либо функции в самой игре, что сказывается на скорости чита.
Internal — в переводится, как “Внутренний”. Это код, чаще всего в виде динамической библиотеки – dll. С помощью инжектора внедряется/загружается в адресное пространство процесса игры. И может напрямую читать и редактировать данные в памяти процесса, обращаться к переменным и функциям. Реализовать чуть сложнее, чем External. В связи со своей активностью внутри памяти процесса игры, относительно легко обнаруживать. Из плюсов чит работает намного быстрее и в плане функций имеет широкий выбор.
Практическая часть…
В этой статье мы сегодня базово разберем первый вид читов - External.
Поработаем с запросами(WinApi) к ОС для доступа к процессу и далее чтение данных/запись в память процесса.
Языком программирования нашего софта будет С++.
Подготовка среды…
Для начала вам нужно скачать среду разработки(IDE) Visual Studio Скачать
Выбираем Visual Studio Community и нажимаем скачать.
Подробная видео инструкция установки С++ в IDE Ютуб
Если вы все сделали правильно, запускаем Visual Studio.
Нас встречает такое окошк:
Выбираем "Создание проекта" и создать.
Здесь выбираем «Консольное приложение» Далее
Даем название проекту, можете любой, главное чтобы имя несло смысловую нагрузку =)
После создания проекта, удаляем весь код из исходного файла.
Далее нажимаете на сочетание клавиш Alt + F7 или же в меню IDE - > Проект -> Свойство: имя вашего проекта -> Дополнительно -> Набор символов -> Использовать многобайтовую кодировку.
Кодинг…
Сразу скажу, что мы не будем сильно разбирать синтаксис языка, пройдемся по базовым моментам. Изучить синтаксис думаю не составит труда. В гугле все есть в открытом доступе. Все же статья не про основы программирования =)
Вообще в идеале наш план будет таков:
Как видите вся работа происходит через WinApi. То есть мы будем буквально просить ОС сделать для нас что-то. Далее увидите сами.
У нас имеется четкий план, теперь осталось реализовать.
Весь исходный код нашего софта:
Давайте теперь разберемся с кодом:
Для чего нужны библиотеки и заголовочные файлы:
Мы определили их глобальными, чтобы получать доступ к ним из любой точки нашего кода.
Далее начинается самое важное. Нам нужно получить дескриптор окна по его названию, чтобы далее получить идентификатор процесса, а уже с помощью идентификатора получить права доступа к процессу игры.
Давайте эти действия вынесем в отдельную функцию и назовем ее GetAccessProcess. Она будет принимать название окна процесса. P.S. Названия для переменных и функций давайте логичные, чтобы вам было понятно для чего они служит.
Хорошо, теперь при помощи функции из WinApi - FindWindowA найдем дескриптор окна по его названию и сохраним в нашу переменную targetWindow.
Документация по функции FindWindowA из MSDN.
Теперь сделаем проверку об успешном получении дескриптора окна:
Если успешно смогли получить дескриптор окна приступаем к получению идентификатора(PID) процесса, для этого есть функция GetWindowThreadProcessId.
Документация по GetWindowThreadProcessId из MSDN.
Полученное значение записывается в переменную pID.
Далее так же нужно сделать проверку:
При успешном получении идентификатора нам остается последний шаг - это получить права доступа для работы с целевым процессом. Делается это при помощи функции OpenProcess и значение записывается в targetProc.
Документация по OpenProcess из MSDN.
Делаем так же проверку, что права доступа получены:
При успешном выполнении, мы получим полный доступ для работы с процессом. Имея доступ мы можем наконец взаимодействовать с памятью процесса, для чего и были сделаны все описанные выше действия.
Еще отмечу, чтобы было удобно нам работать с функциями чтения и записи в память, предлагаю создать шаблоны функций для этого. Шаблоны функций (function template) позволяют определять функции, которые не зависят от конкретных типов. Ведь в памяти могут храниться разные типы данных. Это нам сильно облегчит жизнь =) Создадим два шаблона функций memWrite(Запись в память) и memRead(Чтение из памяти).
Шаблон функции memWrite для записи в память:
Функция memWrite, принимает адрес и значение, которое нужно записать по данному адресу в память. Сама запись происходит при помощи функции из WinApi - WriteProcessMemory.
Шаблон функции memRead для чтения из памяти:
Функция memRead, будет принимать адрес из которого нужно прочитать данные. Прочитанные данные записываются в переменную buffer и возвращаются функцией туда, откуда вызвали memRead. Чтение происходит при помощи функции из WinApi – ReadProcessMemory.
Важную часть мы уже проделали и осталось совсем мало штрихов =)
Далее идет главная функция main нашей программы.
В ней имеется следующее:
Это нужно, если вы работаете с кириллицей в консоли программы. Попробуйте убрать эту строчку кода и сразу поймете, к чему я =)
В ней мы вызываем созданную выше функцию GetAccessProcess и передаем название окна игры. После получения прав доступа к процессу нам нужно проверить, как все работает.
Для этого возьмите любую игру(желательно оффлайн, ибо в онлайн играх есть шанс получения бана, а про обходы АнтиЧитов пока мы не проходили)
Для примера я взял AssaultCube. Поэкспериментируйте с несколькими играми. Практика, практика и только практика!
Запускаем игру и делаем оконный режим, чтобы было удобно.
Значит вызов GetAccessProcess и передача в нее имени окна будет следующим:
Теперь в целях эксперимента, попробуем найти адрес патронов.
Открываем наш любимый сканер Cheat Engine и полученными знаниями из предыдущей части части [0x1] находим нужный нам адрес.
Скорее всего, у вас найдется не меньше двух адресов, связанно это из-за того, что в одном адресе хранится само значение патронов, а в другой значение, которое отображается в интерфейсе игры.
Попробуйте поэкспериментировать и найти адрес самих патронов, а не адрес интерфейса.
У меня этим адресом оказался 008F2268;
Дальше мы создаем переменную address_ammo и сохраняем в ней найденный адрес 008F2268:
Не забываем про 0x перед 16-ым числом.
А теперь настал момент Х =)
Вызываем функцию memRead<int>(address_ammo) и передаем в качестве аргумента переменную.
Заметим, <int> означает, что мы читаем из памяти значение с типом 4 байта. Ведь наше значение патронов целочисленные. Если бы мы хотели вывести (пример*) координаты игрока, то использовали бы float или double.
Попробуем произвести чтение по этому адресу и вывести значение в консоль.
Хорошо, теперь запишем какое-нибудь новое значение в память при помощи функции memWrite.
Передаем в функцию адрес и значение, которое нужно записать, например 100.
И снова при помощи memRead прочитаем, чтобы проверить изменилось ли значение.
Да это сработало!
Мы сначала прочитали значение до записи и после, наша программа смогла произвести чтение и запись. Задача выполнена!
Поработав с процессом мы должны закрыть ее хэндл : CloseHandle(targetProc);
Но у нас все еще осталась одна проблема с адресами. После перезапуска они все еще меняются. В этой части мы не смогли обсудить виртуальную память и смещения в памяти.
Статья итак вышла объемной и тема виртуальной памяти/смещений было бы трудно обсудить сразу. Думаю сделаем это уже в след. части.
Сегодня мы смогли написать основу(скелет) нашего будущего софта. В будущих статьях будем реализовывать новые функции и фичи.
Еще добавлю для совсем зеленых в программировании, как и в предыдущей части:
- Если еще не начали изучать какой-либо язык, то рекомендую сделать это как можно скорее.
- Всегда экспериментируйте! Если что-то не получается, то вы всегда можете спросить на этом чудесном форуме!
Просьба к профешионал кодерам и знатокам, попросил бы Вас дополнять или исправлять моменты, которые я мог пропустить. Таким образом вы сделаете вклад в развитие =)
Всем добра и до следующей части!
Источник: https://xss.pro
Хо-хо-хо Форумчане! До Нового года остается совсем немного и планировал как можно скорее написать вторую нашу часть про мир геймхакинга! Если статья вышла до НГ, значит я выполнил свою поставленную задачу! А вам пожелаю хорошего чтива

Немного предыстории…
В прошлой части мы разобрали, что из себя представляет специальный софт(Читы) для игр, и какие бывают способы заработка с этого направления. Рынок очень хорошо развивается в последние годы и умельцы делают на написании этого софта огромные состояния!
Из технической части вспомним, что мы базово прошлись по основам, а именно что из себя представляет оперативная память(ячейки памяти с уникальными адресами для доступа к ним) и что происходит после загрузки туда исполняемой программы операционной системой. Немного разобрались, как и в каком виде хранится информация в современных компьютерных системах(8бит = 1байт).
Коснулись верхушки типов данных, а именно разобрали 1 и 4 байтовые данные. Далее рассмотрим подробно.
Поговорили немного про сканеры памяти и как ими пользоваться. И поэкспериментировали с нашей написанной самими игрой. Произвели поиск и замену значения по найденному адресу в памяти процесса нашей игры при помощи инструмента Cheat Engine.
Начало…
Давайте углубимся в немного в программирование и разберемся с типами данных.
В программировании типы данных являются фундаментальными понятиями, которые определяют, какие виды данных могут быть использованы в программе и как они могут быть обработаны.
Типы данных – это способ хранить данные в памяти. Тип определяет диапазон его возможных значение, список допустимых операций. Как было продемонстрировано в нашей игре, мы хранили значение жизни в 4байтах . Обычно мы можем группировать числа в 1, 2, 4, 8 байтов. То есть, чем больше байтов = тем больше число можем хранить в ячейке(ах). Зная тип данных искомого значения, мы можем легко и правильно сканировать память. Типы данных в программировании определяются с помощью языка программирования. Думаю для примера возьмем язык Си.
В языке программирования C стандартно используются следующие типы данных:
char (символьный тип):
- Числовой диапазон: -128 до 127 или 0 до 255 (если без знака).
- Размер: 1 байт.
- Числовой диапазон: зависит от компилятора, обычно -32,768 до 32,767 или 0 до 65,535 (если без знака).
- Размер: обычно 4 байта, но может варьироваться в зависимости от платформы.
- Пример: Может хранить количество монет/жизней/патронов
- Числовой диапазон: примерно от -3.4E38 до 3.4E38.
- Размер: 4 байта.
- Пример: Может хранить координаты игрока/врага/предметов и т.д.
- Числовой диапазон: примерно от -1.7E308 до 1.7E308.
- Размер: 8 байт.
- Пример: В большинстве случаев нужен для более точных расчетов.
Пример: Игрок находится на земле? Если значение >(больше) 0 то истинна, иначе ложь. Это используется при проверки на различные варианты обработки событий. Допустим есть переменная onGround, которая служит как флаг истинности, которая подразумевает, находится ли игрок на земле или в воздухе? В игре вы делаете прыжок и не можете прыгать до тех пор, пока ваш персонаж не приземлился на землю. Как раз в этот момент игра обрабатывает это все с помощью переменной onGround.
Подробнее можете почитать про типы данных и их модификаторах Здесь
Системы счисления...
При работе с памятью больше всего нам нужно иметь дело с большими числами и для решения этой проблемы была придумана шестнадцатеричная система счисления(HEX).
Шестнадцатеричная система счисления широко используется в программировании и отображении байт кода по нескольким причинам:
Краткость и удобство:
- Позволяет представлять большие значения байтовой информации (например, адреса памяти) более компактно, чем в двоичной или десятичной системе.
- Уменьшает количество цифр для представления тех же данных.
- Шестнадцатеричные числа более легко читать и записывать в сравнении с длинными последовательностями двоичных цифр.
- Переход между двоичной и шестнадцатеричной системами более интуитивен.
Байт, в основном, представляет собой 8 бит. Шестнадцатеричная система прекрасно сочетается с байтами, так как каждая цифра шестнадцатеричной системы представляет 4 бита (16 возможных значений).
Пример:
В двоичной системе:
Байт 10101010 может быть представлен в шестнадцатеричной системе как AA.
Таким образом, шестнадцатеричная система обеспечивает компромисс между компактностью представления данных и удобочитаемостью, что делает ее удобной для использования в программировании и анализе байтового кода.
Более подробно можете прочитать Здесь
Шестнадцатеричная начинается с 0x, чтобы сообщить компилятору что это hex формат.
Например: 0x123456 0xABCDEF 0x123ABC
Разновидность...
Теперь когда разобрались с базовыми моментами из программирования, думаю стоит поговорить про виды софта.
Их в основном два вида, а именно External и Internal. Различия у этих читов:
External – в переводе на русский, означает “Внешний”. Extrenal чит являясь исполняемой программой, может из вне читать/изменять память игры при помощи запросов(WinApi) к ОС. Данный тип читов труднее обнаружить из-за своего нахождения вне процесса игры. Тут очень важно, записываете ли вы что то в память игры? Если да, то плохо, если нет, то такой чит гораздо труднее обнаружить. Из-за того, что чит находится вне процесса игры , он будет ограничен по своему функционалу. Ведь мы не можем всегда напрямую перезаписывать данные в памяти игры или вызывать какие либо функции в самой игре, что сказывается на скорости чита.
Internal — в переводится, как “Внутренний”. Это код, чаще всего в виде динамической библиотеки – dll. С помощью инжектора внедряется/загружается в адресное пространство процесса игры. И может напрямую читать и редактировать данные в памяти процесса, обращаться к переменным и функциям. Реализовать чуть сложнее, чем External. В связи со своей активностью внутри памяти процесса игры, относительно легко обнаруживать. Из плюсов чит работает намного быстрее и в плане функций имеет широкий выбор.
Практическая часть…
В этой статье мы сегодня базово разберем первый вид читов - External.
Поработаем с запросами(WinApi) к ОС для доступа к процессу и далее чтение данных/запись в память процесса.
Языком программирования нашего софта будет С++.
Подготовка среды…
Для начала вам нужно скачать среду разработки(IDE) Visual Studio Скачать
Выбираем Visual Studio Community и нажимаем скачать.
Подробная видео инструкция установки С++ в IDE Ютуб
Если вы все сделали правильно, запускаем Visual Studio.
Нас встречает такое окошк:
Выбираем "Создание проекта" и создать.
Здесь выбираем «Консольное приложение» Далее
Даем название проекту, можете любой, главное чтобы имя несло смысловую нагрузку =)
После создания проекта, удаляем весь код из исходного файла.
Далее нажимаете на сочетание клавиш Alt + F7 или же в меню IDE - > Проект -> Свойство: имя вашего проекта -> Дополнительно -> Набор символов -> Использовать многобайтовую кодировку.
Кодинг…
Сразу скажу, что мы не будем сильно разбирать синтаксис языка, пройдемся по базовым моментам. Изучить синтаксис думаю не составит труда. В гугле все есть в открытом доступе. Все же статья не про основы программирования =)
Вообще в идеале наш план будет таков:
- Найти нужный нам процесс(Game.exe) при помощи названия окна.(Надеюсь не забыли, что из себя представляет процесс? Если забыли, то бегом перечитывать 0x1 часть)
- При помощи запроса к ОС предоставить нашей программе(Cheat.exe) полный доступ к памяти целевого процесса(Game.exe)
- На этом этапе мы уже можем читать и записывать новые данные по адресам целевого процесса функциями из winapi.
Как видите вся работа происходит через WinApi. То есть мы будем буквально просить ОС сделать для нас что-то. Далее увидите сами.
У нас имеется четкий план, теперь осталось реализовать.
Весь исходный код нашего софта:
C++:
/*
Author Unseen
Source xss.pro
*/
#include <iostream>
/*
специфичный заголовочный файл, в котором объявляются функции,
предоставляющие интерфейс доступа к Windows API
*/
#include <Windows.h>
/*
Заголовочный файл в Windows API, который предоставляет набор функций
для работы с информацией о процессах и потоках.
Он используется для выполнения операций, связанных с манипуляцией процессов и их состояний,
в том числе получения списка запущенных процессов и
информации о модулях в адресном пространстве процесса.
*/
#include <TlHelp32.h>
using namespace std;
/*Глобальные переменные, чтобы работать с ними, где угодно в коде*/
HANDLE targetProc = NULL;
HWND targetWindow = NULL;
DWORD pID = 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;
}
}
}
}
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*)"Название_Окна_Игры");
DWORD adresses_ammo = 0xВставьте найденный адрес;
cout << "Патронов было: " << memRead<int>(adresses_ammo) << endl;
memWrite<int>(adresses_ammo, 100);
cout << "Патронов стало: " << memRead<int>(adresses_ammo) << endl;
CloseHandle(targetProc);
cin.get();
return 0;
}
Давайте теперь разберемся с кодом:
Для чего нужны библиотеки и заголовочные файлы:
- iostream - Стандартная библиотека для работы с потоками ввода и вывода С++
- Windows.h - специфичный заголовочный файл, в котором объявляются функции, предоставляющие интерфейс доступа к Windows API.
- TlHelp32.h - заголовочный файл в Windows API, который предоставляет набор функций для работы с информацией о процессах и потоках. Он используется для выполнения операций, связанных с манипуляцией процессов и их состояний, в том числе получения списка запущенных процессов и информации о модулях в адресном пространстве процесса.
C++:
HANDLE targetProc = NULL;
HWND targetWindow = NULL;
DWORD pID = 0;
- targetProc – будет хранить дескриптор целевого процесса. С помощью него мы будем обращаться к процессу.
- targetWindow – будет хранить дескриптор окна процесса.
- pID – будем хранить идентификатор целевого процесса. Он используется для управления процессами, отслеживания их состояния и взаимодействия с ними.
Мы определили их глобальными, чтобы получать доступ к ним из любой точки нашего кода.
Далее начинается самое важное. Нам нужно получить дескриптор окна по его названию, чтобы далее получить идентификатор процесса, а уже с помощью идентификатора получить права доступа к процессу игры.
Давайте эти действия вынесем в отдельную функцию и назовем ее GetAccessProcess. Она будет принимать название окна процесса. P.S. Названия для переменных и функций давайте логичные, чтобы вам было понятно для чего они служит.
Хорошо, теперь при помощи функции из WinApi - FindWindowA найдем дескриптор окна по его названию и сохраним в нашу переменную targetWindow.
Документация по функции FindWindowA из MSDN.
C++:
targetWindow = FindWindowA(0, targetName);
Теперь сделаем проверку об успешном получении дескриптора окна:
C++:
if (targetWindow == NULL)
{
cout << "Не удалось найти окно игры!" << endl;
}
Если успешно смогли получить дескриптор окна приступаем к получению идентификатора(PID) процесса, для этого есть функция GetWindowThreadProcessId.
Документация по GetWindowThreadProcessId из MSDN.
C++:
GetWindowThreadProcessId(targetWindow, &pID);
Полученное значение записывается в переменную pID.
Далее так же нужно сделать проверку:
C++:
if (pID == 0)
{
cout << "Не удалось получить идентификатор процесса!" << endl;
}
При успешном получении идентификатора нам остается последний шаг - это получить права доступа для работы с целевым процессом. Делается это при помощи функции OpenProcess и значение записывается в targetProc.
Документация по OpenProcess из MSDN.
C++:
targetProc = OpenProcess(PROCESS_ALL_ACCESS, false, pID);
Делаем так же проверку, что права доступа получены:
C++:
if (!targetProc)
{
cout << "Не удалось получить права доступа!" << endl;
}
При успешном выполнении, мы получим полный доступ для работы с процессом. Имея доступ мы можем наконец взаимодействовать с памятью процесса, для чего и были сделаны все описанные выше действия.
Еще отмечу, чтобы было удобно нам работать с функциями чтения и записи в память, предлагаю создать шаблоны функций для этого. Шаблоны функций (function template) позволяют определять функции, которые не зависят от конкретных типов. Ведь в памяти могут храниться разные типы данных. Это нам сильно облегчит жизнь =) Создадим два шаблона функций memWrite(Запись в память) и memRead(Чтение из памяти).
Шаблон функции memWrite для записи в память:
C++:
template<class dataType>
void memWrite(DWORD address, dataType value)
{
WriteProcessMemory(hTargetProc, (PVOID)address, &value, sizeof(dataType), 0);
}
Функция memWrite, принимает адрес и значение, которое нужно записать по данному адресу в память. Сама запись происходит при помощи функции из WinApi - WriteProcessMemory.
Шаблон функции memRead для чтения из памяти:
C++:
template <class dataType>
dataType memRead(DWORD address)
{
dataType buffer;
ReadProcessMemory(hTargetProc, (PVOID)address, &buffer, sizeof(dataType), 0);
return buffer;
}
Важную часть мы уже проделали и осталось совсем мало штрихов =)
Далее идет главная функция main нашей программы.
В ней имеется следующее:
Это нужно, если вы работаете с кириллицей в консоли программы. Попробуйте убрать эту строчку кода и сразу поймете, к чему я =)
C++:
setlocale(LC_ALL, "Russian");
C++:
GetAccessProcess((char*)"Название_Окна_Игры");
Для этого возьмите любую игру(желательно оффлайн, ибо в онлайн играх есть шанс получения бана, а про обходы АнтиЧитов пока мы не проходили)
Для примера я взял AssaultCube. Поэкспериментируйте с несколькими играми. Практика, практика и только практика!
Запускаем игру и делаем оконный режим, чтобы было удобно.
C++:
GetAccessProcess((char*)"AssaultCube");
Теперь в целях эксперимента, попробуем найти адрес патронов.
Открываем наш любимый сканер Cheat Engine и полученными знаниями из предыдущей части части [0x1] находим нужный нам адрес.
Скорее всего, у вас найдется не меньше двух адресов, связанно это из-за того, что в одном адресе хранится само значение патронов, а в другой значение, которое отображается в интерфейсе игры.
Попробуйте поэкспериментировать и найти адрес самих патронов, а не адрес интерфейса.
У меня этим адресом оказался 008F2268;
Дальше мы создаем переменную address_ammo и сохраняем в ней найденный адрес 008F2268:
C++:
DWORD address_ammo = 0x008F2268;
DWORD address_ammo = 0x008F2268Не забываем про 0x перед 16-ым числом.
А теперь настал момент Х =)
Вызываем функцию memRead<int>(address_ammo) и передаем в качестве аргумента переменную.
Заметим, <int> означает, что мы читаем из памяти значение с типом 4 байта. Ведь наше значение патронов целочисленные. Если бы мы хотели вывести (пример*) координаты игрока, то использовали бы float или double.
Попробуем произвести чтение по этому адресу и вывести значение в консоль.
C++:
cout << "Патронов было: " << memRead<int>(address_ammo) << endl;
Хорошо, теперь запишем какое-нибудь новое значение в память при помощи функции memWrite.
C++:
memWrite<int>(address_ammo, 100);
И снова при помощи memRead прочитаем, чтобы проверить изменилось ли значение.
Да это сработало!
Мы сначала прочитали значение до записи и после, наша программа смогла произвести чтение и запись. Задача выполнена!
Поработав с процессом мы должны закрыть ее хэндл : CloseHandle(targetProc);
Но у нас все еще осталась одна проблема с адресами. После перезапуска они все еще меняются. В этой части мы не смогли обсудить виртуальную память и смещения в памяти.
Статья итак вышла объемной и тема виртуальной памяти/смещений было бы трудно обсудить сразу. Думаю сделаем это уже в след. части.
Сегодня мы смогли написать основу(скелет) нашего будущего софта. В будущих статьях будем реализовывать новые функции и фичи.
Еще добавлю для совсем зеленых в программировании, как и в предыдущей части:
- Если еще не начали изучать какой-либо язык, то рекомендую сделать это как можно скорее.
- Всегда экспериментируйте! Если что-то не получается, то вы всегда можете спросить на этом чудесном форуме!
Просьба к профешионал кодерам и знатокам, попросил бы Вас дополнять или исправлять моменты, которые я мог пропустить. Таким образом вы сделаете вклад в развитие =)
Всем добра и до следующей части!
