[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Модератор форума: LordOfDark, NightBringer  
Пишем трейнеры.
ДефиалтусДата: Воскресенье, 17.02.2008, 09:05 | Сообщение # 1
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Трейнер на Delphi с использованием WinAPI

Вступление

Итак, в данном учебнике я попробую обьяснить и рассказать, как в Delphi пишутся трейнеры. Вы наверное сразу же подумаете, что писать трейнеры на Delphi убого,т.к. одна пустая форма в скомпилированном состоянии весит аж 300 Кб. Но я постараюсь разрушить этот миф. Писать наш трейнер мы будем без VCL. Но как же так, скажете вы? - Наш будуший трейнер будет написан на чистом WinAPI.
Приведу в свою защиту несколько фактов:

При написании программ на чистом WinAPI,скомпиленный *.exe будет весить ~15 кб (какие перспективы открываются перед нами).

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

Один, но существенный минус - нам придётся описывать всё вручную. Но мы с вами живём в век высоких технологий (хе-хе). В решении нашей задачи нам поможет программа под названием APIx. При помощи данной программы мы сможем спроектировать своё приложение как и в Delphi, но на чистом WinAPI.
Как говорится, "меньше слов, ближе к делу".

Кодинг

Я постараюсь как можно подробнее описать весь код нашего будущего трейнера. Поехали !

//Немного оптимизации
{$R-} {проверка диапазона}
{$S-} {проверка стека}
{$A+} {"выравнивание слов"}
program FlatOut;
uses
windows, messages, commctrl; //Используемые модули,только самое нужное!
var
WinClass : TWndClass; //переменная класса TWndClass для создания главного окна
hInst : HWND; //Хендл приложения
Handle : HWND; //локальный хендл
Com1 : HWND; //TGroupBox
Com2 : HWND; //TButton
Com3 : HWND; //TButton
Com4 : HWND; //TStaticText
Com5 : HWND; //TStaticText
Com6 : HWND; //TStaticText
Msg : TMSG; //сообщения
hFont : HWND; //хендл шрифта
win : hwnd; //хендл данного окна
var
WindowName : integer; //имя окна
ProcessId : integer; //ID процесса
ThreadId : integer; //Поток
buf : PChar;
HandleWindow : Integer; //хендл окна игры
write : cardinal;
const //id наших контролов
id_1 = 1; //TGroupBox
id_2 = 2; //TButton
id_3 = 3; //TButton
id_4 = 4; //TStaticText
id_5 = 5; //TStaticText
id_6 = 6; //TStaticText
const
WindowTitle = 'Flat-Out'; //конец формы, начало формы, точный заголовок игры
Address = $01B40C64; //адрес нашего значения в памяти игры
PokeValue = $FFFFFFFF; //значение на которое мы будем менять
NumberOfBytes = 4; //кол-во байт
{$R XPMan.res} //Здесь у меня лежит иконка моего трейнера и манифес,
//это для того что бы все контролы были в стиле XP
procedure Cheating; //Собственно сама процедура изменения значения
begin
WindowName := FindWindow(nil,WindowTitle); //находим окно игры
If WindowName = 0 then //или,обьясняем пользователю чтобы
begin //он запустил игру
MessageBox(win,'Вначале игра,а потом трейнер.','Ошибка',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;
procedure ShutDown; //процедура выхода из программы
begin
DeleteObject(hFont); //удаляем шрифт
UnRegisterClass('Sample Class', hInst); //удаляем окно
ExitProcess(hInst); //закрываем окно
Halt; //на всякий случай smile
end;
procedure About; //наше окно о программе
begin
MessageBox(win, ' [C0DED]: bY g-l-u-k [TeaM - X] ' +#13#10+
' ' +#13#10+
' GreatZzz....: ' +#13#10+
' ' +#13#10+
' Baron_Gede,6aHguT,AllexY ' +#13#10+
' And all TeaM - X Members ! ' +#13#10+
' ' +#13#10+
' Write on pure Delphi (WinAPI) ' +#13#10+
' Сopyright (g-l-u-k)R 2004-2005 ' +#13#10+
' ' +#13#10+
' http://www.team-x.ru ' +#13#10+
' e-mail : g-l-u-k@rambler.ru ' +#13#10+
' ' +#13#10+
' GEngine v0.1 ' +#13#10+
' All Right Reserved ',
'About',MB_OK or MB_ICONINFORMATION);
end;
function WindowProc(hwnd, msg, wparam, lparam: longint): longint; stdcall; //обработчик сообщений
begin
Result := DefWindowProc(hwnd, msg, wparam, lparam);
case Msg of
WM_COMMAND:
case LoWord(wParam) of
id_2 :
if HiWord(wParam) = bn_Clicked then
About; //если пользователь нажимает на кнопку "About",получат свой About
id_3 :
if HiWord(wParam) = bn_Clicked then
ShutDown; //если выход то......
end;
WM_DESTROY:
ShutDown;
end;
end;
begin
hInst := GetModuleHandle(nil);
with WinClass do
begin
Style := CS_PARENTDC; //стиль класса главного окна
hIcon := LoadIcon(hInstance, IDI_APPLICATION); //иконка программы
lpfnWndProc := @WindowProc; //назначение обработчика сообщений
hInstance := hInst;
hbrBackground := COLOR_BTNFACE + 1; //цвет окна
lpszClassName := 'Sample Class'; //класс окна
hCursor := LoadCursor(0, IDC_ARROW); //активный курсор
end;
InitCommonControls;
RegisterClass(WinClass); //регистрация класса в сис-ме
{Создание главного окна программы}
Handle := CreateWindowEx(0, 'Sample Class', '[FlatOut] Trainer +1',
WS_OVERLAPPED or WS_SYSMENU or
WS_VISIBLE,
503, 345, 234, 222,
0, 0,
hInst, nil);
{Создание шрифта}
hFont := CreateFont(
-12, 0, 0, 0, 0, 0, 0, 0,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH or FF_DONTCARE, 'Terminal');
Com1:=CreateWindow(
'Button',
'Trainer Options:' ,
WS_CHILD or BS_GROUPBOX or WS_VISIBLE,
2,2,222,165, Handle, id_1, hInst,nil);
SendMessage(Com1,WM_SETFONT,hFont,0);
Com2 := CreateWindow(
'Button',
'About',
WS_CHILD or BS_TEXT or WS_VISIBLE,
3, 171, 74, 20,Handle, id_2, hInst, nil);
SendMessage(Com2,WM_SETFONT,hFont,0);
Com3 := CreateWindow(
'Button',
'Quit',
WS_CHILD or BS_TEXT or WS_VISIBLE,
148, 171, 74, 20,Handle, id_3, hInst, nil);
SendMessage(Com3,WM_SETFONT,hFont,0);
Com4 :=CreateWindow(
'Static',
'[F1] :...: More Money' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE,
51,25,117,17,Handle, id_4, hInst,nil);
SendMessage(Com4,WM_SETFONT,hFont,0);
Com5:=CreateWindow(
'Static',
'[C0DED] :...: g-l-u-k [TeaM - X]' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE or WS_DISABLED,
32,145,180,17,Handle, id_5, hInst,nil);
SendMessage(Com5,WM_SETFONT,hFont,0);
Com6:=CreateWindow(
'Static',
'[R.Mouse] :...: About Box ' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE,
26,50,190,17,Handle, id_6, hInst,nil);
SendMessage(Com6,WM_SETFONT,hFont,0);
//Цикл сбора сообщений
while(GetMessage(Msg, Handle, 0, 0)) do
begin
TranslateMessage(Msg); //приём сообщений
if (GetAsyncKeyState(vk_f1 ) <> 0) then Cheating; //если нажата клавиша F1,читим игру
if (GetAsyncKeyState(vk_RButton) <> 0) then About; //если нажата правая кнопка мыши,то показываем About
if (GetAsyncKeyState(vk_Escape ) <> 0) then ShutDown;
DispatchMessage(Msg); //удаление сообщений из очереди
end;
end.

Как вы видите писать трейнеры в Delphi на чистом WinAPI не так уж и сложно. У меня размер EXE файла получился ~19 кб (Попробуйте меньше). В следующей статье я раскажу вам второй способ написания трейнера на WinAPI. Исходник трейнера ты сможешь скачать здесь


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

 
ДефиалтусДата: Воскресенье, 17.02.2008, 09:07 | Сообщение # 2
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Трейнер на Delphi с использованием WinAPI [часть 2]

Вступление

Вашему вниманию предоставляется вторая часть учебника о написании небольших трейнеров на Delphi c использованием WinAPI.Данная статья поможет тем,кто по каким-то причинам не разобрался или не понял,как пишутся маленькие по размеру трейнера из предыдущей части данного учебника.На сей раз мы будем писать наш трейнер на WinAPI с использованием ресурсов.Данный способ намного проше,чем первый,а так же я приведу пример как блокировать программы,которые шпионят за нашим трейнером.Опять-таки приведу аргументы в пользу данного метода написания трейнеров:

" Писать нам наш трейнер будет легче,так как работаем мы с ресурсами.Для этого нам нужен хороший редактор ресурсов (Restorator или ResHaker),я предпочитаю 2-ой. " По идеи данный способ позволит нам скостить ~ 1-1,5 кб.

В крайности я вдаваться не буду,так что приступим.

Кодинг

Для начала нам придётся создать в блокноте файл trainer.rc,который будет содержать следующие строки:

======================trainer.rc============================
100 DIALOG 0, 0, 173, 69
STYLE DS_SETFONT | DS_CENTER | WS_CAPTION | WS_SYSMENU
CAPTION "[FlatOut] Trainer +1"
FONT 8, "Terminal"
{
PUSHBUTTON "About", 102, 2, 52, 36, 15, BS_FLAT
PUSHBUTTON "Close", 101, 132, 52, 38, 15, BS_FLAT
GROUPBOX "Trainer options: ", -1, 1, -1, 172, 51
LTEXT "[C0DED] <--::--> bY g-l-u-k [TeaM - X]", -1, 7, 36, 164, 12, WS_DISABLED
LTEXT "[F1] :.......: More Money", -1, 7, 9, 126, 12, WS_DISABLED
}
======================trainer.rc============================

После создания этого файла перетащите его на brcc32.exe,который лежит в папке "Program Files\Borland\Delphi7\Lib".После этого получам trainer.res,который подлинкуем к нашему трейнеру.

program FlatOut; //Опять будем ломать FlatOut

uses
windows, messages; //Именно по этому наш трейнер должен весить
//меньше,если помните то в прошлой части учебника мы использовали //ещё и commctrl.

const
ID_ABOUT = 102; //Номера контролов нашего ресурса
ID_EXIT = 101;
Elapse = 10; //Нужен для таймера
aboutcap = 'About'; //Наш About Dialog
aboutmsg = ' [C0DED]: bY g-l-u-k [TeaM - X] ' +#13#10+
' ' +#13#10+
' GreatZzz....: ' +#13#10+
' ' +#13#10+
' Baron_Gede,6aHguT,AllexY ' +#13#10+
' And all TeaM - X Members ! ' +#13#10+
' ' +#13#10+
' Write on pure Delphi (WinAPI) ' +#13#10+
' copyright (g-l-u-k)R 2004-2005 ' +#13#10+
' ' +#13#10+
' http://www.team-x.ru ' +#13#10+
' e-mail : g-l-u-k@rambler.ru ' +#13#10+
' ' +#13#10+
' GEngine v0.1 ' +#13#10+
' All Right Reserved ';

WindowTitle = 'Flat-Out'; //Название окна игры
Address = $01B40C64;//Адресс нашего значения
PokeValue = $FFFFFFFF;//наше значение
NumberOfBytes = 4;//Кол-во байт

var
Msg : TMSG;
Win : HWND;
WindowName : Integer;
ProcessId : Integer;
ThreadId : Integer;
hInst : Dword;
Buf : PChar;
HandleWindow : Integer;
Write : Cardinal;

{$R trainer.res} //Наш ресурс в котором хранится окно трейнера

//Вот самая интересная часть нашего трейнера,процедура роверки.
//Если TrainerSPY активен,то мы обломим следящего за нашим //трейнером.
function IsTrainerSpyActive:bool;
var
hProcess,hKernel:dword;
addr:pointer;
b:byte;
dummy:cardinal;
proc:pchar;
begin
result:=false;
proc:='WriteProcessMemory';
if FindWindowExA(0,0,nil,'Trainer Spy')<>0 then
begin
result:=true;
exit;
end;

hProcess:=GetCurrentProcess;
hKernel:=LoadLibrary('kernel32.dll');
if hKernel<>0 then
begin
addr:=GetProcAddress(hKernel,proc);
ReadProcessMemory(hProcess,addr,@b,1,dummy);
FreeLibrary(hKernel);
if b=204 then result:=true;
end;
end;

//Процедура взлома игры,описывать полностью не буду,опишу только //самое главное.
procedure Cheating;
begin
if IsTrainerSpyActive then //если TrainerSpy запушен, то вырубаемся
begin
MessageBox(0,'Выруби шпион.','Ошибка зашиты',MB_OK or MB_ICONERROR);
exit;
end;
WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
begin
MessageBox(win,'Игра должна быть запушенна до трейнера','Ошибка',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;

procedure Quit; //процедура выхода из программы
begin
EndDialog(win,0);
Halt;
end;

procedure DoTimer; //Дополнительна зашита от шпионов
begin
CreateFileA('C:\logwmemory.bin',$40000000,1,nil,2,1,0);
hInst:= GetModuleHandle(nil);
SetTimer(hInst,1,Elapse,@DoTimer);
end;

//Обработчик событий нашего окна
function SettingsDlgProc(Window : hWnd; Msg,WParam,LParam : Integer): Integer; StdCall;
begin
case Msg of
wm_InitDialog : begin
end;
wm_Close : DestroyWindow(Win);
wm_Destroy : PostQuitMessage(0);
end;
Result := 0;
case Msg of
WM_COMMAND : begin
if wParam = ID_EXIT then Quit; //Ели нажата кнопка выхода,выходим
if wParam = ID_ABOUT then MessageBox(Win,aboutmsg,aboutcap,MB_OK or MB_ICONINFORMATION);//А это наш //about
end;
end;
end;

//Процедура создания главного окна
Procedure RunSettings;
begin
Win := CreateDialog(hInstance,PCHar(100),0,@SettingsDlgProc);
Showwindow(Win,SW_SHOW);
Updatewindow(Win);
end;
begin
RunSettings;
while GetMessage(Msg,0,0,0) do
begin
//Цикл сбора сообщений
TranslateMessage(Msg);
if (GetAsyncKeyState(VK_F1) <> 0) then Cheating;//Горячая клавиша
DispatchMessage(Msg);
end;

end.

В заключении данного учебника я ещё раз повторюсь, что писать трейнера на Delphi не только можно но и нужно. У меня трейнер получился ~19 кб. После сжатия FSG 2.0 вышло ~11 кб, ну это уже по божески. От себя я добавлю следующее, "Delphi самый простой в изучении язык программирования. "Надеюсь, что данным учебником я сумел опровергнуть следующее мнение, что "Delphi - это мусорогенеротор". Исходники данного трейнера можно скачать здесь


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

 
ДефиалтусДата: Воскресенье, 17.02.2008, 09:08 | Сообщение # 3
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Создаем трейнер в Дельфи, используя 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;


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

 
ДефиалтусДата: Воскресенье, 17.02.2008, 09:09 | Сообщение # 4
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Туториал по написанию трейнеров для DMA и не-DMA игр

Содержание:
1. Немного теории
2. Не-DMA игры
3. DMA игры - поиск поинтера
4. Пишем трейнер для:
4.1 Трейнер для не-DMA игры
4.2 Трейнер для DMA игры
4.3 Общие замечания
5. Заключение
6. Некоторые оговорки
7. Контакты и ссылки
8. Условия распространения и всё прочее

В этой статье я рассмотрю написание трейнера (trainer'а) для игры. В рунете совсем мало информации на эту тему, а про написание DMA-трейнеров я вообще ничего не нашёл (может быть, конечно, плохо искал), но в процессе написания мною была дана торжественная клятва, что когда закончу - обязательно напишу нормальный туториал на эту тему. Хотелось написать туториал, в котором бы затрагивалось в равной степени как теория, так и поиск поинтера и написание собственно трейнера.

Основную часть я намерен посвятить именно DMA играм.

Итак, что потребуется:

- Язык программирования. Я буду использовать Delphi для примеров. В принципе, подойдёт любой - для работы с памятью процесса нам потребуются только WinAPI функции.

- Программа типа ArtMoney (чтобы искать значения в памяти). Настоятельным образом рекомендую TSearch (несмотря даже на то, что весит он полтора метра), буду использовать его в примерах.

- Отладчик. Опять же рекомендую TSearch - он содержит в себе простой и удобный отладчик, которого вполне хватит. Если у вас есть SoftICE, и вы умеете им пользоваться - то флаг в руки.

- Минимальные знания ассемблера, общее (хотя бы теоретическое) представление об отладке программ, устройстве памяти.

приступим...

1. Немного теории:

Прежде всего, что такое DMA? DMA - dynamic memory allocation, т.е. динамическое распределение памяти. Проще говоря, DMA игры, в отличие от не-DMA игр, хранят используемые ими величины (нас будут интересовать деньги, жизни и т.п.) по адресам в памяти, которые меняются после каждого запуска/перезапуска/загрузки игры.

Все программы DOS не используют DMA, тогда как большинство игр под Win32 его использует (не используют только игры времён Win95-Win98). С не-DMA играми всё предельно просто - нужно просто найти адрес в памяти, где игра хранит интересующее нас значение, и изменить его. А вот с DMA могут сложнее - что делать, если адреса постоянно изменяются? Для того, чтобы ответить на этот вопрос, нужно понять, как сама игра находит нужный адрес. Для этого используются поинтеры (pointer - указатель, я буду их называть и так, и так). Адреса поинтеров, в отличие ото всех остальных, не изменяются. Поинтер содержит значение, которое соответствует адресу какой-то величины, используемой игрой. Не важно какой, важно, что смещение других адресов относительно адреса, на который указывает поинтер, также не меняется (*). (Если не очень понятно - уверен, что станет понятнее в практической части) Итак, трейнер для DMA игры будет сначала считывать из указателя адрес интересующего значения, а затем уже его изменять. Осталось только его найти и написать соответствующую программу. (Всего-то делов, да? smile )

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

Теперь к практике...

2. Не-DMA игры.

Как я уже говорил, тут всё предельно просто. Запускаем ArtMoney/TSearch/GameHack и т.п., ищем, затем отсеиваем, определяем адрес интересующего значения. Пишем трейнер в пункте 4.1.

3. DMA игры - поиск поинтера.

Итак, сначала нужно найти адрес интересующей величины. Теперь нужно поставить брейкпоинт на этот адрес (TSearch - в меню: AutoHack -> Enable Debugger, Enable AutoHack window, там нажимаем на кнопку добавления брейкпоинта и вводим адрес нашей величины). Мы ставим брейкпоинт на чтение/запись этого адреса, т.е. при чтении или записи этого адреса нам будет показано, какие инструкции в программе и в каком месте читали/писали из/в него (в более профессиональных отладчиках, например, SoftICE, выполнение всех программ приостановится, и вылезет окно отладчика). Далее нужно переключиться в игру и изменить величину. После того, как он изменится, в окне отладчика TSearch появится строка, например, mov [ebx+A], eax (A - некоторое смещение, может быть любым целым числом, например 4). Эта ассемблерная инструкция устанавливает значение по адресу ebx+A равным eax. Что такое ebx+A? Это и есть наш адрес, +A - смещение относительно ближайшего указателя. То есть, поинтер указывает на какой-то адрес, а через A от него находится интересующее нас значение, и это значение всегда будет смещено на A относительно адреса, на который указывает поинтер. Но мы ещё не знаем адрес поинтера (адрес у нас был в регистре ebx, но он должен быть где-то в памяти). Искать его придётся как и любое другое числовое значение. Вычтем из адреса интересующего нас значения, который мы нашли в самом начале, A, затем переведём его в десятеричную систему счисления, а затем будем искать. Возможно, мы найдём несколько адресов, содержащий такое число, но совсем не факт, что все эти адреса - адреса указателей. Для этого придётся перезагрузить игру, затем вновь найти интересующее значение, из его адреса вычесть A, перевести в десятеричную систему счисления, и отсеивать.

Слова, конечно, хорошо, но на примерах любое изучение идёт лучше.

Пример: Red Alert 2. Для наглядности буду использовать TSearch. Запускаем. Open Process - выбираем нужный процесс (game.exe), приступаем к поиску: деньги - Exact Value, 4 bytes. Меняем количество денег. Отсеиваем. В результате останется 3 значения, но только одно из них - значение собственно денег, остальные два - значения счётчика и ещё что-то. (Я знаю, что значение с самым большим адресом - то, которое нужно) У меня этот адрес получился равным 72C6DAC. Далее устанавливаем на этот адрес брейкпоинт. Изменяем значение. В окошке отладчика видим: 4E48FF:
mov [ebx+0x24C], eax. Ассемблерная инструкция mov [ebx+0x24C], eax устанавливает значение, равное eax по адресу ebx+024C (ebx - регистр процессора, содержащий адрес, квадратные скобки указывают на то, что число, содержащееся в них, - адрес, и нужно изменять значение по этому адресу), но нас интересует только ebx+0x24C. Отнимаем от 72C6DAC (адреса, по которому хранится значение денег) 24Ch - получаем 72C6B60, переводим в десятеричную систему счисления - 120351584. Теперь ищем это число в памяти. В начале я получил 37 адресов. Теперь по новой ищем адрес, по которому находятся деньги, не забыв предварительно добавить в таблицу уже найденные адреса потенциальных указателей. На этот раз это 73B29FC. Вновь отнимаем 24С, переводим, ищем. В результате у меня осталось несколько адресов. Пожалуй, можно выбрать любой. Один из них - A1E0C4, сгодится. Перезапускаем. Итак, прибавим к значению, хранящемуся по адресу A1E0C4, 24С. Теперь переводим результат сложения в шестнадцатеричную систему счисления и смотрим на значение по этому адресу. Если мы видим там количество наших денег - значит всё удалось, поздравляю smile (Если нет - придётся повторить всё с начала)

Итак, поинтер мы нашли. Пожалуй, это был самый сложный этап.

4. Пишем трейнер.

Для записи и чтения в памяти мы будем использовать две WinAPI функции - ReadProcessMemory и WriteProcessMemory. (Всё предельно просто)

4.1. Трейнер для не-DMA игры.

К примеру, возьмём старую DOS-овскую игрушку Raptor: Call of The Shadows.

var
Form1: TForm1;
WindowName: integer; // Для удобства объявим как глобальные переменные.
ProcessId: integer; // Все эти переменные нужны для того, чтобы найти
ThreadId: integer; // процесс с игрой.
HandleWindow: Integer; //
write: cardinal; // В эту переменную попадёт количество записанных байтов.
buf: dword; // Тут будет содержаться значение, на которое будем изменять.
const
WindowTitle = 'RAP'; // Заголовок окна с игрой
Address = $83C4BF64; // Адрес, по которому будем изменять значение.
NumberOfBytes = 4; // Количество байт, которые будем заменять.

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
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:=$DEAD; // smile DEADh = 57005d
WriteProcessMemory(HandleWindow, ptr(address), @buf, 4, write); // Изменяем значение по этому адресу на наше.
end;

4.2. Пишем трейнер для DMA-игры.

Тут уже будем читать из поинтера адрес, по которому будем далее менять значение. Возьмём в качестве примера, скажем, SimCity 4.

var
Form1: TForm1;
WindowName : integer;
ProcessId : integer;
ThreadId : integer;
HandleWindow : Integer;
b:dword; // Всё по-прежнему, кроме этой переменной - сюда прочитаем адрес из поинтера.
readwrite:cardinal;
buf : dword;
Const WindowTitle = 'SimCity 4';
Address = $B321E4; // Это адрес поинтера.
NumberOfBytes = 4;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
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);

ReadProcessMemory(HandleWindow,ptr(address),@b,4,readwrite); // Прочитали в b значение из адреса поинтера.
b:=b+40; // Смещение адреса денег относительно адреса, на который указывает поинтер равно 40. Прибавляем.
buf:=$FFFFFFFF; // Денег должно быть много smile (**)
WriteProcessMemory(HandleWindow, ptr(b), @buf, 4, readwrite); // Наконец, запишем по адресу,
// содержащемуся в b, новое значение денег.
end;

Для того, чтобы значение "заморозить" нужно выложить на форму таймер или использовать бесконечный цикл (думаю, это итак понятно smile ), но в этом случае будет очень полезно проверять значение по адресу в b, т.к. всё в том же SimCity 4 если выйти со включённым таймером на экран выбора города - игра вывалится.

Также хорошей идеей является использование горячих клавиш. В тему борьбы с DMA это не входит, так что всё это на вкус читателя.

4.3. Общие замечания

Хочется ещё обратить внимание на то, сколько мы байт читаем и записываем (константа NumberOfBytes). Есличитаем из поинтера - то читать нужно 4 байта (т.е. dword - переменная, в которую читаем, должна уместить в себя это значение; все адреса - 32-разрядные). Если пишем значение, размер которого 1 байт - то соответственно и писать надо 1 байт smile Иначе, опять же, чревато аварийным завершением игры.

Напоминаю, что:
byte это 1 байт (это 8 бит wink ) - число от 0 до 255.
word это 2 байта - число от 0 до 65535.
dword это 4 байта - число от 0 до 4294967295.

Приводить описание всех использованных WinAPI функций я счёл ненужным, так что если интересно - смотри MSDN сам.

5. Заключение.

Ну вот и всё. Трейнер написан и работает (по крайней мере, надеюсь на это), остаётся лишь пожелать всем удачи в этом непростом деле smile

Удачи, br0k3n_MinD.

6. Некоторые оговорки

* - я пишу, что смещение адресов относительно поинтера не меняется. Это так. Но иногда его сложнее найти - встречаются конструкции типа mov [ebx+ecx], eax (правда, встречается значительно реже - это всякие полоски с жизнью и другие неявные величины). Тут хорошо бы вооружиться отладчиком посерьёзнее (SoftICE) и либо смотреть, что откуда попадает в регистры (быть указаны явно), либо ставить брейкпоинт на адрес, когда брейкпоинт сработает - смотреть содержимое регистров и искать их в памяти по-отдельности, точно так же, как мы это делали в случае с одним регистром. Но так или иначе, раз игра находит адрес - то можем найти и мы. (Это здесь написано для того, чтобы не возникало сомнений относительно неизменности адреса поинтера 8) )

** - я смело заменяю значение на FFFFFFFFh. Это прокатит для SimCity 4, но в общем случае также чревато последствиями. (Значение может быть и 4 байта, но такая цифра может не влезть в строку, вылезти за пределы экрана или ещё как-нибудь заглючить ;), в общем, лучше не жадничать)


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

 
ДефиалтусДата: Воскресенье, 17.02.2008, 09:10 | Сообщение # 5
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Пишем трейнер на Visual Basic

До сих пор в журнале мы учили вас ломать игры и изготавливать для них трейнеры с помощью таких программ, как MTC и ему подобных. Теперь же рассмотрим более продвинутый вариант - попробуем научиться программировать трейнеры своими силами. Зачем? А затем, что трейнеры, написанные самим на любом языке программирования, имеют намного более широкие возможности. Во-первых, поскольку вы сами пишете код, то, соответственно, знаете, где там что, и получаете возможность настраивать трейнер так, как вам нужно, учитывая любые мелочи. Во-вторых, такие трейнеры занимают небольшой объем памяти. Ну а в-третьих, намного приятней юзать собственную прогу, а также показывать и дарить ее своим знакомым. Но, разумеется, надо хотя бы немного знать какой-нибудь язык программирования и разбираться в шестнадцатеричных кодах. Неважно, на каком языке писать трейнер, общий принцип будет примерно тем же. Здесь мы рассмотрим написание трейнеров на Visual Basic, так как этот язык - один из наиболее простых и его легче осваивать начинающим. Если же вы хорошо разбираетесь в программировании, то можете использовать любой другой язык. Все, что нужно, это Visual Basic, желательно не ниже 5-й версии, и программа для поиска адресов памяти. В качестве последней вполне можно использовать тот же самый Magic Trainer Creator (MTC). Также можно воспользоваться теми адресами, которые мы публикуем в "шестнадцатеричном" разделе "КОДекса".

Windows API - функции:

Любое Windows-приложение использует так называемые стандартные API-функции. Вначале поговорим о тех функциях, которые понадобятся при написании простейшего трейнера. О том, куда это все писать и как, будет рассказано позже.

FindWindow (ClassName, "заголовок окна") - с помощью этой функции программа ищет окно по его заголовку. В скобках указывается класс окна и, через запятую, в кавычках его заголовок. В Visual Basic любую функцию надо "декларировать", т.е. как бы описать машине, что это функция из себя представляет. Выглядит это так: Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long. Я не буду подробно пояснять смысл этих слов, так как в этой строке менять вам все равно ничего не придется, какую бы игру вы ни взяли.

GetWindowThreadProcessId (WindowHandle, ProcessId) - перехватывает управление из FindWindow и возвращает идентификатор процесса (ProcessId), который нужен для управления этим процессом. Это описывается так: Declare Function GetWIndowProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long

OpenProcess (DesiredAccess, Inherit, ProcessId) - эта функция возвратит управление игре. Потом это можно использовать для записи и чтения данных. DesiretAccess определяет права доступа к данным игры. Здесь мы укажем полный доступ: PROCESS_ALL_ACCESS. Inherit всегда должен иметь значение False. ProcessId устанавливается такой же как в функции, описанной в пункте 2. Описывается все это так: Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long

CloseHandle (ProcessHandle) - закрывает все открытые программой процессы. Описывается так: DeclareFunction CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

WriteProcessMemory (ProcessHandle, Address, Value, SizeofValue, BytesWritten) - записывает значение в адрес игры. Декларируется так: Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
ReadProcessMemory (ProcessHandle, Address, Value, SizeofValue, BytesWritten) - читает значение из адреса игры. Описывается: Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long

Это пока что все функции Windows, которые понадобятся. Вы можете использовать еще и другие, если знаете их и если это вам понадобится для расширения возможностей. Прочитать подробней про них можно в хелпах и в соответствующей литературе.
Если вы ничего не поняли из сказанного выше, а под рукой не нащупывается книги по Visual Basic, то пока что продолжайте читать дальше - по ходу разберетесь.

Простейший трейнер

Шаг первый - поиск адреса:

Вначале ищем адрес. Для примера возьмем игру SimCity 3000 Holiday Theme Edition. Вы можете искать адрес любой удобной для вас программой, но здесь я объясню, как искать, на примере Magic Trainer Creator (МТС). Запустим игру, затем MTC. В поле Process ID выбираем запускаемый файл игры (Sc3.exe). Выбираем режим поиска Normal, в поле Value to search вводим текущую сумму денег. Нажимаем Start. По завершении поиска (нужный адрес, скорее всего, не будет найден с первого раза) возвращаемся в игру и меняем количество денег (строим новое недорогое здание, чтобы потратить немного). Затем в MTC ставим в поле Value to search новое количество денег и нажимаем Continue. Проделываем все это несколько раз, пока не найдем один адрес. У меня этот адрес был 235B218, но у вас может быть и другой. Адрес надо будет указывать в программе сразу после знаков &H без пробелов - это указывает программе, что мы используем шестнадцатеричные значения. Выглядеть будет так: &H235B218. Теперь на всякий случай проверим, правильно ли найден адрес. Щелкаем по нему, чтобы добавить в нижнее поле, затем щелкаем по нему там. В поле Monitor нажимаем кнопку в левом верхнем углу и в появившемся окне меняем первые 2 бита на FF FF. Нажимаем кнопку в нижнем правом углу окна для возврата в основное окно MTC. В поле Monitor нажимаем среднюю нижнюю кнопку. Возвращаемся в игру и смотрим, изменилось ли количество денег. Если да, тогда выписываем найденный адрес на бумагу - он нам пригодится при создании трейнера. Конечно, здесь можно ограничиться этим и создать трейнер в MTC. Но мы-то собрались писать трейнер сами. Выходим из MTC и игры и продолжаем читать статью дальше.

Шаг второй - написание трейнера:

Запускайте Visual Basic. Начните новый проект и выберите Standard EXE. В окне Properties в поле Caption можете оставить свое название для заголовка окна вместо принятого по молчанию Form1. Добавьте1 Textbox, 1 Button и Timer. Выделите только что добавленное текстовое поле (Textbox) и сотрите в окне Properties в поле Text название text1 - текстовое поле нужно для записи желаемого значения. Выделите добавленный таймер (Timer) и в окне Properties в поле Interval поставьте 500 - это частота обновления значения в игре (заморозка).
Выделите добавленную кнопку (Button), и в окне Properties в поле Caption сможете поставить свое наименование кнопки (она нужна для записи в игру набранного нами в текстовом поле трейнера значения). Выберите в меню Project опцию Add Module, чтобы добавить новый модуль в программу. В окне Project перейдите в этот модуль и наберите указанные ниже строчки. Каждая новая строка начинается со слова Declare и должна быть набрана в одну строку (здесь некоторые строчки могут быть напечатаны с переносом):

Declare Function FindWindow Lib “user32” Alias “FindWindowA” (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function GetWindowThreadProcessId Lib “user32” (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Function OpenProcess Lib “kernel32” (ByVal dwDesiredAcess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Declare Function WriteProcessMemory Lib “kernel32” (ByVal hProcess As Long, ByVal lpBaseAdress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Declare Function ReadProcessMemory Lib “kernel32” (ByVal hProcess As Long, ByVal lpBaseAddess As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWriten As Long) As Long
Declare Function CloseHandle Lib “kernel32” (ByVal hObject As Long) As Long

Теперь перейдите в Form1, щелкнув по ней в окне Project. Вы увидите исходный экран с образом вашего будущего трейнера. Щелкните в любом месте этого окна правой кнопкой мыши и выберите пункт View Code. Напоминаю, что адрес, который я использовал здесь и в котором хранится инфа о деньгах, был у меня 235B218, но у вас может быть и другой. А заголовок окна игры в моей версии был Sim City 3000. У вас заголовок может несколько отличаться, и узнать вы его можете, переключившись из игры по Alt+Tab - заголовок написан на кнопке свернутого окна. Теперь наберите весь написанный ниже текст программы (после знака ' следуют комментарии, которые можно и не писать):

Private Sub Command1_Click() ' Объявляем некоторые необходимые моменты для кнопки.
Dim hwnd As Long ' удерживает управление, переданное функцией Find Window.
Dim pid As Long' используется для хранения идентификатора процесса.
Dim pHandle As Long' держит управление процессом.
' Ищем окно игры и, если игра не запущена, выдаем сообщение об ошибке.
hwnd = FindWindow(vbNullString, "Sim City 3000")
If (hwnd = 0) Then
MsgBox "Window not found!"
Exit Sub
End If
' Теперь можно определить идентификатор процесса.
GetWindowThreadProcessId hwnd, pid
' Используем этот идентификатор для получения управления процессом.
pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
If (pHandle = 0) Then
MsgBox "Couldn't get a process handle!"
Exit Sub
End If
' Теперь можно записать новое значение в память по нужному адресу.
WriteProcessMemory pHandle, &H235B218, "Beans", 5, 0&
' Прекращаем управлять процессом.
CloseHandle hProcess
End Sub
Private Sub ReadTimer_Timer()
' Объявляем некоторые необходимые моменты для таймера.
Dim hwnd As Long' удерживает управление, переданное функцией FindWindow.
Dim pid As Long ' удерживает идентификатор процесса.
Dim pHandle As Long ' удерживает управление процессом.
Dim str As String * 20 ' параметр текстовой строки.
' Вначале ищем окно игры.
hwnd = FindWindow(vbNullString, "Sim City 3000")
If (hwnd = 0) Then Exit Sub
' Теперь можно определить идентификатор процесса.
GetWindowThreadProcessId hwnd, pid
' Используем идентификатор для управления процессом.
pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
If (pHandle = 0) Then Exit Sub
' Теперь можно прочитать из памяти...
ReadProcessMemory pHandle, &H235B218, str, 20, 0&
' ... и показать строку в текстовом поле трейнера.
txtDisplay = str
' Прекращаем управлять процессом.
CloseHandle hProcess
End Sub

Вот вроде и все. Теперь нажмите в меню File->Make Project1.exe, чтобы создать запускаемый файл вашего трейнера. Не забудьте предварительно сохранить проект и вообще почаще сохраняйтесь в процессе написания программы, чтобы вернуться, если что, к первоначальному рабочему ее варианту. Вот теперь уже можно испытать трейнер в работе. Не забудьте, что вначале нужно запускать игру, а затем уже трейнер.
Прочие игры ломаются аналогичным образом.

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


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

 
ДефиалтусДата: Воскресенье, 17.02.2008, 09:12 | Сообщение # 6
Жнец Богов
Группа: Пользователи
Сообщений: 685
Репутация: 13
Замечания: 0%
Статус: Offline
Делаем трейнер на Visual C++

В этой статье я покажу, как сделать трейнер, используя Visual C++. На написание этой статьи меня подтолкнул тот факт, что когда я пытался найти в инете хоть какую-нибудь информацию о том, как сделать простой трейнер в VC, все, что я находил, были статьи, показывающие примеры на С/С++, а в основном на Delphi и MASM. Казалось бы что в этой ситуации можно было использовать материал статей на С/С++ если бы не одно "но" - сильная привязанность VC++ к классам, в следствии чего некоторые API функции ведут себя не так, как надо. Позже это станет понятно.

Итак приступим

Вот что нам понадобится:

Сама игра. Я выбрал интересную RPG - Avernum 3 (берем отсюда )

Программа типа GameHack или TSearch для поиска нужных нам значений в памяти. Я предпочитаю TSearch, поскольку, на мой взгляд, она наиболее функциональна из всех подобных программ. Хотя дело выбора каждого.

Visual C++. У меня стоит версия 6.0, поэтому туториал будет сделан на ней. Версия не особо принципиальна, как ты понимаешь smile

Разведка:

Для начала соберем необходимую нам информацию, а именно найдем адреса в которых лежат нужные нам значения. Думаю с этим проблем у Тебя не будет, поэтому приведу уже готовые данные:

HP первого character'a 0x00C1C050 2 байта
MP первого character'a 0X00C1C054 4 байта

HP второго character'a 0x00C1C420 2 байта
MP второго character'a 0x00C1C424 4 байта

HP третьего character'a 0x00C1C7F0 2 байта
MP третьего character'a 0x00C1C7F4 4 байта

HP четвертого character'a 0x00C1CBC0 2 байта
MP четвертого character'a 0x00C1CBC4 4 байта

К нашему счастью игрушка не является DMA, то есть адреса наших переменных останутся неизменными все время. Конечно можно было найти ещё кучу полезных адресов, таких как, к примеру, опыт каждого из персонажей или их характеристики, но это уже дополнения, которые можно будет сделать потом. Сейчас главное понять принцип, а уж наворачивать будем позже.

Кодинг:

Приступим к самому главному и творческому процессу - написанию нашего трейнера. Делать будем, конечно, используя API функции, поскольку так будет проще, да и прога будет работать намного быстрее. Вот необходимые нам функции (посмотри их описания в Win32 API reference):

SetTimer
FindWindow
GetThreadProcessId
OpenProcess
WriteProcessMemory
GetAsyncKeyState
KillTimer
CloseHandle

Тепeрь создадим новый проект для будующего трейнера. Запусти Visual С и дави File->New. В появившемся окне выбирай тип MFC AppWizard (exe), набери имя проекта - Avernum3tr. Дави Next. Выбирай Dialog based. Next. Тут убери галочки с Active X и About box - они нам нафиг не нужны. Все, дави Finish и получай каркас. Как видишь AppWizard уже сгенерировал диалоговоё окно для нас, как и много кода, инициализирующего много всякой ерунды. Можешь сразу удалить static text и две кнопки с окна - они тоже лишние.

Разберемся с таймером и его функцией-обработчиком. Чтобы создать таймер, нажми Ctrl+W. Откроется окно MFC ClassWizard - эдакий редактор классов. Выбери следующее на вкладке Message Maps:

Class name - CAvernum3trDlg
Object ID's - CAvernum3trDlg
Member functions - OnIntiDialog

и нажми Edit code. Поскольку эта функция уже создана, попадем на кусок кода этой функции, которая вызывается когда создается наше окно. Там мы и инициализируем наш таймер. Добавляй перед строчкой return TRUE строку:

SetTimer(1, 100, NULL);

Тем самым, при запуске нашего трейнера, будет создаваться таймер , который будет посылать нашему окну сообщение WM_TIMER примерно 4 раза в секунду (второй параметр). Но раз мы создали таймер при запуске, надо его уничтожить при выходе из трейнера. Дави опять Ctrl+W и добавляй функцию WM_DESTROY (думаю понятно как) и жми Edit code. Пиши там:

KillTimer(1);

OK, перейдем к обработчику таймера. Ctrl+W и добавляй функцию WM_TIMER и перейди на её код. Вот. Эта функция будет вызываться каждый раз, когда сработает таймер. Как ты уже понял, тут мы будем писать код, который будет отвечать за отлов нажатия нужных нам клавиш и делать то, для чего, собственно, создается трейнер smile

Лови приколы:

Вот мы и подобрались к тому моменту, почему , собственно, многие статьи по написанию трейнера на С/С++ не срабатывали в VC. Попробуй в теле функции ON_WM_TIMER написать функцию FindWindow( и увидишь всплывшую подсказку о параметрах функции:

CWnd *FindWindow(LPCTSTR lpszClassName, LPCTSTR plszWindowName);

и сравни ее с тем, что нам нужно:

HWND FindWindow(LPCTSTR lpszClassName, LPCTSTR plszWindowName);

Дело в том, что первый вариант является функцией-членом класса нашего окна (я знаю, чтo это звучит запутанно, но тем не менее) и поэтому возвращает несколько другой тип. Что же делать??? А сделаем мы вот что - создадим свой собственный независимый универсальный класс, который мы будем использовать не только в этой проге. Если у нас будет такой класс, то в следующий раз, когда Ты будешь писать свой следующий трейнер, ты просто добавишь этот класс к проекту, впишешь в тело функции таймера всего пару строчек (позже поймешь почему) и все - готово. Это, друг мой, классы - просто супер возможность ООП. Да, конечно, это займет некоторое время сейчас, но здорово сэкономит его позже. Не веришь - смотри.

Пальцы веером - лепим класс:

Справа, в окне выбери вкладку ClassView и кликни на Avernum3tr classes правой кнопкой. Выбери New Class. В появившемся окне выбирай:

Class type Generic Class
Name CProcess

и дави ОК и увидишь, что он появился в списке классов нашего проекта. Если Ты нажмешь на значок "+" рядом с названием нашего нового класса, то увидишь там две функции-члена - конструктор и деструктор класса.
Теперь я покажу как добавить одну функцию, а остальные Ты уже добавишь сам. Жми правой кнопкой на нашем классе и выбирай Add member function. В появившемся окне пиши следующее:

Function Type HANDLE
Function Declarations cOpenProcess(char *p_ClassName, char *p_WindowName)
Access Public

а в тело вбей:

HWND hWindow;
DWORD pid;

hWindow = FindWindow(p_ClassName, p_WindowName);
if(hWindow)
{
GetWindowThreadProcessId(hWindow, &pid);
return ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
}
return NULL;

У тебя должно получится следующее:

HANDLE CProcess::cOpenProcess(char *p_ClassName, char *p_WindowName)
{
HWND hWindow;
DWORD pid;

hWindow = FindWindow(p_ClassName, p_WindowName);
if(hWindow)
{
GetWindowThreadProcessId(hWindow, &pid);
return ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
}
return NULL;
}

Таким же макаром пиши следующие функции:

bool CProcess::FindProcess(char *p_WindowTitle)
{
if(process==NULL)
{
process=this->cOpenProcess(NULL, p_WindowTitle);
}
if(process)
{
isrunning = TRUE;
return isrunning;
}
else
return FALSE;

}

bool CProcess::IsRunning()
{
return isrunning;
}

bool CProcess::IsKeyPressed(int key)
{
if (IsRunning()) return ((GetAsyncKeyState(key) & 1) == 1);
return FALSE;
}

bool CProcess::WriteByteToProcess(DWORD p_Adress, BYTE p_Value)
{
DWORD bytes;

if(isrunning)
{
return (WriteProcessMemory
(process, (void*)(p_Adress), (void*)(&p_Value), 1, &bytes) != 0);
}
return FALSE;
}

Ах да, чуть не забыл - добавь в класс две private переменных (правый click->Add member variable):

HANDLE pocess
bool isrunning

Почти финиш:

Открой во вкладке (справа) FileView файл Avernum3trDlg.cpp и в самом верху (после всех #include'ов напиши:

СProcess game;

Это создаст глобальную переменную типа CProcess с названием game. Так мы будем общаться с нашим классом и с игрой (через него)

Теперь вернемся с нашей функции ON_WM_TIMER. Впиши туда вот этот фрагмент - я думаю с его пониманием проблем возникнуть не должно:

bool check;

check = game.IsRunning();

if(!check)
{
game.FindProcess("Avernum 3");
}
else
{
if(game.IsKeyPressed(VK_F4))
{
game.WriteByteToProcess(0x00c1c050, 0xFF);
game.WriteByteToProcess(0x00c1c051, 0x10);
}

}

if( (!check && GetAsyncKeyState(VK_F4)) )
{
MessageBox("Zapusti igru snachala!!!", "lameru", MB_OK);
}

CDialog::OnTimer(nIDEvent);

Теперь откомпилируй проект и запусти его. Вроде проблем быть не должно - переписывал, вроде, как и у себя. Не запуская игру, нажми F4 - лови ругательства. Теперь запусти игру, создай персонажей... и в игре нажми F4 - получай бонусы здоровья. Остальные опции допиши сам - это тебе как домашнее задание.


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

 
  • Страница 1 из 1
  • 1
Поиск:


Используются технологии uCoz