Перейти к содержанию

Создаем Трейнер В Delphi, Используя Winapi


Рекомендуемые сообщения

В этом учебнике я собираюсь обрисовать основной API, необходимый для создания трейнера в Дельфи. Основы знания Дельфи предпочтительны, но Дельфи итак довольно прост в освоении.

Концепция.

Хорошо, вот что мы хотим от трейнера. Мы запускаем игру. После этого ALT+TAB в Windows. Мы запускаем трейнер, и жмем кнопку. Это действие запишет некие значения в некоторые адреса в игре. Так, например, если мы знаем адрес денег в памяти игры, мы сможем хакать деньги, используя этот трейнер.

Вот что нам надо для этого:

Название окна игры

Запускаем игру, потом переходим в Windows по Alt+Tab. Ищем в панели задач нашу игру и записываем е_ точный заголовок. (К примеру, запустив Red Alert 2, в панели задач Вы увидите кнопку с ее названием - Red Alert 2. Это и есть заголовок главного окна программы. Кстати, Red Alert 2 взломать способом, описанным здесь, не удастся - это DMA игра. Читайте пару документов здесь, посвященных именно A.G.T. и борьбе с DMA)

Адреса в памяти игры (в шестнадцатеричном виде)

Используем программу, подобную GameHack или MTC (Magic Trainer Creator), мы можем найти любое значение в игре и соответствующий ему адрес в памяти. К примеру, адрес в шестнадцатеричном виде 41D090. Запишем и это тоже.

Значение, которое мы хотим записать (в шестнадцатеричном виде):

Так, у нас есть адрес в памяти. Что мы хотим в него записать? Скажем, я хочу 50 единиц золота. То есть первым делом мне надо перевести 50 в шестнадцатеричную форму, используя соответствующий конвертер (подойдет и Калькулятор из Стандартных программ Windows - не забудьте включить инженерное представление - прим.пер.) Конвертер скажет 32. Так что запишите и это значение также.

Число байт, которое мы хотим писать

В том значении, которое мы получили выше, мы должны знать также сколько байт это займет в памяти. К примеру, число 32 займет только 1 байт, но FF07 займет уже два байта. В общем случае, две цифры будут занимать один байт.

Начнем кодить

Мы собираемся использовать Win32 API чтобы записывать значения в память другого процесса. Вот те функции, которые мы будем использовать. По порядку:


FindWindow
GetWindowThreadProcessID
OpenProcess
ReadProcessMemory
WriteProcessMemory
CloseHandle

(Прочтите описания этих функций в файле Win32.hlp (или MSDN - прим.пер.) для полного описания. )

Я буду показывать только основы, так что начинающие могут просто копировать код из этого документа и вставлять его в свой проект.)

Итак, начало. Во-первых, мы объявляем наши переменные. Скопируйте и вставьте это в свой проект:


Var WindowName : integer;
ProcessId : integer;
ThreadId : integer;
buf : PChar;
HandleWindow : Integer;
written : cardinal;

Теперь надо объявить следующие константы. Скопируйте и этот раздел. Эти константы устанавливаются в соответствии с тем, что вы записали выше.


Const WindowTitle = 'prog test';
Address = $41D090;
PokeValue = $32;
NumberOfBytes = 1;

Теперь, чтобы записать значения, вы должны получить хэндл памяти игры. Невозможно сделать это в одно действие, поэтому мы сделаем следующее.

Получаем хэндл главного окна

С этим хендлом мы получаем идентификатор процесса (process identifier - pID)

С этим pID, мы получаем хэндл области памяти.

С этим хэндлом мы можем начинать хакать.

Во-первых, нам надо получить хэндл главного окна. Используем функцию FindWindow


WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageDlg('Игра должна быть запущена до трейнера.
Запустите ее, потом трейнер', mtwarning,[mbOK],0);
end;

Заметим, что код проверяет, равен ли нулю хэндл этого окна. Если оно равно, это значит, что игра не запущена, так что мы предупреждаем пользователя и говорим ему о том, чтобы он запустил игру.

Теперь нам нужен pID. Мы используем функцию GetWindowThreadProcessId. После этого мы получаем хэндл области памяти через OpenProcess. Скопируйте код, приведенный ниже.


ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);

Вот оно. Теперь нам надо использовать WriteProcessMemory чтобы писать что-то внутри этого хэндла. Как только мы это сделаем, мы закрываем хэндл. Так принято. Так безопасно. Скопируйте код, приведенный ниже:


GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);
CloseHandle(HandleWindow);

Вот исходный код для всего трейнера. Для начинающих программистов, чтобы быстро сделать трейнер, требуется только поменять константы, объявленные в начале программы.


Var WindowName : integer;
ProcessId : integer;
ThreadId : integer;
buf : PChar;
HandleWindow : Integer;
write : cardinal;
Const WindowTitle = 'prog test';
Address = $41D090;
PokeValue = $32;
NumberOfBytes = 1;

###########################################################
# (Вставьте следующий код в обработчик OnClick кнопки )#
###########################################################

