Все начинается с
запуска Делфи Client/Server :), и вы должны иметь
навыки программирования на нем. В Винде
должен быть установлен протокол TCP/IP. Итак, я
использую Делфи 4, это все должно работать и
на Делфи 5, а на Делфи 3 не проверял. После
запуска Делфи создаем новый проект. Во
вкладке "Internet" выбираем компонент Server
Socket и кидаем его на форму. В свойстве
компонента ServerSocket1 "port", в объект-инспекторе,
установите по вкусу, но учтите, что порты
примерно до 1000 уже заняты сервисными
программами Интернета, например порт 80, это
WWW сервер, порт 21 FTP сервер, 110 и 25 для работы с
почтой и так далее, мы возьмём 33333. Во
вкладке Win32 выберем компонент RichEdit и
скидываем его на форму. В свойстве RichEdit Algin
выбираем Client. Во вкладке инспектора выберем
объект Form1 и во вкладке Events выберем событие
onCreate, происходящее при создании формы.
Впишем строчку

ServerSocket1.Active:=True;

Теперь, при
запуске приложения наш сервер будет
активизироваться на порту 33333. У нашего
компонента ServerSocket1 выберем событие ОnClientRead,
происходящее при передаче на сервер
информации. Объявим переменную а : string , и в
секции begin end; напишем:

a:=Socket.ReceiveText;
RichEdit1.Lines.Add(a);// Ведем лог сообщений
if a = 'helo' then ServerSocket1.Socket.Connections[0].SendText('Hello, i`m you
server');

Все
будет выглядеть примерно так:

procedure
TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
a:string;
begin
a:=Socket.ReceiveText;
RichEdit1.Lines.Add(a);
if a = 'helo' then Socket.SendText('Hello, i`m you server');
end;

Разберем немного эту программу.
При получении сообщения ServerSocket вызывает
событие OnClientRead, передовая переменную Socket,
имеющую тип   TCustomWinSocket. В  Socket.ReceiveText
находится текст, переданный серверу.
Выводим текст, переданный нам в окно лога,
которое представляет собой компонент RichEdit.
Далее идет проверка полученных данных, и
если полученный текст совпадает с какой-либо командой сервера, в нашем случае, пока
одна команда 'helo', тогда клиенту передается
текст 'Hello, i`m you server'. Socket.SendText('Hello, i`m you server') -
передает текст в то соединение, из которого
был получен запрос. ServerSocket может
поддерживать несколько соединений, но в
данной статье мы это не будем рассматривать.
Полный текст программы выглядит так:

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, StdCtrls, ComCtrls;

type
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
RichEdit1: TRichEdit;
procedure FormCreate(Sender: TObject);
procedure ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Active:=true;
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket:
TCustomWinSocket);
var
a:string;
begin
a:=Socket.ReceiveText;
RichEdit1.Lines.Add(a);
if a = 'helo' then Socket..SendText('Hello, i`m you server');
end;

end.

Работающая программа сервер готова, теперь
напишем универсальную программу-клиент.
Создаем новый проект и на форму выложим
компонент StatusBar, он будет служить нам
информационной строкой сообщающей,
соединен клиент с сервером или нет. Во вкладке
Standart выбираем компонент Panel и кидаем его на
на форму. Свойству Algin этого компонента
присваиваем параметр alBotton. Далее кладем на
эту Панель сначала компонент Edit, его
свойству Text присваиваем значение '127.0.0.1',
это локальный IP нашего компьютера ( для
испытания программы на нашем компе), 
затем кладем кнопочку Button, свойству Caption
присваиваем 'Connect', еще один компонент Edit, и
кнопочку с надписью 'Send', располагаем все
это друг за другом. Далее на форму опускаем
компонент RichEdit, здесь мы будем
просматривать ответ от сервера. Добавляем в
любое место формы компонент ClientSocket, и
параметр 'Port' устанавливаем равный 33333, как и
в нашем сервере. В Инспекторе объектов (Object
Inspector) выбираем вкладку Events. Щелкаем мышой
два раза на событии OnConnect. Вписываем строчку
"StatusBar1.SimpleText:='Connect'";,
выбираем событие OnDisconnect и вписываем
строчку "StatusBar1.SimpleText:='Disconnect';".
Выбираем событие OnRead и вписываем
'RichEdit1.Lines.Add(Socket.ReceiveText);'. Два раза
нажимаем на кнопке 'Connect' и в появившемся
событии onClick вписываем текст:

