Загрузка...

[C#} Написание простого чита AIMBOT на примере CS Source

Тема в разделе C# создана пользователем rootmode 17 мар 2022. (поднята 8 май 2024) 6588 просмотров

Загрузка...
Опрос

Как вам тема?

  1. Интересно, просто.

    50
    38,8%
  2. Интересно, сложно.

    72
    55,8%
  3. Не интересно, просто.

    4
    3,1%
  4. Не интересно, сложно.

    3
    2,3%
  1. rootmode
    rootmode Автор темы 17 мар 2022 Бу! Испугался? Не бойся, я друг... 19 062 17 июл 2021
    Аннотация
    В этой статье описано, как программировать читы категории Аимбот, или же другими словами стороннее ПО, осуществляющее наведение курсора игрока на другие игровые цели.

    • Автор статьи не является носителем истинны в последней инстанции, может допускать ошибки.
    • Выбранный вариант реализации прежде всего демонстрирует возможности программирования.
    • Использование читов и/или их написание может привести к санкциям со стороны игровых площадок, все изложенное воспринимать на свой риск.
    • Автор не призывает никого и ни к чему, не использует читы и ценит сохранность игровой атмосферы.

    • Visual Studio 2017
    • Cheat Engine
    • Counter strike source v34
    • Virtual box (по усмотрению)

    Механизм расчета
    Стоит отметить, что все программы используют пространство памяти для своих задач, это могут быть различные типы данных - опкоды, переменные, таблицы импортов экспортов и т.д. Когда пользователь запускает какую либо программу происходит резервирование виртуальной памяти, в данном примере при запуске партии в CS происходит резервирование и заполнение памяти различных структур - HP игрока, его никнейм, координаты, помимо данных игрока создаются и данные соперников.
    Большинство читов используют (читают, записывают) пространство памяти подобных переменных, преследуя свои цели.


    В рамках темы нас интересуют следующие переменные

    • Координаты игрока (X, Y, Z)
    • Оси видовой матрицы(XY, XZ)
    • Координаты соперников
    [IMG] [IMG]
    Первое изображение - координаты X, Y, Z. Допустим, бот прыгает - его координата Z сначала возрастает (на начале прыжка), затем снижается (в момент падения).
    Второе изображение - оси видовой матрицы, их две.
    Для понимания можно представить изображение в профиль - боком к боту, получится ось XY.
    Например, прицелившиьс вниз в самую минимальную позицию значение оси станет равно 89 (градусов) а при самой максимальной позиции - станет равным -89 (градусов). При достижении курсора параллельно плоскости XY значение будет равно 0.


    В случае с осью ZX (вид сверху) диапазон значений будет находится от 0 до 360 градусов.

    Зная координаты игрока и координаты соперника можно вычислить необходимые для поподания в цель оси.

    А вот и формулы расчета:
    • XY = Arctg(Xp - Xb; Yp - Yb)
    • XZ = Arctg(Zp - Zb; Xp - Xb)
    P - игрок
    B - бот

    Тангенс - это отношения противолежащего катета к прилежащему, зная это отношение можно найти угол.

    Пример для оси XZ
    [IMG]
    D = X (цели) - X (игрока), то есть растояние до цели только по координате X (движение вперед)
    H = Z (Цели) - Z (игрока), то есть растояние до цели только по координате Z (по высоте)
    Ранее приведенные формулы расчета формируются именно из этого соотношения.
    Для получения оси XY необходимо провести точно такие же действия, только с координатами X,Y

    Код

    float SwapXY = (float)(Math.Atan2((CrntTarget.Coord_Y - MainPlayer.Coord_Y), (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);
    float SwapXZ = (float)(Math.Atan((CrntTarget.Coord_Z - MainPlayer.Coord_Z - PLAYER_HEIGHT) / (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);

    Программе осталось только вписать высчитанные значения в память игры, в таком случае курсор наведется на цель.
    Еще стоит отметить, что есть координаты камеры игрока, а есть координаты самого игрока и это не одни и те же значения, в моем случае для расчета высоты потребовалось подкалибровать параметр высота игрока.


    Механизм взаимодействия с памятью игры
    Предыдущая часть была наиболее сложной для понимания, дело осталось за малым - получить переменные из процесса (игры).
    На помощь спешат методы библиотеки kernel32.dll


    • OpenProcess
    • ReadProcessMemory
    • WriteProcessMemory
    Немного подробнее о них... Read и Write - означает читать и писать соответственно, такие операции производятся с памятью процесса.
    Читая память данная функция возвращает набор байт из указанной области памяти, как и описывалось ранее - это могут быть различные типы данных, например String (строка), Int (целое число) Float (число с плавающей точкой).
    Если необходимо извлечь из памяти число - программа читает 4 байта, поскольку SizeOf(Int) = 4 в рамках данной ОС.
    Если необходимо извлечь переменную типа String - та же самая операция, только длина может быть значительно больше в зависимости от длины строки.


    Далее байты нужно преобразовать в соответствующий тип, например, в Int.
    Пример из кода:
    Код

    HP = BitConverter.ToInt32(MemTool.ReadReg(BotsAddress + OFFSET_HP + OFFSET_BTWN * i, 4), 0),
    Получение значения HP

    Можно попытаться преобразовать данные строки в Int, это тоже получится для перых 4х байт, но адекватного значения ждать не стоит, строки преобразуются с использованием кодировки, в данном примере при помощи ASCII.

    Пример из кода:
    Код
     
    Name = Encoding.ASCII.GetString(MemTool.ReadReg(BotsAddress + OFFSET_NAME + OFFSET_BTWN * i, 8)),
    Получение ника пользователя.


    В случае с методом Write ситуация зеркально противоположная - программа выполняет запись в память процесса, тем самым подменяя текущее значение переменной на заранее подготовленное. По такому принципу работают читы класса GodMode, NoRecoil, позволяя использовать бесконечное здоровье игрока и бесконечные патроны.

    Пример из кода:
    Код

    MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XY, BitConverter.GetBytes(SwapXY));
    MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XZ, BitConverter.GetBytes(SwapXZ));
    Запись значений осей в соответствующие адреса памяти.


    Поиск адресов и модульная адресация
    Поиск адресов в памяти производится изменением интересующих значений с целью дальнейшей сверки с предыдущими, эта операция проводится до тех пор, пока не будет найден адрес памяти, а их может быть много.
    Приведу пример, наша задача найти координату Z игрока - придется подниматься по лестнице, сканировать память, спускаться и так до тех пор, пока не останется несколько адресов. А их может быть много - координата игрока, координата его оружия, координата его ботинок и тд.
    Для этой задачи хорошо подходит программа Cheat Engine.


    [IMG]
    В поле адресов памяти есть два адреса - это найденные оси XY, XZ, как было описано ранее. 89 - значение для оси XY, сейчас курсор направлен вниз до предела.
    В случае с поиском координат ботов лучше использовать другой метод - поиск структуры целиком. В свою очередь это означает, что у бота помимо координат рядом должны находиться какие либо сопутствующие его структуре значения - имя или здоровье.
    Нанеся урон боту можно узнать на сколько его здоровье снизилось, взглянув на индикатор здоровья, в рамках данной игры, узнать значение здоровье, не прибегая к хитростям можно только у союзников. Рядом с адресом уровня здоровья можно найти адреса координатов конкретного бота, в случае, если их там нет стоит поискать другой адрес, это мог быть адрес не структуры бота, а, например, лист игроков, открывающийся при помощи клавиши Tab.

    Идея поиска структур плавно переходит к модульной адресации, если в случае со структурой бота найти адрес здоровья можно через прибавления отступа или Offseta, то с модульной адресацией немного иначе.
    При перезагрузке программы старые адреса могут измениться и скорее всего изменятся, если они не статические.
    Модульная адресация подразумевает под собой адресацию относительно чего-то определенного, например, модулей client.dll и engine.dll


    [IMG]
    Изображено 6 отступов или Offset'ов
    Адреса могут быть многоуровневыми и содержать адрес последующего уровня в ячейке памяти. Алгоритм решения задачи таков - программа читает значения по адресу, прибавляет к нему отступ, данное действие повторяется для каждого отступа.


    [IMG]
    Отладка программы, как видно на изображении получено значение бота по имени Yogi, если Yogi начнет двигаться его координаты изменятся, а в случае вступления в бой с соперником может измениться уровень здоровья. Также в структуре бота есть его фракция - 2/3 T/Ct соответственно.


    КОД
    Код
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Diagnostics;
    using System.Threading;




    namespace SimpleAIM
    {
    class Player
    {
    public float Axis_XY;
    public float Axis_YZ;

    public float Coord_X;
    public float Coord_Y;
    public float Coord_Z;

    public int HP = 0;
    public int Team = 0;

    public string Name = "n/a";
    }


    class Program
    {


    static string NAME_GAME_PROCESS = "hl2";
    static string NAME_MODULE_CLIENT = "client.dll";
    static string NAME_MODULE_ENGINE = "engine.dll";
    static string PLAYER_NAME = "rootmode";
    static float PLAYER_HEIGHT = 67.0f;
    static float PLAYER_TEAM = 3;


    // BOTS OFFSETS

    static int MAIN_OFFSET = 0x04035C0;
    static int OFFSET_NAME = 0x038;
    static int OFFSET_TEAM = 0x58;
    static int OFFSET_HP = 0x05C;
    static int OFFSET_X = 0x060;
    static int OFFSET_Y = OFFSET_X + 4;
    static int OFFSET_Z = OFFSET_Y + 4;
    static int OFFSET_BTWN = 0x0140;




    // MIAN PLAYER OFFSETS

    static int OFFSET_MP_AXIS_XZ = 0x039D510;
    static int OFFSET_MP_AXIS_XY = OFFSET_MP_AXIS_XZ + 4;
    static int OFFSET_MP_X = 0x039D55C;
    static int OFFSET_MP_Y = OFFSET_MP_X + 4;
    static int OFFSET_MP_Z = OFFSET_MP_Y + 4;




    // CONTROLS OFFSET

    static int OFFSET_CONTROL_AXIS_XZ = 0x03953F8;
    static int OFFSET_CONTROL_AXIS_XY = OFFSET_CONTROL_AXIS_XZ + 4;



    static void Main(string[] args)
    {
    /*
    double A = -1.0F;
    double B = -1.0F;
    double Alpha = Math.Atan2(A,B) * 180/Math.PI - 90;

    Console.WriteLine(Alpha.ToString());
    Console.ReadKey();
    return;
    */




    MemReader MemTool = new MemReader(NAME_GAME_PROCESS);


    int ModuleClientAddr = MemTool.GetModuleAddr(NAME_MODULE_CLIENT);
    int ModuleEngine = MemTool.GetModuleAddr(NAME_MODULE_ENGINE);
    int BotsAddress = BitConverter.ToInt32(MemTool.ReadReg(ModuleClientAddr + MAIN_OFFSET, 4), 0);

    Player MainPlayer = new Player();
    List<Player> Bots = new List<Player>();


    while (true)
    {

    // * PART I
    // Parsing main players coord


    MainPlayer.Axis_XY = BitConverter.ToSingle(MemTool.ReadReg(ModuleClientAddr + OFFSET_MP_AXIS_XY, 4), 0);
    MainPlayer.Axis_YZ = BitConverter.ToSingle(MemTool.ReadReg(ModuleClientAddr + OFFSET_MP_AXIS_XZ, 4), 0);

    MainPlayer.Coord_X = BitConverter.ToSingle(MemTool.ReadReg(ModuleClientAddr + OFFSET_MP_X, 4), 0);
    MainPlayer.Coord_Y = BitConverter.ToSingle(MemTool.ReadReg(ModuleClientAddr + OFFSET_MP_Y, 4), 0);
    MainPlayer.Coord_Z = BitConverter.ToSingle(MemTool.ReadReg(ModuleClientAddr + OFFSET_MP_Z, 4), 0);

    /*
    * check main player
    *
    Console.WriteLine($"AXIS: {MainPlayer.Axis_XY} {MainPlayer.Axis_YZ}");
    Console.WriteLine($" X: {MainPlayer.Coord_X}\r\n Y: {MainPlayer.Coord_Y}\r\n Z: {MainPlayer.Coord_Z}");
    Console.WriteLine("=============");
    */







    // * PART II
    // Parsing Bots


    Bots.Clear();
    for (int i = 0; i < 32; i++)
    {
    var newOne = new Player()
    {
    HP = BitConverter.ToInt32(MemTool.ReadReg(BotsAddress + OFFSET_HP + OFFSET_BTWN * i, 4), 0),
    Name = Encoding.ASCII.GetString(MemTool.ReadReg(BotsAddress + OFFSET_NAME + OFFSET_BTWN * i, 8)),
    Team = BitConverter.ToInt32(MemTool.ReadReg(BotsAddress + OFFSET_TEAM + OFFSET_BTWN * i, 4), 0),

    Coord_X = BitConverter.ToSingle(MemTool.ReadReg(BotsAddress + OFFSET_X + OFFSET_BTWN * i, 4), 0),
    Coord_Y = BitConverter.ToSingle(MemTool.ReadReg(BotsAddress + OFFSET_Y + OFFSET_BTWN * i, 4), 0),
    Coord_Z = BitConverter.ToSingle(MemTool.ReadReg(BotsAddress + OFFSET_Z + OFFSET_BTWN * i, 4), 0),
    };

    if (newOne.Team == 0)
    break;

    Bots.Add(newOne);
    }


    /*
    * check bots
    *
    foreach (var bot in Bots)
    {
    Console.WriteLine($"-HP: {bot.HP} -Name: {bot.Name} -Team: {bot.Team}");
    Console.WriteLine($" coord: {bot.Coord_X}; {bot.Coord_Y}; {bot.Coord_Z}");
    Console.WriteLine("=============");
    }
    */







    // * PART III
    // Aim
    Player CrntTarget = new Player();
    double MinDst = 2500;

    foreach (var bot in Bots)
    {
    //bot.Name != PLAYER_NAME & bot.Team != PLAYER_TEAM
    if (bot.Name != PLAYER_NAME
    && bot.HP > 0)
    {
    double dst = Math.Sqrt((MainPlayer.Coord_X - bot.Coord_X) * (MainPlayer.Coord_X - bot.Coord_X) +
    (MainPlayer.Coord_Y - bot.Coord_Y) * (MainPlayer.Coord_Y - bot.Coord_Y) +
    (MainPlayer.Coord_Z - bot.Coord_Z) * (MainPlayer.Coord_Z - bot.Coord_Z));
    if (dst < MinDst)
    {
    MinDst = dst;
    CrntTarget = bot;
    }
    }
    }





    float SwapXY = (float)(Math.Atan2((CrntTarget.Coord_Y - MainPlayer.Coord_Y), (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);
    float SwapXZ = (float)(Math.Atan((CrntTarget.Coord_Z - MainPlayer.Coord_Z - PLAYER_HEIGHT) / (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);




    /*
    * check bots
    *
    Console.WriteLine($"Aim to {CrntTarget.Name} with dst: {MinDst}");
    Console.WriteLine($" coord: {CrntTarget.Coord_X}; {CrntTarget.Coord_Y}; {CrntTarget.Coord_Z}");

    Console.WriteLine($"SwapXY: {SwapXY}");
    Console.WriteLine($"SwapXZ: {SwapXZ}");
    */

    Console.WriteLine($"Aim to {CrntTarget.Name} with dst: {MinDst}");

    MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XY, BitConverter.GetBytes(SwapXY));
    MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XZ, BitConverter.GetBytes(SwapXZ));

    Thread.Sleep(300);
    }

    }


    }
    }

    Код
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Diagnostics;





    namespace SimpleAIM
    {
    class MemReader
    {
    const int PROCESS_FLAGS = 0x1F0FFF;


    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll")]
    public static extern bool ReadProcessMemory(int hProcess,
    int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool WriteProcessMemory(int hProcess, int lpBaseAddress,
    byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);




    private Process process;
    private IntPtr processHandle;
    private int PID;


    public MemReader(string procName)
    {

    PID = GetPID(procName);
    if (PID == -1)
    throw new Exception("Process not found!");


    process = Process.GetProcessById(PID);
    processHandle = OpenProcess(PROCESS_FLAGS, false, process.Id);
    }

    public byte[] ReadReg(int addr, int size)
    {
    int count = 0;
    byte[] buffer = new byte[size];

    ReadProcessMemory((int)processHandle, addr, buffer, buffer.Length, ref count);

    return buffer;
    }

    public void WriteReg(int addr, byte[] reg)
    {
    int writen = 0;
    WriteProcessMemory((int)processHandle, addr, reg, reg.Length, ref writen);
    }


    private int GetPID(string GameName)
    {
    Process[] list = Process.GetProcesses();
    foreach (var prc in list)
    {
    if (prc.ProcessName == GameName)
    return prc.Id;
    }

    return -1;
    }


    public int GetModuleAddr(string ModuleName)
    {

    foreach (ProcessModule module in process.Modules)
    {
    if (module.ModuleName == ModuleName)
    return (int)module.BaseAddress;
    }

    return -1;
    }

    }
    }


    Результат работы
     
  2. алвейсванафлай
    алвейсванафлай 17 мар 2022 Заблокирован(а)
    Зачем убивать товарища по команде?
     
    1. rootmode Автор темы
      алвейсванафлай, Они ближе всего, это лишь с целью демонстрации работы чита. В коде есть функция деления на свой/чужой, если расскоменитровать строку, то цели будут выборочные.
  3. MisterMixa
    MisterMixa 18 мар 2022 Заблокирован(а) 9 4 окт 2021
    Годно
     
  4. Resow
    Resow 18 мар 2022 Гениальные ответы в оффтопе 11 530 31 мар 2021
    Видео не грузит
     
    1. rootmode Автор темы
      Resow, Видео лежит на хостинге форума, тоже иногда недоступно. Конкретно сейчас появилось.
  5. гарнизон
    гарнизон 25 мар 2022 Заблокирован(а) 0 13 мар 2022
    код говно а так +rep
     
    25 мар 2022 Изменено
    1. Посмотреть предыдущие комментарии (2)
    2. гарнизон
      LasiNoob, float SwapXZ = (float)(Math.Atan((CrntTarget.Coord_Z - MainPlayer.Coord_Z - PLAYER_HEIGHT) / (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);
    3. гарнизон
      LasiNoob, про архитектуру речи не идет
    4. vtlstolyarov
      LasiNoob, Объективно - говнокод, идёя хорошая, реализация - неподдерживаемое говно.
  6. LasiNoob
    LasiNoob 26 мар 2022 25 9 июл 2018
    Автору спасибо, статья заслуживает уважения
     
    1. rootmode Автор темы
      LasiNoob, спасибо вам за проявленный интерес.
  7. JanitorHvhboom
    JanitorHvhboom 7 апр 2022 хочу работать. 32 4 апр 2018
    Годно. Только вопрос, почему в коде где мы ищем расстояние до цели, мы в конце формулы умножаем на 180 и делим на Pi? Я чето недопонял.
     
    1. rootmode Автор темы
      JanitorHvhboom, если речь идет про стрко 196, 197, то это преобразование радиан в градусы, всего лишь разное представление одной и той же велечины - функция на C# находит ответ в радианах, а игра принимает значения в градусах.
  8. unnamed001
    unnamed001 18 июл 2022 5997 2 сен 2020
    лучше паттерны делать, т.к. игры обновляются а не просто адреса из це взять и всё каждый обновлять я кнш знкаю что сурс не обновляется но просто говорю
     
    1. rootmode Автор темы
      unnamed001, есть ABS **** и прочие в этом нет проблем, статья больше про написание чита
  9. codact
    codact 30 авг 2022 python dev 164 4 июл 2021
    Жалко что копипаст, ладно хоть перевел
     
    1. rootmode Автор темы
      codact, нет, не копипаст, вы по новорегжеству ошибаетесь.
      Изображения - ориг, видео - ориг, код - ориг. Комментарии - ориг.
  10. Zarin666_inactive5841429
    Zarin666_inactive5841429 21 сен 2022 Заблокирован(а) 0 2 сен 2022
    rootmode, Доброго времени суток. Извиняюсь за оффтоп, меня интересует проблема получения pidки процесса без использования (system.Diagnostics). Мб возможно это реализовать через winapi? Получить дескриптор окна через FindWindow не вышло под шарпом(дело не в разрядности моего по).

    ps. Идет какая-то непонятка. Сама игра имеет защиту. Встает вопрос, как это обойти.
    [IMG]
    Решил вот вашу функцию взять, тк тупо -1 выдавало.
    [IMG]
     
    1. rootmode Автор темы
      Привет, FindWindow это совершенно другая функция, она возвращает дескриптор окна программы или HWIND.
      С этим можно ознакомиться в документации по WinAPI.
      PID - идентификатор процесса, он один (не учитывая мьютексы), а хэндлеров окна может быть много, в том числе многочисленные MessageBox'ы и прочее.

      Функции пространства имен Diagnostics и используют ВинАпи, но не напрямую, вернее через свою интерпретацию, приведение типов и тд. Использовать WinAPI можно, для этого есть пространство имен System.Runtime.InteropServices

      Код
      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
      static extern int GetFileAttributes([MarshalAs(UnmanagedType.LPStr)] string filename);
    2. rootmode Автор темы
      Zarin666_inactive5841429, И да, PID ищется обычно по имени процесса, то есть gta_sa.exe и тд, иначе возвращаемое значение скорее всего будет -1, то есть процесс не найден. Увидеть имя процесса целиком можно через свойства или хороший аналог его ProcessHacker2
  11. Trophieno
    Trophieno 5 окт 2022 Заблокирован(а) 165 22 авг 2022
    Авторку ему так назывемую давайте
     
  12. Holerus
    Holerus 4 дек 2022 1 4 сен 2021
    А можешь готовое дать?
     
  13. rootkit
    rootkit 9 янв 2023 1785 12 фев 2019
    видео не грузит а так годно
     
  14. miq_ebawit
    miq_ebawit 14 янв 2023 2 24 фев 2020
    а смысл от этого
     
  15. Суриков
    Суриков 11 фев 2023 Заблокирован(а) 0 11 фев 2023
    Видео не загружает. Я думаю с ним было бы доступнее
     
  16. Ставьлюбой
    Ставьлюбой 12 дек 2023 Заблокирован(а) 783 23 июн 2023
    годно , даже я понял :yupiyei:
     
Top