Ваяем radminАндрей «LittleBudda» Шкрыль Хакер, номер #101, стр. 120 (ShkrylAndrei@rambler.ru, www.vr-online.ru) Написать программу для удаленного администрирования? Легко!Как же интересно наблюдать на своем мониторе за тем, что делает твой сосед по отделу, и понимать, что под его грозным видом скрывается человек с трогательной привязанностью к сайтам знакомств и любовью к фотографиям пятничной тематики! Однако речь в этой статье пойдет совсем не о вуайеризме… Зачем писать свой radmin? Не поверишь, но многие антивирусы теперь предупреждают пользователя не только о запуске (!), но даже о наличии оригинального radmin’а на жестком диске, не без оснований считая его программой как минимум подозрительной. Принцип работы этой программы может быть положен в основу сервисов-скриншутеров, призванных начальством следить за своими сотрудниками. Стоимость таких программ достигает сотен долларов. ПриступимВ этом материале мы рассмотрим создание несколько усеченного и безвозмездного аналога radmin’а. Кстати, мы испытали его в рабочих условиях и узнали много нового о трудовых буднях Никитоса. Оказывается, он, как и робот Бендер, интересуется кулинарными онлайн-передачами! Надеюсь, со временем он угостит нас своей ядовитой стряпней ;). Итак, приступим! Первым делом разработаем серверную часть. Размести на форме следующие компоненты (смотри рисунок):
Кодим серверДля начала создай обработчик для кнопки «Включить…», который будет задавать IP-адрес и номер порта для приема команд сервером. Делается это через свойство Bindings, оно является контейнером для всех сокетов, связанных с сервером (смотри врезку «Активизируем сервер»). Далее необходимо активизировать сервер посредством установки свойства Active компонента TIdTCPServer в True, при этом кнопка «Включить сервер» превращается в «Выключить сервер» и меняется картинка в TImage. Благодаря этому пользователь сможет визуально наблюдать состояние сервера. Листинг обработчика ты найдешь в исходниках программы на DVD, прилагающемуся к журналу. Также нам потребуется специальная процедура Log - она отвечает за журналирование действий, осуществляемых сервером. Объяви ее в секции public формы следующим образом: procedure TForm1.Log(S:string); Реализация ее очень проста:Memo1.Lines.Add(TimeToStr(Time)+' '+S); Иначе говоря, в ней происходит обычное добавление строки текста в TMemo. При желании процедуру можно модифицировать, записывая данные в файл или еще что-то в этом роде. Кроме того, нам понадобится процедура для получения скриншота экрана. Прежде чем писать ее, установи компонент TPNGImage, который позволяет работать с изображениями формата PNG (компонент прилагается к исходникам программы). Объяви в секции public формы процедуру GET_SCREEN. Ее реализация представлена ниже: procedure TForm1.GET_SCREEN; var Desktop: TCanvas; B: TBitmap; W, H :Integer; PNG: TPNGObject; Kursor:TPoint; TempRect:TRect; begin GetCursorPos(Kursor); W:=Screen.Width; H:=screen.Height; TempRect:=Rect(Kursor.x,Kursor.y,Kursor.x+10,Kursor.y+10); B:=TBitmap.Create; B.Width:=W; B.Height:=H; Desktop:=TCanvas.Create; try with Desktop do Handle := GetWindowDC(GetDesktopWindow); with B.Canvas do begin Brush.Color:=clGreen; CopyRect(Rect (0, 0, w, h), DeskTop, Rect(0, 0, w, h)); FillRect(TempRect); end; PNG := TPNGObject.Create; PNG.Assign(B); PNG.SaveToFile(ExtractFilePath(Application.ExeName)+'\'+'s.png'); finally DeskTop.Free; B.Free; PNG.Free; end; end; С помощью этой процедуры мы получим скриншот экрана, вместо курсора сделаем зеленый прямоугольник, для чего используем функции GetCursorPos, Rect и FillRect. Полученный графический образ мы сохраняем в файле s.png (комментарии к коду ты найдешь в исходниках программы). Итак, мы подошли к заключительному этапу создания сервера. Сделай обработчик OnExecute компонента TIdTCPServer, в котором будет происходить анализ присланных команд от клиента (на основании их будут совершаться необходимые нам действия). procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var z: string; fstream:TFileStream; X,Y:integer; K:TPoint; begin with AThread.Connection do begin //Читаем, что прислало нам клиентское приложение z := ReadLn; if SameText(Copy(z, 1, 11), 'get_screen') then begin Log('Запрошен скриншот'); GET_SCREEN; fStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+'s.png', fmOpenRead + fmShareDenyNone); //ПЕРЕДАЕМ файл клиентскому приложению OpenWriteBuffer; Log('Передаем файл s.png'); WriteStream(fStream); Log('Файл передан'); CloseWriteBuffer; FreeAndNil(fStream); end; ... //Обработка остальных команд ... AThread.Connection.Disconnect; end; end; Здесь приведен лишь фрагмент кода обработчика OnExecute, который отправляет скриншот экрана. Остальную часть ты найдешь в исходниках программы, расположенной на диске. Поговорим более детально о командах, которые может принимать наш сервер (с первой мы уже знакомы, это «get_screen». Остальные представлены на схеме, изображенной на рисунке. Во время обращения к серверу каждый клиент создает свой собственный поток. Информация о нем содержится в переменной AThread, из которой мы читаем присланную команду с помощью функции ReadLn. Далее с помощью функции SameText мы осуществляем проверку того, является ли присланная команда разрешенной. Затем создаем поток и через него передаем файл клиенту, после чего рвем соединение, так как запрошенное действие выполнено. Проектируем и пишем клиентскую частьЯ уже чувствую нетерпение, с которым ты хочешь приступить к тестированию разработанной тобой утилиты. Конечно, хочется расслабиться, откинувшись на спинку кресла (копирайт на эту фразу по-прежнему находится у инсталлятора Windows 98) и, никуда не бегая, решать (или создавать) все проблемы пользователей удаленно. Вот и отлично! Нам остался последний рывок - разработка клиентской части. Создай новое приложения, не забыв при этом сохранить старое. Размести на форме следующие компоненты :
Приступаем к работе. Для кнопки «Скриншот» создай следующий обработчик:procedure TForm1.B_GetScrClick(Sender: TObject); var s:TFileStream; Bitmap: TBitmap; PNG: TPNGObject; begin //Подключаемся к серверу connect_to_server; //Посылаем серверу команду «get_screen» IdTCPClient1.WriteLn('get_screen '); with IdTCPClient1 do begin if FileExists('C:\s.png') then DeleteFile('C:\s.png'); //Создаем поток s := TFileStream.Create('C:\s.png',fmCreate); //Пока есть соединение, читаем данные while connected do ReadStream(s,-1,true); //Уничтожаем поток FreeAndNil(s); //Отключаемся Disconnect; Image1.Picture:=nil; //Выводим полученный скриншот на экран PNG := TPNGObject.Create; Bitmap := TBitmap.Create; try PNG.LoadFromFile('C:\s.png'); Bitmap.Assign(PNG); Image1.Picture.Bitmap.Assign(Bitmap); finally PNG.Free; Bitmap.Free; end; end; //end - with end; Как видишь, тут все элементарно. С помощью собственной процедуры connect_to_server (ее реализацию ты можешь найти в исходниках) мы подключаемся к серверу. Для этого вызывается метод Connect компонента TidTCPClient и предварительно устанавливаются свойства Host и Port. Затем с помощью процедуры WriteLn мы отправляем серверу команду «get_screen» и в ответ получаем графический файл формата PNG, который сохраняем на диске C и отображаем пользователю посредством компонента TImage. Реализацию остальных функций ты найдешь в исходниках программы, а сейчас давай просто посмотрим, как она осуществлена. Если нажать на кнопку «Просмотр», ее текст поменяется на «Отключить просмотр», а кнопки «Скриншот» и «Управление» блокируются. Далее включается таймер, который через определенные промежутки времени (я установил значение 400 мс) обращается к серверу с просьбой прислать скриншот. Если пользователь нажимает на кнопку повторно, то режим просмотра отключается. Аналогично работает и режим управления, только клиент, кроме запроса прислать скриншот, отправляет еще координаты курсора мыши (сообщения «mouse_move», «mouse_x» и «mouse_y», полученные с помощью функции GetCursorPos, которые затем и устанавливаются у получателя. Если пользователь нажимает на кнопку «Сообщение 'Пора работать!'», на сервер уходит команда «message_for_you»), которая приводит к вызову функции MessageBox и выводу поверх всех открытых окон указанного текстового сообщения. Итак, программа разработана, пора испытывать ее в боевых условиях. Предупреждаю: если у тебя стоит Firewall, настрой его так, чтобы был возможен обмен сообщениями между двумя компьютерами сети по определенном порту (у меня выбран 6666). Несколько слов напоследокКонечно, эта утилита не совершенна, но за совершенство обычно просят деньги :). Все в твоих руках - ты запросто можешь сделать из нее более серьезный продукт, реализовав дополнительные функции и возможности. Во-первых, наверняка, сразу бросается в глаза проблема мерцания при обновлении картинок. Ее можно решить, несколько изменив алгоритм работы получения скриншотов: для этого посылай не сами картинки, а лишь изменения между ними. Стоит отметить тот факт, что, если разрешения экранов мониторов клиента и сервера различаются, может наблюдаться эффект отставания курсора мыши: когда на своем мониторе ты наводишь курсор на одно место, на скриншоте, присланном с удаленного компьютера, он отстает и располагается чуть выше. Решается эта проблема вводом специального коэффициента и применением его к координатам, передаваемым на сервер. Также можно реализовать функцию работы по паролю. А уж про скрытие программы в трей и придание ей невидимости я и говорить не буду - это классика жанра. А вот управление компьютером посредством ICQ - это действительно круто. Представь только! Тебе вообще не нужно вдаваться в тонкости сетевых технологий и протоколов передачи данных. Ты просто встраиваешь в свой сервер возможность приема и отправки ICQ-сообщений и управляешь компьютером удаленно из любой точки мира (благо в сети можно найти компоненты, помогающие реализовать это без особых хлопот, да и мы в одном из предыдущих выпусков писали про создание асечного бота). Активизация сервераIdTCPServer1.Bindings.Add; IdTCPServer1.Bindings.Items[0].IP:=IPadress.Text; IdTCPServer1.Bindings.Items[0].Port:=StrToInt(PortNumber.Text); IdTCPServer1.Active:=true; Полезная информацияIndy представляет собой набор классов Object Pascal для работы с сетью (в частности, с интернетом). Так, класс TIdTCPConnection предоставляет базовые функции для низкоуровневой работы с сетевыми сервисами. Одним из главных достоинств Indy является встроенный механизм многопоточности (multi-threading). Стоит отметить, что компоненты Indy входят в комплект поставки Delphi и очень просты в использовании. Например, ты можешь запросто создать FTP-сервер или почтовый клиент, при этом написав минимум кода и не вникая в технологию реализации сетевых протоколов. Несколько слов о формате PNGВ 1978 году израильские исследователи Яков Зив (Jacob Ziv) и Абрам Лемпел (Abraham Lempel) изобрели новый алгоритм компрессии данных без потерь, названный LZ78. Публикация алгоритма была открытой, любой человек мог взять и использовать его для своих целей, и в 1987 году сотрудник компании CompuServe Боб Берри (Bob Berry) разработал на основе LZW новый формат хранения изображений - Graphic Interchange Format (GIF). В декабре 1994 года было объявлено, что за использование алгоритма LZ78 нужно выплачивать лицензионные отчисления разработчикам. Таким образом, использование формата GIF стало платным. После этого была создана рабочая группа под руководством Томаса Боутела (Thomas Boutell), которая решала задачу по разработке альтернативы GIF. Новый формат получил название PNG (Portable Network Graphics). DVDНа диске ты найдешь полные исходные коды программы, описанной в статье, а также компонент TPNGImage, предназначенный для работы с графическими файлами формата PNG. |