Пишем простой Auto-Potter для Perfect World [Delphi 7]
Пишем простой Auto-Potter для Perfect World
Доброго времени суток! Решил написать эту статью, т.к. наткнулся на эту тему и меня попросили. В этой статье я расскажу о том, как создать самый простой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 шт.
Разместите компоненты примерно так:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Теперь поясню, для чего нужен каждый из них:
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 готов. Исходник в аттаче. Спасибо за внимание!
Копирование статьи разрешено только с обязательной ссылкой на источник!
Re: Пишем простой Auto-Potter для Perfect World [Delphi 7]
Цитата:
Сообщение от BritishColonist
Только с цветом текста немного переборщил.
С каким цветом текста? Что переборщил? Что-то я не въехал...
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов
Кошельки: WMZ - Z276844220882; WMR - R231028582939; WMU - U394136909210; ЯД - 410011494605270.
Re: Пишем простой Auto-Potter для Perfect World [Delphi 7]
Цитата:
Сообщение от Хакерок:)
Это он имеет ввиду перебор с оранжевым цветом текста.
Ну, по моему, тут нет никакого перебора. Вся статья написана приятными для глаза цветами шрифта...
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов
Кошельки: WMZ - Z276844220882; WMR - R231028582939; WMU - U394136909210; ЯД - 410011494605270.
Re: Пишем простой Auto-Potter для Perfect World [Delphi 7]
Цитата:
Сообщение от Хакерок:)
HP: [[[$B280C4]+$34]+$490]
Прошу прощения за нубство, я понимаю что значит например $490, но вот что значит $34 и $B280C4 не понимаю))) Догадываюсь, что $B280C4 это Base Address, ну а про $34 вообще ничего)))
________________ Задание:⊗ Репутация 10.
Ο Репутация 50.
Ο Репутация 150.
Re: Пишем простой Auto-Potter для Perfect World [Delphi 7]
Цитата:
Сообщение от Fahbrf
Прошу прощения за нубство, я понимаю что значит например $490, но вот что значит $34 и $B280C4 не понимаю))) Догадываюсь, что $B280C4 это Base Address, ну а про $34 вообще ничего)))
Skuka.95 тебе уже ответил, но если хочешь узнать подробнее, то тут тема TBX1n. Оффсеты там старые, но проще понять всё, что касается их.
BA - BaseAddress - Базовый адрес
GA - GameRun - начало игровой структуры
GA = [BA] + $1C
Структура игрока: [GA] +$34
Ну и следовательно значение ХП: [[[GA] + $34] + $490]
function ReadPlayerName(hProcess,data:DWord): string;
var
i,rw:DWord;
ch:WideChar;
wch:array[0..255] of WideChar;
str:string;
begin
i:=0;
repeat
ReadProcessMemory(hProcess,ptr(data),@ch,2,rw);
data:=data+2;
wch[i]:=ch;
inc(i);
until
(ord(ch)=0) or (i>=255);
i:=0;
str:='';
repeat
str:=str+wch[i];
inc(i);
until
wch[i]='';
result:=str;
end;
procedure WritePlayerName(hProcess,dat:DWord);
var
i,rw:DWord;
ch:WideChar;
wch:array[0..255] of WideChar;
begin
i:=0;
StringToWideChar(Nm,wch,255);
i:=0;
repeat
ch:=wch[i];
WriteProcessMemory(hProcess,ptr(dat),@ch,2,rw);
dat:=dat+2;
inc(i);
until
(ord(ch)=0) or (i>=255);
end;
Если нужны объяснения, то я готов объяснить всё в этой теме...
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов
Кошельки: WMZ - Z276844220882; WMR - R231028582939; WMU - U394136909210; ЯД - 410011494605270.
Потому что при нажати на кнопку происходит изменение имени в Юзербаре игрока, без изменения имени окна. В остальном все хорошо работает!! Самое главное что есть примерная структура построения программы!
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов
Кошельки: WMZ - Z276844220882; WMR - R231028582939; WMU - U394136909210; ЯД - 410011494605270.