if not constat then
   begin
     ClientSocket1.Address:=Edit1.text;
      ClientSocket1.Active:=true;
      Button2.Caption:='Disconnect';
     constat:= not constat;
   end

else
  begin
   ClientSocket1.Active:=false;
     Button2.Caption:='Connect';
   constat:= not constat;
  end;

Переменная 'constat'
показывает нам наше состояние, соединены мы
с сервером или нет, и ее надо объявить как
глобальную переменную  ' constat:boolean = false;' в
поле var до implementation. Нажимаем два раза на
кнопку 'Send' и вписываем строчку:
"ClientSocket1.Socket.SendText(Edit2.text);". Сохраним
проект. Юнит сохраним как cliUnit1, а имя
проекту дадим Client. Итак, весь текст должен
выглядеть так:

unit cliUnit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, ComCtrls, ScktComp;

type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
RichEdit1: TRichEdit;
Panel1: TPanel;
Edit1: TEdit;
Edit2: TEdit;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
StatusBar1: TStatusBar;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
constat:boolean = false;
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
ClientSocket1.Socket.SendText(Edit2.text);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
if not constat then
    begin
      ClientSocket1.Address:=Edit1.text;
      ClientSocket1.Active:=true;
      Button2.Caption:='Disconnect';
      constat:= not constat;
   end
else
   begin
     ClientSocket1.Active:=false;
     Button2.Caption:='Connect';
     constat:= not constat;
   end;

end;

procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
begin
RichEdit1.Lines.Add(Socket.ReceiveText);
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
StatusBar1.SimpleText:='Connect';
end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket:
TCustomWinSocket);
begin
StatusBar1.SimpleText:='Disconnect';
end;

end.

Все, пока наш
клиент подходит для большинства целей.
Откомпилируем этот проект, зайдем в
директорию с нашим проектом любой
оболочкой и запустим нашу программу клиент.
Загрузим наш проект сервер. Из под Делфи
запустим на выполнение.  Итак, на экране
запущены два наших проекта - Server из Делфи,
Client как отдельная программа. В клиенте
нажимаем кнопку 'Connect', и если все сделано
правильно и на компьютере установлен
протокол TCP/IP, то в статус баре должна будет
появиться надпись 'Connect'. Вписываем в
текстовое поле возле кнопки 'Send', слово helo и
нажимаем на кнопку 'Send'. В текстовом поле
RichEdit появилась надпись  'Hello, i`m you server'. Наш
сервер нас понимает и отвечает!!. Начало
положено. Определим, какие функции должна
выполнять простенькая программа
удаленного администрирования.

1. Закачивать,
удалять и запускать программы на удаленном
компьютере.
2. Просматривать и удалять процессы
запущенные на удаленном компьютере.
3. Получать  копию экрана с удаленного
компьютера.
4. Запускать программы на сервере.
5. По возможности быть невидимой.

Для работы с
файловой системой удаленного компьютера я
предпочитаю использовать FTP соединение. На
нашей программе-сервере для этого надо
установить программу FTPServer. Для этого
понадобится свободно распространяемый
компонент для работы с Интернетом от Francois
Piette
. Установим этот компонент в Делфи. Я
выбрал более простой путь, но можно и самому
попытаться написать ФТП сервер, это
требует знаний формата передачи данных по
протоколу ФТП и очень хороший опыт работы с
сокетом, а пока при начальной разработке
программы нам этого пока не надо,
поэтому просто поставим себе этот
компонент и пойдем дальше. Выберем
компонент FTPserv из вкладки FPiette и опустим его
на нашу форму. В событиях этого компонента
выберем событие OnAuthenticate и впишем следующие
строчки:

Client.HomeDir:='C:\';
Client.Directory:='C:\';

При соединении на сервер, начальной
директорией будет главный каталог диска С:.
Теперь в свойстве OnClientRead  компонента
ServerSocket допишем еще одну строчки:

if a= 'StartFTP' then
  begin
   FTPServer1.Start;
   Socket.SendText('FTP Server started');
   end;
if a= 'StopFTP' then
begin
  FTPServer1.DisconnectAll;
  FTPServer1.Stop;
  Socket.SendText('FTP Server stoped');