begin

WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageDlg('Игра должна быть запущена до трейнера.
Запустите ее, потом трейнер', mtwarning,[mbOK],0);
end;

ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);

GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);
CloseHandle(HandleWindow);
end;

  • Плюс 3
Ссылка на комментарий
Поделиться на другие сайты

Я уже не раз и не в одной теме на нашем форуме писал, что указанных функций мало для создания трейнера к современым играм. Необходимо обязательно упомянуть о функции VirtualProtectEx, позволяющей менять защиту взламываемой памяти процесса.

Ссылка на комментарий
Поделиться на другие сайты

Вот оно.

трейнер моей мечты :lol:

ps. какое же это извращение писать на дельфи ^^) я как после ФГС стал считать за богов девушек, так и после того как мне два последние года учебы парили в школе паскаль я стал считать за богов тех у кого хватает терпения прописывать эти var, begin и прочую муть, поэтому в 11 классе я был максимум на двух уроках информатики.

Ссылка на комментарий
Поделиться на другие сайты

Чисто понять основы + попрактиковаться на сапёре\косынке - вполне. Не хватает ещё архива с собранным трейнером и его прокомментированными исходниками.

  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Ни на что. Надо полученное значение в буфере сконвертировать в текст. Функции из SysUtils помогут (судя по коду, ты используешь Дельфи), или же их заменители - тут вопрос к гуглу.

Ссылка на комментарий
Поделиться на другие сайты

Еще вопрос :) Почему на некоторых ОС не работает трейнер? Т.е. адреса все совпадают, но на нажатие кнопки никак не реагирует?

Тут что угодно может быть - нужно отлаживать. Отдельные компоненты могут не работать в разных осях, но WinAPI у нас на все винды в принципе одинаковые.

Ссылка на комментарий
Поделиться на другие сайты

Еще вопрос :) Почему на некоторых ОС не работает трейнер? Т.е. адреса все совпадают, но на нажатие кнопки никак не реагирует?

Нужно больше конкретизировать. Мы же не телепаты.

Какой трейнер и для какой игры? Есть ли исходный код? Какие операционные системы? Как было определено, что адреса совпадают, когда нажатия кнопок не срабатывают?

Ссылка на комментарий
Поделиться на другие сайты

var
WindowName: integer;
ProcessId: integer;
ThreadId: integer;
HandleWindow: Integer;
write: cardinal;
buf: dword;
const
WindowTitle = 'Name';

Address = $007B5678;
NumberOfBytes = 4;
begin
WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then begin
MessageDlg('Игра должна быть запущена до трейнера. Запустите ее, потом трейнер', mtwarning,[mbOK],0);
end;
ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
buf:=$0;
WriteProcessMemory(HandleWindow, ptr(address), @buf, 4, write);
end;

Можно и в таком варианте:

var
WinClass : TWndClass;
hInst: HWND;
Handle: HWND;
Msg: TMSG;
hFont: HWND;
win: hwnd;
WindowName: integer;
ProcessId: integer;
ThreadId: integer;
buf: PChar;
HandleWindow: Integer;
write: cardinal;
const
WindowTitle= 'Name';
Address= $007B5678;
PokeValue= $0;
NumberOfBytes= 4;
begin
WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageBox(win,'Игра должна быть запущенна до трейнера.'+#13+#10+
' Запустите её,а потом трейнер','Ошибка',MB_OK or MB_ICONINFORMATION);
end;

ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);

GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);
CloseHandle(HandleWindow);
end;

У большинства людей работает.

Не работает на: Win 7 x32 Максимальная, Win 7 x64 Ultimate

Работает на: Win XP SP2 x32, Win XP SP3 x32, Win 7 x64 Домашняя, Win 7 x64 Максимальная...

Адреса были проверены через CheatEngine, соответственно на компьютере где не работает.

Ссылка на комментарий
Поделиться на другие сайты

А как именно не работает? Как ошибка вылазит, если вылазит?

1) Если вылазит сообщение "Игра должна быть запущенна до трейнера", то может быть в названии окна есть какие-то проблемы с русскими символами, если они там есть.

2) Сделай аналогичные сообщения для проверки ProcessId, Handlewindow.

Проверь WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write) на исключение. Если исключение возникает выведи мессагу. Возможно у адреса защита от записи. Или блокировка Антивирусом.

3) Потом мне не нравится GetMem c переменной buf, т.е. вот эти манипуляции.

GetMem(buf,1);

buf^ := Chr(PokeValue);

//...

FreeMem(buf);

Проверь существует ли действительно адрес buf

4) Я точно не помню, но кто знает, может WriteProcessMemory не успевает выполнится по каким-то причинам и тут же идёт сразу FreeMem(buf) и CloseHandle(HandleWindow);

5) Может быть нужно какие-то параметры компиляции поставить. 6) Может быть требуется присутствие каких то dll-ок. См. пункт 5

