Создание простого Auto-Potter'а

  • Автор темы Автор темы >A1RN1kE<
  • Дата начала Дата начала
  • Для просмотра чата и остального функционала вам нужно авторизоваться или пройти регистрацию!
A

>A1RN1kE<

Original poster
Добрый день!
В этой статье я расскажу о том, как создать самый простой Auto-Potter для Perfect World. Итак, приступим.

Для начала опишем функции, которые мы будем использовать:


  • FindWindow(ClassName, WindowName: PChar): HWnd; ClassName - Имя класса окна (заканчивающееся пустым символом, nil - если все классы). WindowName - Текстовый заголовок окна или 0, если все окна. С помощью этой функции мы будем искать Handle окна клиента.
  • GetWindowThreadProcessId(hWnd: HWND; lpdwProcessId: Pointer = nil): DWORD; stdcall; overload; hWnd - Дескриптор окна. lpdwProcessId - Указатель на переменную типа DWord, после использования функции в него скопируется идентификатор потока создавшего окно.
  • OpenProcess(dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwProcessId: DWORD): THandle; stdcall; dwDesiredAccess - Устанавливает права доступа к объекту (мы будем получать полные права доступа PROCESS_ALL_ACCESS)/ bInheritHandle - Параметр дескриптора наследования. dwProcessId - идентификатор потока. С помощью этой функции мы получим права доступа к памяти объекта.
  • ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: DWORD): BOOL; stdcall; hProcess - Идентификатор объекта lpBaseAddress - Указатель на адрес из которого будем читать lpBuffer - Указатель на переменную-буфер, в которую будем читать значение из памяти. nSize - Количество байт, которое мы хотим прочитать. lpNumberOfBytesRead - Переменная-буфер, в которой устанавливается значение соответствующее количеству прочитанных байт. С помощью этой функции мы будем "подбираться" к нужному нам адресу.
  • SendMessage(Wnd: HWnd; Msg, wParam: Word; lParam: Longint): Longint; Wnd - Окно, пpинимающее сообщение. Msg - Тип сообщения. (В нашем случае WM_KEYDOWM и WM_KEYUP - нажатие клавиши). wParam - Дополнительная инфоpмация о сообщении. (В нашем случае код виртуальной клавиши). lParam - Дополнительная инфоpмация о сообщении. (В нашем случае 0). С помощью этой функции мы будем посылать нажатие клавиши в окно клиента.

Прежде чем перейти к кодингу мы определим, как будет действовать наш Auto-Potter. В данном случае, дабы не нагромождать статью, мы воспользуемся самым простым путём:
Таймер будет обновлять показатели HP и MP на двух Gauge, и, если их процентное значение меньше требуемого, то происходит отправка нажатия клавиши в окно клиента.

Переходим к кодингу:

Создаем новый проект Delphi 7. На форму кидаем такие компоненты:

  • Timer (Вкладка System)
  • Gauge (Вкладка Samples) - 2 шт.
  • Edit (Вкладка Standart) - 2 шт.
  • Button (Вкладка Standart) - 2 шт.
Разместите компоненты примерно так:
as019.radikal.ru_i639_1204_ce_c50826a754a4.jpg

Теперь поясню, для чего нужен каждый из них:
  • Timer - основа нашей программы. Служит для обновления значений ХП и МП. (Свойство Enabled := False; Interval := 10);
  • Кнопки Вкл и Выкл служат для включения и выключения Auto-Pottera.
  • Два Gauge - мониторинг ХП и МП.
  • Два Edit'a - значение ХП(1) и МП(2), при котором посылаем нажатие клавиши.