end;

И запустим проект на выполнение. (Если будет
выдаваться ошибка 'неизвестный
идентификатор TFtpCtrlSocket', добавьте  в поле uses
надпись FTPSRVC ) Программа клиент должна уже
быть запущена. Нажмем кнопку 'Connect' и в поле
для передачи текста введем StartFTP. Если
сервер ответит 'FTP Server started' значит все
работает. Запускаем любой FTP клиент и
коннектимся на IP 127.0.0.1 без ввода пароля. При
соединении вам будет доступен диск С:
полностью, можете удалять, записывать и
читать файлы. Мы получили полный доступ к
диску С:. Первое задание готово. Едем дальше.

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

В стандартных
юнитах Делфи есть юнит, который содержит в
себе объявления этой функции. Для этого
добавим в uses главной формы Form1 строчку tlhelp32.
Теперь можно будет воспользоваться этой
функцией. Создадим две новые процедуры 
listproc и delproc и объявим их в секции private:

private
  procedure listproc;
  procedure delproc(numb:string);
public

Процедура delproc принимает параметр numb,
содержащий номер удаляемого процесса.
Опишем эти процедуры. Начнем с listproc. Текст
этой процедуры будет выглядеть так.

procedure TForm1.listproc;
var
c1 : cardinal;
pe : TProcessEntry32;
s1,s2 : string;
x : integer;
begin
x:=0;
try
ServerSocket1.Socket.Connections[0].SendText('Listing processes . . .'+#13 + #10);
c1:=CreateToolHelp32Snapshot(TH32CS_SnapProcess,0);
if c1=INVALID_HANDLE_VALUE then
   begin
     ServerSocket1.Socket.Connections[0].SendText('Listing
processes failed'+#13 + #10);
     exit;
   end;
try
pe.dwSize:=sizeOf(pe);
  if Process32First(c1,pe) then
   repeat
    inc(x);
    s1:=ExtractFileName(pe.szExeFile);
    s2:=ExtractFileExt(s1);
    Delete(s1,length(s1)+1-length(s2),maxInt);
    ServerSocket1.Socket.Connections[0].SendText(IntToStr(x)+' -
'+s1+' : '+pe.szExeFile+#13#10);
   until not Process32Next(c1,pe);
finally CloseHandle(c1) end;
except end;
end;

Разберем ее по порядку. В секции var мы
объявили несколько переменных, с1 - в эту
переменную мы помещаем хэндл, возвращенный функцией CreateToolHelp32Snapshot, хэндл указывает
на снимок процессов.  pe : TProcessEntry32,
структура, куда будут помещаться данные о
процессе, x - счетчик. Сначала обнуляем
счетчик x. Получаем хэндл от CreateToolHelp32Snapshot, и
если хэндл не получен сообщаем об этом
клиенту и выход и процедуры. Сообщение
выглядит так ServerSocket1.Socket.Connections[0].SendText('Listing
processes . . .'+#13 + #10); Разберем это. Как было
сказано ранее, мы не стали использовать
многопользовательское свойство сервера, мы
будем использовать только одно соединение,
поэтому мы ввели в тест строчку Connections[0],
указывающую на то, что текст надо передавать в
самое первое соединение. Если хэндл
получен,  то в структуре TProcessEntry32 в поле
dwSize укажем размер структуры  в памяти и
начнем цикл repeat для обработки найденных
процессов, увеличивая счетчик процессов.
При обработки строк, для более понятного
вида выделим имя процесса и передадим 
клиенту номер процесса, имя и полный путь к
этому процессу. После завершения цикла
закрываем хэндл. 

Функция DELPROC:

procedure TForm1.delproc(numb:string);
var
c1 : cardinal;
pe : TProcessEntry32;
s1,s2 : string;
x : integer;
begin
x:=0;
try
StrToInt(numb);
except
ServerSocket1.Socket.Connections[0].SendText('Вы ввели
неправильное число'+#13 + #10);
exit;
end;
try
ServerSocket1.Socket.Connections[0].SendText('Listing processes . . .'+#13 + #10);
c1:=CreateToolHelp32Snapshot(TH32CS_SnapProcess,0); // Получение
хэндля списка процесов
if c1=INVALID_HANDLE_VALUE then // Если по какой либо
причине не удалось получить хэндл
begin
ServerSocket1.Socket.Connections[0].SendText('Listing processes failed'+#13 + #10);
exit;
end;
try // Все нормально, хэндл получен
pe.dwSize:=sizeOf(pe);
if Process32First(c1,pe) then
repeat
inc(x); // Увеличиваем счетчик процесов
s1:=ExtractFileName(pe.szExeFile);
s2:=ExtractFileExt(s1);
Delete(s1,length(s1)+1-length(s2),maxInt); // Переменной s1
присваеваем имя процесса
if x = StrToInt(numb) then // сравнение процесса с
переданным номером для удаления
if TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS, False, pe.th32ProcessID), 1)
then
ServerSocket1.Socket.Connections[0].SendText('Killed '+s1+' : '+ pe.szExeFile +#13
+ #10)
else
ServerSocket1.Socket.Connections[0].SendText('Not killed '+s1+' : '+
pe.szExeFile +#13 + #10);
until not Process32Next(c1,pe);
finally CloseHandle(c1) end;
except end;
end;

Эта процедура почти полностью совпадает с
процедурой листинга процессов, но
добавлена проверка на дополнительный
параметр, номер процесса, который надо
удалить из системы. Текст одинаковый
поэтому остановимся только на описании
удаления процесса. Win32API функция TerminateProcess берет переданный ей хэндл
процесса и пытается удалить процесс из
памяти. Получить хэндл процесса можно
функцией OpenProcess(PROCESS_ALL_ACCESS, False, pe.th32ProcessID),
где в параметрах указываем индентификатор
процесса pe.th32ProcessID, и PROCESS_ALL_ACCESS все флаги
доступа к процессу. После вызова проверяем,
если вызов прошел успешно, то передаем
клиенту, что процесс уничтожен, иначе
передаем сообщение об ошибке.

Теперь в свойстве OnClientRead 
прописываем вызов этих процедур:

if a = 'lp' then listproc;
if copy(a,1,2) = 'dp' then delproc(copy(a,4,Length(a)-3));

С первой строчкой все
понятно, а во второй строчке функцией copy
выделяем первые две буквы, проверяем их на
строчку типа 'dp', и, если совпадает, то
передаем в процедуру delproc оставшуюся
половину строки, содержащую номер процесса
в системе. Запускаем программу на
выполнение. Переключаем задачу на
программу клиент и нажимаем кнопку connect.
Вводим строчку lp и передаем это значение
нашему серверу. В ответ мы получим
пронумерованный список процессов.
Используем этот номер для удаления
процесса. Запустим любую программу,
например, "Пасьянс косынка" .
Просмотрим список процессов. Пошлем
серверу строчу "lp". Ищем в списке
процессов процесс 19 - SOL : C:\WINDOWS\SOL.EXE, у меня
он в списке под номером девятнадцать.
Попытаемся удалить этот процесс. Пишем dp 19 и
нажимаем кнопку Send. Получили строчку "Killed
SOL : C:\WINDOWS\SOL.EXE", значит, все ОК, процесс
удален. Со второй задачей справились.

Задание 3. Получить 
копию экрана с удаленного компьютера.

В OnClientRead  прописываем
вызов:

if a = 'scr' then getscreen;

Объявляем эту процедуру 
в секции private:

procedure getscreen;

Добавляем в uses строчку JPEG,
чтоб можно было работать с файлами JPEG
формата, и ExtCtrls, чтоб можно было объявить TImage.

Теперь описываем саму
процедуру.

procedure TForm1.getscreen;
var
bmp: Graphics.TBitmap;
DC: HDC;
MyJpeg: TJpegImage;
Image1: TImage;

begin
try
bmp:=Graphics.TBitmap.Create;
bmp.Height:=Screen.Height;
bmp.Width:=Screen.Width;
DC:=GetDC(0); //Дескpиптоp экpана
bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
Image1:= TImage.Create(self);
MyJpeg:= TJpegImage.Create;
bmp.IgnorePalette:=true;
Image1.Picture.Assign(bmp);
MyJpeg.Assign(Image1.Picture.Bitmap);
MyJpeg.SaveToFile('c:\Screen.jpg');
ReleaseDC(0, DC);
ServerSocket1.Socket.Connections[0].SendText(windir+'\Screen.jpg created (use
ftp)'+#13 + #10);
Image1.free;
MyJpeg.free;
except
ServerSocket1.Socket.Connections[0].SendText('Screen bitmap not created '+#13 +
#10);
end;

end;

Распишем поподробнее.
Чтоб создать копию экрана, надо сначала
получить указатель на этот экран, и тогда
уже с ним и работать. Этим заведуют эти
функции 

DC:=GetDC(0); //Дескpиптоp
экpана
bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
// копирование экрана в TBitmap

Далее идет простенькая
программка
преобразования файла формата BMP в JPG, и, если
произошла ошибка, то сообщается об этом.
Запускаем на выполнение и в клиенте вводим
строчку 'scr' и, если не возникнет каких-либо
непредвиденных ошибок, то на диске С
создастся файл Screen.jpg, содержащий копию
экрана в данный момент времени, и его можно
будет забрать с помощью FTP клиента. (запустив
предварительно перед этим сервер,
который мы уже себе поставили)

Теперь немного поговорим
о том, как можно спрятаться из панели задач (TaskBar).

В метод onCreate Form1 добавляем
две строчки

ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,GetWindowLong(Application.Handle,
GWL_EXSTYLE) or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW);

В OnClientRead  прописываем
две строчки для управления видимостью
нашего сервера:

if a = 'show' then Form1.Visible:=true;

if a = 'hide' then Form1.Visible:=false;

В Делфи в меню Project выбрать меню View Source, откроется файл dpr
вашего проекта, и после строчки Application.Initialize добавляем строчку
Application.ShowMainForm:=False;. Все, теперь при запуске
программы ее не будет видно. Чтоб увидеть,
надо будет дать команду серверу 'show', а чтоб
спрятать 'hide'.

Ну теперь надо маленькой
программе удаленного администрирование
научиться  запускать какие-нибудь
программы на сервере. Для этого
воспользуемся стандартной функцией Виндовс
ShellExecute, ее полное описание представлено в
справке по Win32, которая входит в комплект с
Делфи, мы будем использовать ее так:

ShellExecute (0, 'open', FILE, PARAM, 'c:\', 1);

где FILE запускаемый файл,
который может быть как EXE, так и например DOC,
Windows запустит его с помощью программ, на
которые зарегистрировано данное
расширение, на DOC, например, Microsoft Word. А PARAM
параметр к запускаемой программе, если это
выполняемый модуль, такой как EXE.

В OnClientRead  прописываем
строчку для запуска программ:

секции
var
filepath: string;
param: string;

после строчки begin

if copy(a,1,6) = 'start ' then
   begin
    
a:= copy(a,7,Length(a)-6); //получаем строку без start
     if pos(' ',a)> 0 then filepath:= copy(a,1,pos(' ',a)-1) // Если
передано без параметров то просто присвоим 
     else filepath:=a; //значение переменной filepath
     if filepath <> a then param:= copy(a,pos(' ',a)+1,Length(a)-pos(' ',a));
     ServerSocket1.Socket.Connections[0].SendText('Start:
'+filepath);
     ShellExecute (0, 'open', PChar(filepath), PChar(param), '', 1);
end;

Для законченного вида программы вставляем
маленький HELP и команду остановки сервера:

if a = 'stopserver' then Form1.close;
if a = 'help' then
begin
     Socket.SendText('Программа
удаленного администрирования v 1.0');
     Socket.SendText('help - этот экран
помощи');
     Socket.SendText('StartFTP - запуск FTP
сервера');
     Socket..SendText('StopFTP - остановка FTP
сервера');
     Socket..SendText('lp - показать все
процессы в системе');
     Socket..SendText('dp - удалить процесс
под номером указанным после запятой');
     Socket.SendText('scr - создать
графический файл с копией экрана');
     Socket.SendText('show - показать
программу сервер на сервере');
     Socket.SendText('hide - спрятать
программу сервер на сервере');
     Socket..SendText('stop - остановка
сервера');
end;

Ну вроде ВСЕ, программа
работает и выполняет некоторые функции,
правда, небольшие, но зато написанные
своими руками! На этом пока все, но это еще
не конец, со временем будем дорабатывать
программу!!!!

По всем вопросам и
пожеланиям обращайтесь haword@mail.ru

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии