Перейти к содержанию
Авторизация  
24K

Создаем Трейнер В 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

Поделиться сообщением


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

Как можно сделать программу, чтобы, к примеру, в Label показывалось мое кол-во жизней в игре?

PS: Адрес есть...

Поделиться сообщением


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

Таймер - ReadProcessMemory - Конвертация в нужный формат - Вывод на Label, например.

Поделиться сообщением


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

Разобрался, спс. А как можно в текст вывести?

ReadProcessMemory(HandleWindow, ptr($01162420), @bb, 4, wread);

На что это поменять надо?

Поделиться сообщением


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

Ни на что. Надо полученное значение в буфере сконвертировать в текст. Функции из 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 назад.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Гость
Эта тема закрыта для публикации ответов.
Авторизация  

×

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

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