Kitsune
23.07.2010, 14:56
> Создание бота на C# 2.0 Часть 1 <
Данный гайд является основоположником серии гайдов посвященным созданию бота/ботов для игры PW.
Вступление:
Бот (сокр. от «робот») — в Википедии специальная программа для совершения рутинных операций.
Для работы программы(бота), ей необходимы получать данные из игры, чтобы знать как реагировать в той или иной ситуации.
В данном гайде речь как раз и пойдет о получении данных из клиента PW.
Так же хочу сразу отметить, что в этом гайде не будет идти речь об устройстве памяти PW и о том как хранятся данные в ней. На эту тему уже есть статьи и ссылки на них приведены в конце темы.
Приступим:
Создадим новый проект WinForms
Накидываем на нашу форму 2 элемента Label и 2 элемента ProgressBar и расположим их как нам угодно.
Например так:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Данные наш бот будет получать из памяти клиента PW. Для этого нам придется прибегнуть к функции ReadProcessMemory из библиотеки kernel32.
.NET Framework позволяет нам подключать и использовать методы из библиотек написанных не на управляемом коде (managed code).
В этом нам поможет DllImport, который находится в пространстве имен System.Runtime.InteropServices.
// Импортируем функцию для чтения памяти чужого процесса
// из kernel32
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesRead
);
Покодив немного у меня получился такой класс для чтения памяти клиента PW.
Примечание: этот класс использует namespace: System.Diagnostics и System.Runtime.InteropServices соответственно надо не забыть их подключить.
public class Monitor
{
// Объявление события
public event EventHandler InfoUpdated;
// Метод, который вызывает событие
private void OnInfoUpdated(object sender, EventArgs e)
{
if (InfoUpdated != null) InfoUpdated(sender, e);
}
// Импортируем функцию для чтения памяти чужого процесса
// из kernel32
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesRead
);
// Определение базового адреса и оффсетов
// для текущей версии клиента руоффа (130)
private int baseAddress = 0x98B47C,
offsetHP = 0x464,
offsetMP = 0x468,
offsetMaxHP = 0x494,
offsetMaxMP = 0x498;
// Служебная переменная для хранения PID процесса
private Int32 PID = 0;
private Timer updateTimer;
// Свойство, для хранения текущего HP
public Int32 HP { get; set; }
// Свойство, для хранения текущего MP
public Int32 MP { get; set; }
// по аналогии
public Int32 MaxHP { get; set; }
// по аналогии
public Int32 MaxMP { get; set; }
public Monitor(int pid)
{
this.PID = pid;
updateTimer = new Timer();
updateTimer.Enabled = true;
updateTimer.Interval = 500;
updateTimer.Tick += new EventHandler(updateTimer_Tick);
}
/// <summary>
/// Данный метод вызывается каждый тик таймера
/// Здесь происходит чтение из памяти клиента значений
/// жизни и маны
/// </summary>
private void updateTimer_Tick(object sender, EventArgs e)
{
try
{
if (PID != 0)
{
IntPtr handle = Process.GetProcessById(PID).Handle;
HP = HighLevelReadIntFromMemory(handle, offsetHP);
MP = HighLevelReadIntFromMemory(handle, offsetMP);
MaxHP = HighLevelReadIntFromMemory(handle, offsetMaxHP);
MaxMP = HighLevelReadIntFromMemory(handle, offsetMaxMP);
OnInfoUpdated(this, new EventArgs());
}
}
catch (Exception ex)
{
updateTimer.Enabled = false;
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// Читаем Int'овое значение из памяти приложения
/// с указанным оффсетом
/// </summary>
private Int32 HighLevelReadIntFromMemory(IntPtr handle, int offset)
{
byte[] buffer = new byte[0];
IntPtr read = IntPtr.Zero;
int temp = 0;
temp = LowLevelReadIntFromMemory(handle, baseAddress);
temp = LowLevelReadIntFromMemory(handle, temp + 0x20);
temp = LowLevelReadIntFromMemory(handle, temp + offset);
return temp;
}
/// <summary>
/// Читаем из памяти приложения массив байт по указанному адресу
/// и возвращаем целочисленное его представление
/// </summary>
private Int32 LowLevelReadIntFromMemory(IntPtr handle, int address)
{
byte[] buffer = new byte[4];
IntPtr read = IntPtr.Zero;
ReadProcessMemory(handle, (IntPtr)address, buffer, 4, out read);
return (int)BitConverter.ToUInt32(buffer, 0);
}
}
Теперь как обычно: создадим объект нашего класса Monitor в теле главного класса.
private Monitor mon;
В конструкторе главного класса, в данном случаи это наша форма сделаем следующее:
Process[] p = Process.GetProcessesByName("elementclient");
if(p != null)
{
if (p.Length > 0)
{
// инициализируем новый объект класса Monitor
// передаем туда PID первого процесса из найденных
mon = new Monitor((int)p[0].Id);
// Подписываемся на событие, в котором будем обновлять
// пользовательский интерфейс
mon.InfoUpdated += new EventHandler(mon_InfoUpdated);
}
}
В тело нашего главного класса добавляем метод, который вызывается каждый раз когда срабатывает событие InfoUpdated класса Monitor.
private void mon_InfoUpdated(object sender, EventArgs e)
{
label1.Text = String.Format("HP: {0} / {1}", mon.HP, mon.MaxHP);
label2.Text = String.Format("MP: {0} / {1}", mon.MP, mon.MaxMP);
progressBar1.Value = (int)((mon.HP * 100) / Math.Max(mon.MaxHP, 1));
progressBar2.Value = (int)((mon.MP * 100) / Math.Max(mon.MaxMP, 1));
}
Примечание: не забываем подключить namespace: System.Diagnostics. Данный неймспейс содержит в себе класс для работы с процессами.
После сборки проекта и тестирования мы получили результат:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
З.Ы. На скрине небольшая неувязочка со значением MP в клиенте и нашей програмке. Причина: окно PW заморозилось, когда фокус был с него снят.
В аттаче находится рабочий проект созданный на MS Visual Studio 2008 Professional.
Полезные ссылки:
Узнаем base address клиента для использования ботов и хаков ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Injection Codes - Delphi/C++/AutoIt ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Данный гайд является основоположником серии гайдов посвященным созданию бота/ботов для игры PW.
Вступление:
Бот (сокр. от «робот») — в Википедии специальная программа для совершения рутинных операций.
Для работы программы(бота), ей необходимы получать данные из игры, чтобы знать как реагировать в той или иной ситуации.
В данном гайде речь как раз и пойдет о получении данных из клиента PW.
Так же хочу сразу отметить, что в этом гайде не будет идти речь об устройстве памяти PW и о том как хранятся данные в ней. На эту тему уже есть статьи и ссылки на них приведены в конце темы.
Приступим:
Создадим новый проект WinForms
Накидываем на нашу форму 2 элемента Label и 2 элемента ProgressBar и расположим их как нам угодно.
Например так:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Данные наш бот будет получать из памяти клиента PW. Для этого нам придется прибегнуть к функции ReadProcessMemory из библиотеки kernel32.
.NET Framework позволяет нам подключать и использовать методы из библиотек написанных не на управляемом коде (managed code).
В этом нам поможет DllImport, который находится в пространстве имен System.Runtime.InteropServices.
// Импортируем функцию для чтения памяти чужого процесса
// из kernel32
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesRead
);
Покодив немного у меня получился такой класс для чтения памяти клиента PW.
Примечание: этот класс использует namespace: System.Diagnostics и System.Runtime.InteropServices соответственно надо не забыть их подключить.
public class Monitor
{
// Объявление события
public event EventHandler InfoUpdated;
// Метод, который вызывает событие
private void OnInfoUpdated(object sender, EventArgs e)
{
if (InfoUpdated != null) InfoUpdated(sender, e);
}
// Импортируем функцию для чтения памяти чужого процесса
// из kernel32
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesRead
);
// Определение базового адреса и оффсетов
// для текущей версии клиента руоффа (130)
private int baseAddress = 0x98B47C,
offsetHP = 0x464,
offsetMP = 0x468,
offsetMaxHP = 0x494,
offsetMaxMP = 0x498;
// Служебная переменная для хранения PID процесса
private Int32 PID = 0;
private Timer updateTimer;
// Свойство, для хранения текущего HP
public Int32 HP { get; set; }
// Свойство, для хранения текущего MP
public Int32 MP { get; set; }
// по аналогии
public Int32 MaxHP { get; set; }
// по аналогии
public Int32 MaxMP { get; set; }
public Monitor(int pid)
{
this.PID = pid;
updateTimer = new Timer();
updateTimer.Enabled = true;
updateTimer.Interval = 500;
updateTimer.Tick += new EventHandler(updateTimer_Tick);
}
/// <summary>
/// Данный метод вызывается каждый тик таймера
/// Здесь происходит чтение из памяти клиента значений
/// жизни и маны
/// </summary>
private void updateTimer_Tick(object sender, EventArgs e)
{
try
{
if (PID != 0)
{
IntPtr handle = Process.GetProcessById(PID).Handle;
HP = HighLevelReadIntFromMemory(handle, offsetHP);
MP = HighLevelReadIntFromMemory(handle, offsetMP);
MaxHP = HighLevelReadIntFromMemory(handle, offsetMaxHP);
MaxMP = HighLevelReadIntFromMemory(handle, offsetMaxMP);
OnInfoUpdated(this, new EventArgs());
}
}
catch (Exception ex)
{
updateTimer.Enabled = false;
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// Читаем Int'овое значение из памяти приложения
/// с указанным оффсетом
/// </summary>
private Int32 HighLevelReadIntFromMemory(IntPtr handle, int offset)
{
byte[] buffer = new byte[0];
IntPtr read = IntPtr.Zero;
int temp = 0;
temp = LowLevelReadIntFromMemory(handle, baseAddress);
temp = LowLevelReadIntFromMemory(handle, temp + 0x20);
temp = LowLevelReadIntFromMemory(handle, temp + offset);
return temp;
}
/// <summary>
/// Читаем из памяти приложения массив байт по указанному адресу
/// и возвращаем целочисленное его представление
/// </summary>
private Int32 LowLevelReadIntFromMemory(IntPtr handle, int address)
{
byte[] buffer = new byte[4];
IntPtr read = IntPtr.Zero;
ReadProcessMemory(handle, (IntPtr)address, buffer, 4, out read);
return (int)BitConverter.ToUInt32(buffer, 0);
}
}
Теперь как обычно: создадим объект нашего класса Monitor в теле главного класса.
private Monitor mon;
В конструкторе главного класса, в данном случаи это наша форма сделаем следующее:
Process[] p = Process.GetProcessesByName("elementclient");
if(p != null)
{
if (p.Length > 0)
{
// инициализируем новый объект класса Monitor
// передаем туда PID первого процесса из найденных
mon = new Monitor((int)p[0].Id);
// Подписываемся на событие, в котором будем обновлять
// пользовательский интерфейс
mon.InfoUpdated += new EventHandler(mon_InfoUpdated);
}
}
В тело нашего главного класса добавляем метод, который вызывается каждый раз когда срабатывает событие InfoUpdated класса Monitor.
private void mon_InfoUpdated(object sender, EventArgs e)
{
label1.Text = String.Format("HP: {0} / {1}", mon.HP, mon.MaxHP);
label2.Text = String.Format("MP: {0} / {1}", mon.MP, mon.MaxMP);
progressBar1.Value = (int)((mon.HP * 100) / Math.Max(mon.MaxHP, 1));
progressBar2.Value = (int)((mon.MP * 100) / Math.Max(mon.MaxMP, 1));
}
Примечание: не забываем подключить namespace: System.Diagnostics. Данный неймспейс содержит в себе класс для работы с процессами.
После сборки проекта и тестирования мы получили результат:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
З.Ы. На скрине небольшая неувязочка со значением MP в клиенте и нашей програмке. Причина: окно PW заморозилось, когда фокус был с него снят.
В аттаче находится рабочий проект созданный на MS Visual Studio 2008 Professional.
Полезные ссылки:
Узнаем base address клиента для использования ботов и хаков ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Injection Codes - Delphi/C++/AutoIt ([Ссылки могут видеть только зарегистрированные и активированные пользователи])