Для того чтоб
программа имела маленький размер, надо
отказаться от использования юнита Forms. Из за
этого юнита программа увеличивается до
размера более 200 кбайт. Делфи был создан для
быстрого написания программы и программист
создающий программу на Делфи не терял много
времени на разработку интерфейса, а больше
занимался реализацией своей задачи. Из-за
этого программы, написанные на Делфи, имеют
очень большой размер. Делфи включает в
проект много нужных и ненужных классов и
функций, не используя их можно добиться
такого же размера программы как и на Си++.
Для этого надо полностью отказаться от
использования компонентов включенных в
состав Делфи, а использовать WinAPI, как это
делается в программах на Си++.
Ниже приведенный
пример показывает, как создать программу, не
используя TForms. Здесь я буду использовать
стандартный компонент TServerSocket, немного
переработанный для того, чтоб он не
использовал TForms. Я для простоты использовал
TServerSocket, из-за этого моя программа увеличилась до 89 кбайт, потому что юнит
ScktComp использует SysUtils и Classes. Но если создать
свой Сервер, используя юнит Winsock, то можно
очень здорово уменьшить свою программу.
Например, если в пример показанный ниже не
вставлять TServerSocket, то скомпилированный
размер программы, которая ничего не
выполняет 🙂 а просто сидит в памяти, около 15
килобайт. Если вставить в данную программу
функцию работы с почтой, описанную в
предыдущей статье, и функции определения
пароля, то размер такой почтовой программы
будет 30 кбайт. Вместо ТTimer нужно в методе Create
использовать tim:=SetTimer(0,0,600000,@TRojan.mail);
где
tim: integer
@TRojan.mail указатель на процедуру обработки
убивается в Destroy
KillTimer(0,tim);
Начнем. Создадим
новый проект. Удалим все стандартные юниты,
которые входят в новый проект. Сделаем так,
чтоб файл Project1.dpr выглядел так:
program Project1;
uses windows, Unit1, messages ;
var
msg: TMsg;
sock: TRojan;
begin
sock:= TRojan.Create;
while GetMessage( Msg, HInstance, 0, 0) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end.
Здесь идет зацикливание программы, и все
сообщения переданные программе передаются
в класс.
а unit1.pas чтоб
выглядел так.
unit Unit1;
interface
uses
Windows, Messages;
type
TRojan= class
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
end.
Теперь сохраним в какой-нибудь
директории наш проект. Закроем его и
откроем заново. Если откомпилировать, то
получим программу объемом 15 килобайт.
Добавим в директорию проекта файл ScktComp.pas из
папки Source Делфи. Добавим в Unit1 юнит ScktComp.
Удалим из него ссылку на Forms во втором uses.
Попробуем скомпилировать. Получили
крепкий мат :). Здесь используются некоторые
функции, находящиеся в юните Forms. При
компиляции получили неправильные
идентификаторы DeallocateHWnd, AllocateHwnd и
Application.HandleException(Self). Заремарим
Application.HandleException(Self) а процедуры DeallocateHWnd и
AllocateHwnd добавим в ScktComp.pas из юнита Forms.
Добавим объявления этих функций
TSocketErrorProc
SetErrorProc(ErrorProc: TSocketErrorProc):
implementation
удалим
var
WSAData: TWSAData;
добавим сюда это.
var
WSAData: TWSAData; // это было
InstBlockList: PInstanceBlock; // это и далее добавили
InstFreeList: PObjectInstance;
UtilWindowClass: TWndClass = (
style: 0;
lpfnWndProc: @DefWindowProc;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: nil;
lpszClassName: 'TPUtilWindow');
procedure FreeObjectInstance(ObjectInstance:
Pointer);
begin
if ObjectInstance <> nil then
begin
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList := ObjectInstance;
end;
end;
function StdWndProc(Window: HWND; Message, WParam: Longint;
LParam: Longint): Longint; stdcall; assembler;
asm
XOR EAX,EAX
PUSH EAX
PUSH LParam
PUSH WParam
PUSH Message
MOV EDX,ESP
MOV EAX,[ECX].Longint[4]
CALL [ECX].Pointer
ADD ESP,12
POP EAX
end;
function CalcJmpOffset(Src, Dest: Pointer): Longint;
begin
Result := Longint(Dest) - (Longint(Src) + 5);
end;
function MakeObjectInstance(Method: TWndMethod): Pointer;
const
BlockCode: array[1..2] of Byte = (
$59, { POP ECX }
$E9); { JMP StdWndProc }
PageSize = 4096;
var
Block: PInstanceBlock;
Instance: PObjectInstance;
begin
if InstFreeList = nil then
begin
Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Block^.Next := InstBlockList;
Move(BlockCode, Block^.Code, SizeOf(BlockCode));
Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
Instance := @Block^.Instances;
repeat
Instance^.Code := $E8; { CALL NEAR PTR Offset }
Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
Instance^.Next := InstFreeList;
InstFreeList := Instance;
Inc(Longint(Instance), SizeOf(TObjectInstance));
until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
InstBlockList := Block;
end;
Result := InstFreeList;
Instance := InstFreeList;
InstFreeList := Instance^.Next;
Instance^.Method := Method;
end;
function AllocateHWnd(Method: TWndMethod): HWND;
var
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
UtilWindowClass.hInstance := HInstance;
ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
begin
if ClassRegistered then
Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
Windows.RegisterClass(UtilWindowClass);
end;
Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
'', WS_POPUP {!0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
if Assigned(Method) then
SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));
end;
procedure DeallocateHWnd(Wnd: HWND);
var
Instance: Pointer;
begin
Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));
DestroyWindow(Wnd);
if Instance <> @DefWindowProc then FreeObjectInstance(Instance);
end;
Добавим после
const
CM_SOCKETMESSAGE = WM_USER + $0001;
CM_DEFERFREE = WM_USER + $0002;
объявление доплнительных
типов
type
TWndMethod = procedure(var Message: TMessage) of object;
PObjectInstance = ^TObjectInstance;
TObjectInstance = packed record
Code: Byte;
Offset: Integer;
case Integer of
0: (Next: PObjectInstance);
1: (Method: TWndMethod);
end;
PInstanceBlock = ^TInstanceBlock;
TInstanceBlock = packed record
Next: PInstanceBlock;
Code: array[1..2] of Byte;
WndProcPtr: Pointer;
Instances: array[0..313] of TObjectInstance;
end;
заремарим еще одну строчку Application.ShowException(FException)
🙂 и скомпилируем. Объем программы составил
57 килобайт (многовато, но на первый раз
пойдет, это не 300).
Объявим в нашем классе
процедуры Create и Destroy и компонет TServerSocket
TRojan= class
Server:TServerSocket;
constructor Create;
destructor Destroy; override;
опишем эти функции.
constructor TRojan.Create;
begin
Server:=TServerSocket.Create(nil);
Server.OnClientRead:=ClientRead;
Server.OnClientError:=ServerSocketClientError;
Server.Port:=23;
Server.Active:=true;
end;
destructor TRojan.Destroy;
begin
Server.Free;
inherited Destroy;
end;
Добавим процедуры для работы с сервером в
наш класс
procedure ClientRead(Sender: TObject; Socket:
TCustomWinSocket);
procedure ServerSocketClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
на эти процедуры сервер
будет передавать управление. Опишем
действия при возникновении ошибки
procedure TRojan.ServerSocketClientError(Sender: TObject; Socket:
TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
if errorcode= 10054 then errorcode := 0;// или можно чтоб
вообще не было никаких реакций на ошибки
// errorcode := 0;
end;
И при получении строки от клиента
procedure TRojan.ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
s:string;
begin
s:=Socket.ReceiveText;
if Uppercase(s) = 'HELP' then
begin
Socket.SendText('I`m remote server
version 1.0 designed HaWord (c)2001'#13#10);
Socket.SendText('HELP - help :)'#13#10);
Socket.SendText(#13#10);
end;
if Uppercase(clientdata) = 'STOP' then
begin
Socket.SendText('nu nu davai davai
pokeda'#13#10);
PostQuitMessage(WM_QUIT);
end;
end;
Остальное можете добавить по вкусу 🙂
Добавим приветствие. Объявим в классе
процедуру
procedure ClientConnect(Sender: TObject; Socket:
TCustomWinSocket);
Опишем обработчик
procedure TRojan.ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Socket.SendText('Привет, я программа Удаленного
администрирования'+#13#10);
end;
Ну вроде все. Размер получившейся программы,
откомпилированной на Делфи 4 разбух до 89
килобайт. Я упаковал его ASPack и он уменьшился
до 45 кбайт. Чтоб еще уменьшить размер, надо
убрать TServerSocket и описать сервер, полностью
используя WinAPI. Можно что угодно делать и на
чем угодно, только надо иметь не кривые руки
🙂
(с) 2001 HAWORD
Исходный текст
файл Project1.dpr
program Project1;
uses windows, Unit1, messages ;
var
msg: TMsg;
sock: TRojan;
begin
sock:= TRojan.Create;
while GetMessage( Msg, HInstance, 0, 0) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
sock.Destroy;
end.
файл unit1
unit Unit1;
interface
uses
Windows, Messages, scktcomp, sysutils ;
type
TRojan= class
Server:TServerSocket;
constructor Create;
destructor Destroy; override;
procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSocketClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{ TRojan }
procedure TRojan.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
Socket.SendText('Привет, я программа Удаленного
администратирования'+#13#10);
end;
procedure TRojan.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
s:string;
begin
s:=Socket.ReceiveText;
if {Uppercase(s)}s = 'HELP' then
begin
Socket.SendText('I`m remote server version
1.0 designed HaWord (c)2001'#13#10);
Socket.SendText('HELP - help :)'#13#10);
Socket.SendText(#13#10);
end;
if {Uppercase(s)}s = 'STOP' then
begin
Socket.SendText('nu nu davai davai
pokeda'#13#10);
PostQuitMessage(WM_QUIT);
end;
end;
constructor TRojan.Create;
begin
Server:=TServerSocket.Create(nil);
Server.OnClientRead:=ClientRead;
Server.OnClientError:=ServerSocketClientError;
Server.OnClientConnect:=ClientConnect;
Server.Port:=33333;
Server.Active:=true;
end;
destructor TRojan.Destroy;
begin
Server.Free;
inherited Destroy;
end;
procedure TRojan.ServerSocketClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
if errorcode= 10054 then errorcode := 0;
end;
end.