Короче поставь мессаги на каждой функцией. И обверни блок кода от begin до end в обработку исключения с выводом мессаги о об этом исключении. Можно также извлечь информацию об этом исключении используя WinAPI. К сожалению я не буду заниматься тестированиями. Подсказал чем смог. Ещё предлагаю поискать примеры на дельфи без  GetMem ->  FreeMem...

Ссылка на комментарий
Поделиться на другие сайты

Я написал 2 примера кода, ни 1 из них на указанных ОС не работают.

п. 1 не подходит, не понял пункты 3, 5 и 6 :) а п.4 не думаю что не успевает, если только на некоторых ОС не работает, антивируса нету.

Ссылка на комментарий
Поделиться на другие сайты

Я написал 2 примера кода, ни 1 из них на указанных ОС не работают.

п. 1 не подходит, не понял пункты 3, 5 и 6 :) а п.4 не думаю что не успевает, если только на некоторых ОС не работает, антивируса нету.

Просто поставь такую же проверку, как в функции FindWindow (через MessageBox), после вызова всех функций. Где ошибку выдаст - там её и искать.

Ссылка на комментарий
Поделиться на другие сайты

  WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageBox(win,'Игра должна быть запущенна до трейнера.'+#13+#10+
' Запустите её,а потом трейнер','Ошибка',MB_OK or MB_ICONINFORMATION);
end;

ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
If ThreadID := 0
then begin
ShowMessage('ThreadID.');
end;
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
if HandleWindow = 0
then begin
Show Message('HandleWindow');
end;

Как я понял эту часть надо сделать так, а дальше как сделать? Можно код плиз?

GetMem(buf,1);

buf^ := Chr(PokeValue);

WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);

FreeMem(buf);

Ссылка на комментарий
Поделиться на другие сайты

Что-то типа:


var
WinClass : TWndClass;
hInst: HWND;
Handle: HWND;
Msg: TMSG;
hFont: HWND;
win: hwnd;
WindowName: integer;
ProcessId: integer;
ThreadId: integer;
buf: PChar;
HandleWindow: Integer;
write: cardinal;
const
WindowTitle= 'Name';
Address= $007B5678;
PokeValue= $0;
NumberOfBytes= 4;
begin
WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageBox(win,'Игра должна быть запущенна до трейнера.'+#13+#10+
' Запустите её,а потом трейнер','Ошибка',MB_OK or MB_ICONINFORMATION);
end;

ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
If ThreadId = 0 then
begin
MessageBox(win,'Error!'+#13+#10+
'Couldn't get thread id.','Ошибка',MB_OK or MB_ICONINFORMATION);
end
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
If HandleWindow = 0 then
begin
MessageBox(win,'Error!'+#13+#10+
'Couldn't get window handle.','Ошибка',MB_OK or MB_ICONINFORMATION);
end
GetMem(buf,1);
buf^ := Chr(PokeValue);
If buf = 0 then
begin
MessageBox(win,'Error!'+#13+#10+
'Buffer is empty.','Ошибка',MB_OK or MB_ICONINFORMATION);
end
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);
CloseHandle(HandleWindow);
end;

Вообще, не пробовал под отладчиком пошагово пройти всю программу?

Ссылка на комментарий
Поделиться на другие сайты

GetMem(buf,1);

buf^ := Chr(PokeValue);

If buf = 0 then

begin

MessageBox(win,'Error!'+#13+#10+

'Buffer is empty.','Ошибка',MB_OK or MB_ICONINFORMATION);

end;

На выделенную строчку ругается... Operator not applicable to this operand type

Ссылка на комментарий
Поделиться на другие сайты

Я не совсем понимаю, что делают эти строчки:

GetMem(buf,1);
buf^ := Chr(PokeValue);

GetMem выделяет 1 байт памяти и кладёт указатель на начало этой памяти в buf. После этого в buf кладётся сконвертированный при помощи Chr нолик. Затем всё это добро записывается по адресу 007B5678. Я правильно понимаю, да? Если да - зачем такие махинации с выделением одного байта и т.п.? Не проще сразу ноль записать?

Ссылка на комментарий
Поделиться на другие сайты

Ещё можно [вот такой] вариант попробовать. 4-й пост сверху. Первая процедура ищет хэндл нужного процесса, вторая - пишет указанные данные по указанному адресу. В конце поста показано, как это дело вызывать.

Ссылка на комментарий
Поделиться на другие сайты

Я просто код копировал с 1 поста этой темы, не заметил 1 в GetMem(buf,1);

Если я правильно понимаю надо 1 поменять на 4 ? или вообще убрать эту часть кода?

Ссылка на комментарий
Поделиться на другие сайты

Попробуй вместо:

GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);

Написать сразу:

WriteProcessMemory(HandleWindow,ptr(Address),$00,1,write);

Ссылка на комментарий
Поделиться на другие сайты

Значит твой код верен, но отлаживать его всё равно нужно. На всех ОСях трейнер запускался с админскими правами? У меня просто под рукой компилятора нет, да и дельфи я последний раз видел лет 10 назад.

Ссылка на комментарий
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.