Аннотация В этой статье описано, как программировать читы категории Аимбот, или же другими словами стороннее ПО, осуществляющее наведение курсора игрока на другие игровые цели. Дисклеймер Автор статьи не является носителем истинны в последней инстанции, может допускать ошибки. Выбранный вариант реализации прежде всего демонстрирует возможности программирования. Использование читов и/или их написание может привести к санкциям со стороны игровых площадок, все изложенное воспринимать на свой риск. Автор не призывает никого и ни к чему, не использует читы и ценит сохранность игровой атмосферы. Используемое ПО Visual Studio 2017 Cheat Engine Counter strike source v34 Virtual box (по усмотрению) Механизм расчета Стоит отметить, что все программы используют пространство памяти для своих задач, это могут быть различные типы данных - опкоды, переменные, таблицы импортов экспортов и т.д. Когда пользователь запускает какую либо программу происходит резервирование виртуальной памяти, в данном примере при запуске партии в CS происходит резервирование и заполнение памяти различных структур - HP игрока, его никнейм, координаты, помимо данных игрока создаются и данные соперников. Большинство читов используют (читают, записывают) пространство памяти подобных переменных, преследуя свои цели. В рамках темы нас интересуют следующие переменные Координаты игрока (X, Y, Z) Оси видовой матрицы(XY, XZ) Координаты соперников Трудности в понимании могут произойти уже на этом этапе, разбираемся Первое изображение - координаты 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 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); Code 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), Code 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)), Code 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)); Code MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XY, BitConverter.GetBytes(SwapXY)); MemTool.WriteReg(ModuleEngine + OFFSET_CONTROL_AXIS_XZ, BitConverter.GetBytes(SwapXZ)); Запись значений осей в соответствующие адреса памяти. Поиск адресов и модульная адресация Поиск адресов в памяти производится изменением интересующих значений с целью дальнейшей сверки с предыдущими, эта операция проводится до тех пор, пока не будет найден адрес памяти, а их может быть много. Приведу пример, наша задача найти координату Z игрока - придется подниматься по лестнице, сканировать память, спускаться и так до тех пор, пока не останется несколько адресов. А их может быть много - координата игрока, координата его оружия, координата его ботинок и тд. Для этой задачи хорошо подходит программа Cheat Engine. Cheat Engine В поле адресов памяти есть два адреса - это найденные оси XY, XZ, как было описано ранее. 89 - значение для оси XY, сейчас курсор направлен вниз до предела. В случае с поиском координат ботов лучше использовать другой метод - поиск структуры целиком. В свою очередь это означает, что у бота помимо координат рядом должны находиться какие либо сопутствующие его структуре значения - имя или здоровье. Нанеся урон боту можно узнать на сколько его здоровье снизилось, взглянув на индикатор здоровья, в рамках данной игры, узнать значение здоровье, не прибегая к хитростям можно только у союзников. Рядом с адресом уровня здоровья можно найти адреса координатов конкретного бота, в случае, если их там нет стоит поискать другой адрес, это мог быть адрес не структуры бота, а, например, лист игроков, открывающийся при помощи клавиши Tab. Идея поиска структур плавно переходит к модульной адресации, если в случае со структурой бота найти адрес здоровья можно через прибавления отступа или Offseta, то с модульной адресацией немного иначе. При перезагрузке программы старые адреса могут измениться и скорее всего изменятся, если они не статические. Модульная адресация подразумевает под собой адресацию относительно чего-то определенного, например, модулей client.dll и engine.dll Пример модульной адресации Изображено 6 отступов или Offset'ов Адреса могут быть многоуровневыми и содержать адрес последующего уровня в ячейке памяти. Алгоритм решения задачи таков - программа читает значения по адресу, прибавляет к нему отступ, данное действие повторяется для каждого отступа. Отладка программы, как видно на изображении получено значение бота по имени Yogi, если Yogi начнет двигаться его координаты изменятся, а в случае вступления в бой с соперником может измениться уровень здоровья. Также в структуре бота есть его фракция - 2/3 T/Ct соответственно. КОД Program.cs 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); } } } } Code 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); } } } } MemReader.cs 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; } } } Code 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; } } } Результат работы
алвейсванафлай, Они ближе всего, это лишь с целью демонстрации работы чита. В коде есть функция деления на свой/чужой, если расскоменитровать строку, то цели будут выборочные.
LasiNoob, float SwapXZ = (float)(Math.Atan((CrntTarget.Coord_Z - MainPlayer.Coord_Z - PLAYER_HEIGHT) / (CrntTarget.Coord_X - MainPlayer.Coord_X)) * 180 / Math.PI);
Годно. Только вопрос, почему в коде где мы ищем расстояние до цели, мы в конце формулы умножаем на 180 и делим на Pi? Я чето недопонял.
JanitorHvhboom, если речь идет про стрко 196, 197, то это преобразование радиан в градусы, всего лишь разное представление одной и той же велечины - функция на C# находит ответ в радианах, а игра принимает значения в градусах.
лучше паттерны делать, т.к. игры обновляются а не просто адреса из це взять и всё каждый обновлять я кнш знкаю что сурс не обновляется но просто говорю
codact, нет, не копипаст, вы по новорегжеству ошибаетесь. Изображения - ориг, видео - ориг, код - ориг. Комментарии - ориг.
rootmode, Доброго времени суток. Извиняюсь за оффтоп, меня интересует проблема получения pidки процесса без использования (system.Diagnostics). Мб возможно это реализовать через winapi? Получить дескриптор окна через FindWindow не вышло под шарпом(дело не в разрядности моего по). ps. Идет какая-то непонятка. Сама игра имеет защиту. Встает вопрос, как это обойти. Решил вот вашу функцию взять, тк тупо -1 выдавало.
Привет, 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); Code [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] static extern int GetFileAttributes([MarshalAs(UnmanagedType.LPStr)] string filename);
Zarin666_inactive5841429, И да, PID ищется обычно по имени процесса, то есть gta_sa.exe и тд, иначе возвращаемое значение скорее всего будет -1, то есть процесс не найден. Увидеть имя процесса целиком можно через свойства или хороший аналог его ProcessHacker2