Перед кодингом мы должны знать оффсеты. На момент написания статьи это:
HP: [[[$B280C4]+$34]+$490]
MaxHP: [[[$B280C4]+$34]+$4D0]
MP: [[[$B280C4]+$34]+$494]
MaxMP: [[[$B280C4]+$34]+$4D4]
Теперь переключаемся в редактор кода и пишем всё по порядку:
  • Объявим несколько глобальных переменных для удобства:
    Код:
    KlientWindow:HWND; //Handle клиента
    ProcessId:Integer;
    hProcess:Integer; //Идентификатор объекта
    HPMinValue:Integer; //Минимальное значение ХП (из Edit1) 
    MPMinValue:Integer; //Минимальное значение МП (из Edit2)
  • Для начала создаем обработчик события кнопки Вкл - OnClick:
    Код:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    KlientWindow := FindWindow(nil, PChar('Perfect World')); //Находим Handle окна
    GetWindowThreadProcessId(KlientWindow,@ProcessId); //Получаем Ид.П.
    hProcess := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId); //Открываем процесс с полным доступом
    HPMinValue := StrToInt(Edit1.Text);
    MPMinValue := StrToInt(Edit2.Text);
    Timer1.Enabled := True;
    end;
  • Теперь переходим к нашему Timer - создаем обработчик события OnTimer:
    Код:
    procedure TForm1.Timer1Timer(Sender: TObject);
    var HP,HPMax,MP,MPMax:Integer;
    WHP,WHPMax,WMP,WMPMax,NoB:DWord;
    begin
    try //На всякий случай заключаем в try...except, дабы в случай релога не засыпать пользователя ошибками
    ReadProcessMemory (hProcess, Pointer($B280C4), @WHP, sizeof(WHP), NoB);
    ReadProcessMemory (hProcess, Pointer(WHP+$34), @WHP, sizeof(WHP), NoB);
    ReadProcessMemory (hProcess, Pointer(WHP+$490), @WHP, sizeof(WHP), NoB); //Читаем значение HP
    HP := Integer(WHP); //Переводим Integer
    ReadProcessMemory (hProcess, Pointer($B280C4), @WHPMax, sizeof(WHPMax), NoB);
    ReadProcessMemory (hProcess, Pointer(WHPMax+$34), @WHPMax, sizeof(WHPMax), NoB);
    ReadProcessMemory (hProcess, Pointer(WHPMax+$4D0), @WHPMax, sizeof(WHPMax), NoB); //Читаем значение MaxHP
    HPMax := Integer(WHPMax); //Переводим Integer
    ReadProcessMemory (hProcess, Pointer($B280C4), @WMP, sizeof(WMP), NoB);
    ReadProcessMemory (hProcess, Pointer(WMP+$34), @WMP, sizeof(WMP), NoB);
    ReadProcessMemory (hProcess, Pointer(WMP+$494), @WMP, sizeof(WMP), NoB); //Читаем значение MP
    MP := Integer(WMP); //Переводим Integer
    ReadProcessMemory (hProcess, Pointer($B280C4), @WMPMax, sizeof(WMPMax), NoB);
    ReadProcessMemory (hProcess, Pointer(WMPMax+$34), @WMPMax, sizeof(WMPMax), NoB);
    ReadProcessMemory (hProcess, Pointer(WMPMax+$4D4), @WMPMax, sizeof(WMPMax), NoB); //Читаем значение MaxMP
    MPMax := Integer(WMPMax); //Переводим Integer
    Gauge1.Progress := round(HP * 100 / HPMax);  //Присваиваем процентное значение HP Gauge1.Progress
    Gauge2.Progress := round(MP * 100 / MPMax);  //Присваиваем процентное значение MP Gauge2.Progress
    except
    end;
    if Gauge1.Progress < HPMinValue then //Если HP меньше минимального
    begin
    SendMessage(KlientWindow, WM_KEYDOWN, VK_F1, 0); //то нажимаем
    SendMessage(KlientWindow, WM_KEYUP, VK_F1, 0); //клавишу F1
    end;
    if Gauge2.Progress < MPMinValue then //Если MP меньше минимального
    begin
    SendMessage(KlientWindow, WM_KEYDOWN, VK_F2, 0); //то нажимаем
    SendMessage(KlientWindow, WM_KEYUP, VK_F2, 0); //клавишу F2
    end;
    end;
  • И завершаем это всё обработчиком события кнопки Выкл - OnClick:
    Код:
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    Timer1.Enabled := False;
    end;
Ну вот и всё, простейший Auto-Potter готов. Исходник в аттаче. Спасибо за внимание!
Особая благодарность ''Хакерок:)''
 

Вложения

  • Like
Реакции: Эрнест
А как искать эти оффсеты, написать можешь?
 
Сверху Снизу