Данный гайд является основоположником серии гайдов посвященным созданию бота/ботов для игры 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.
Для просмотра ссылок или изображений в подписях, у Вас должно быть не менее 10 сообщение(ий). Сейчас у Вас 0 сообщение(ий). - Быстрое снятие и загрузка скриншотов на хостинг.
Dunя, в самом верху файла класса, где прописаны все using'и, добавляем новый
Код:
using System.Runtime.InteropServices;
Затем в теле класса добавляем метод, который будет вызываться из библиотеки Kernel32.
Код:
// Импортируем функцию для чтения памяти чужого процесса
// из kernel32
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[In, Out] byte[] buffer,
UInt32 size,
out IntPtr lpNumberOfBytesRead
);
А потом используем его по обстоятельствам
________________
Для просмотра ссылок или изображений в подписях, у Вас должно быть не менее 10 сообщение(ий). Сейчас у Вас 0 сообщение(ий). - Быстрое снятие и загрузка скриншотов на хостинг.
Привет,
TBX1n помоги пожалуйсто с чтением других значений...
оффсеты вроде есть... (http://zhyk.ru/forum/showthread.php?t=116673)
и читает норм численые значения типа хп/мп перса... а вот текст или флоат не фочет...
никак не разберусь...
как получить имя перса, моба, хп/мах хп моба и так далее...
заранее спасибо за любую инфу...
(Я в Си Шарп новенкий, не смейтесь.... не издевайтесь )
кстати спасибо за ранее написаное, там всё ясненько было
[DllImport("kernel32.dll")]
private static extern Int32 ReadProcessMemory(
IntPtr hProcess,
Int32 lpBaseAddress,
[In, Out] byte[] buffer,
Int32 size,
out int lpNumberOfBytesRead
);
private Single ReadFloatFromMemory(int address)
{
int read = 0;
byte[] buffer = new byte[8];
ReadProcessMemory(ProcessHandle, address, buffer, 8, out read);
return BitConverter.ToSingle(buffer, 0);
}
private String ReadStringFromMemory(int address, int length)
{
int read = 0; string rtnStr = string.Empty;
byte[] buffer = new byte[length];
ReadProcessMemory(ProcessHandle, address, buffer, length, out read);
UnicodeEncoding enc = new UnicodeEncoding();
rtnStr = enc.GetString(buffer);
return rtnStr.Substring(0, rtnStr.IndexOf('\0'));
}
Вот мои 2 метода, для чтения Float и String из памяти клиента.
При чтении строки, размер мы явно не знаем и читает с запасом, но чтобы лишний мусор не выципить, я обрезаю строку до первого нулевого символа. По хорошему не мешает добавить проверку, существует ли вообще нулевой символ, а потом до него резать.
________________
Для просмотра ссылок или изображений в подписях, у Вас должно быть не менее 10 сообщение(ий). Сейчас у Вас 0 сообщение(ий). - Быстрое снятие и загрузка скриншотов на хостинг.
Данные наш бот будет получать из памяти клиента PW. Для этого нам придется прибегнуть к функции ReadProcessMemory из библиотеки kernel32.
.NET Framework позволяет нам подключать и использовать методы из библиотек написанных не на управляемом коде (managed code).
непонятно((( и у меня нету System.Runtime.InteropServices. в ссылках
Здравствуйте, нашел baseadress и offset hp и mp
Взял для базы этот код и применил для Runes of magic
А он всегда выдает 0\0 и 0\0.
В чем может быть беда?
У меня еще есть 1 offset который я не где не указываю 0x598, может дело в этом?
Спасибо
Вот код.
Код:
// Объявление события
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 = 0x5BC394,
offsetHP = 0x2C8,
offsetMP = 0x2D4,
offsetMaxHP = 0x2D0,
offsetMaxMP = 0x2D8;
// Служебная переменная для хранения 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);
}