На моей основной работе мне приходится заниматься администрированием серверов и рабочих станций, работающих под управлением Windows/Linux. К серверам я привык подключаться через стандартный RDP, а для соединения с рабочими местами пользователей чаще предпочитаю использовать одну из модификаций VNC.
Статья опубликована в журнале «Хакер» №9 (129), сентябрь 2009.
Функциональность RDP и VNC меня полностью удовлетворяет, за исключением одного «но». При интенсивной работе мой рабочий стол захламляется копиями приложений TightVNC и mstsc. Бывает, работаешь с тремя серверами, а тут тебе звонит пользователь и слезно просит помочь. Хочется или нет, а приходится сворачивать окна mstsc и запускать консоль TightVNC. В результате, рабочий стол быстро превращается в хаос, состоящий из открытых окон mstsc и TightVNC. Найти в таком бардаке окно с нужным сеансом крайне проблематично.
Однажды меня все это окончательно достало, и я решил во что бы то ни стало исправить ситуацию. Что из этого получилось, ты узнаешь по ходу чтения этой статьи.
Путь смертного
Пожалуй, самым простым способом решения проблемы будет поиск и внедрение готового клиента-комбайна, поддерживающего протоколы RDP и VNC. Способ, несомненно, хорош, а главное, времени на его реализацию практически не тратится. Утилит пруд пруди, успевай только выбирать. Но, отдавая предпочтение готовому софту, ты автоматически становишься обладателем всех его плюсов и минусов. Среди основных минусов обычно выступает цена. Продвинутый софт стоит денег, а бесплатный редко обладает всеми необходимыми функциями. В свое время меня это не устроило, и я решил пойти по нетоптаной дорожке.
Путь джедая
Вооружившись сишарпом, я задумал создать клиент, способный поддерживать подключения по протоколам RDP и VNC. Решить проблему открытых окон я запланировал с помощью хорошо проверенного метода — использования вкладок. А что? Все WEB-браузеры, в том числе и хромой ослик ИА, открывают новые страницы в отдельном табе. Юзеры фичей довольны, и уже ни одного из них не заставишь от нее отказаться. Тем более, в заголовке таба можно прописывать название сервера.
RDP
Для организации поддержки протокола RDP обратимся за помощью к Com-компоненту — Microsoft RDP Client. Пользоваться компонентом чрезвычайно просто и, что немаловажно, удобно. К тому же, он установлен в каждой системе, поэтому заботиться о его поставке тебе не придется.
Чтобы воспользоваться возможностями компонента, тебе необходимо добавить его на панель выбора элементов. Делается это через окно «Выбор элементов панели инструментов → COM-компоненты». В представленном списке компонент отметь флажком Microsoft RDP Client Control. На панели выбора элементов, как и следовало ожидать, появится новый компонент.
Для демонстрации возможностей компонента я накидал простенький демонстрационный пример. Давай поглядим на методы и свойства недавно установленного компонента. Начнем наше знакомство со свойств:
- Connected — если соединение с удаленным рабочим столом установлено, то здесь будет 1
- ColorDepth — палитра цветов. Может принимать следующие значения: 8, 15, 16, 24, 32
- Server — адрес удаленного сервера. Одинаково воспринимает как символьные имена, так и IP-адрес
- UserName — имя пользователя
- AdvancedSettings2.ClearTextPassword — пароль
- AdvancedSettings2.RDPPort — номер порта
- DesktopWidth — ширина удаленного рабочего стола
- DesktopHeight — высота удаленного рабочего стола
- FullScreen — полноэкранный режим
- RedirectPrinters — переопределять принтеры. Если true, то при отправке документа на печать с удаленной машины, он будет печататься на твоем принтере
- RedirectSmartCards — переопределять смарт-карты
- RedirectPorts — переопределять порты
Со свойствами разобрались, перейдем к методам. Из всех имеющихся нас интересуют лишь два: Connect() и Disconnect(). Полагаю, пояснять, для чего они нужны, необходимости нет.
Помимо свойств и методов, как и у любого другого компонента, у MS RDP Client Control имеется целая пачка событий. Здесь рассматривать не буду, поскольку об их предназначении нетрудно догадаться по названию.
Теорией мы заправились, а раз так, самое время переходить к практике! Рассмотрим код:
Подключаемся к RDP
RdpClient.Server = textBox1.Text.Trim();
RdpClient.AdvancedSettings2.ClearTextPassword =
textBox3.Text.Trim();
RdpClient.UserName = textBox2.Text;
// RdpClient.Domain = "Домен";
// RdpClient.FullScreen = true;
switch (comboBox1.SelectedIndex)
{
case 0: RdpClient.ColorDepth = 15;
break;
case 1: RdpClient.ColorDepth = 16;
break;
case 2: RdpClient.ColorDepth = 24;
break;
case 3: RdpClient.ColorDepth = 32;
break;
}
switch (comboBox2.SelectedIndex)
{
case 0:
RdpClient.DesktopWidth = 640;
RdpClient.DesktopHeight = 480;
break;
case 1:
RdpClient.DesktopWidth = 800;
RdpClient.DesktopHeight = 600;
break;
case 2:
RdpClient.DesktopWidth = 1024;
RdpClient.DesktopHeight = 768;
break;
case 3:
RdpClient.DesktopWidth = 1120;
RdpClient.DesktopHeight = 700;
break;
case 4:
RdpClient.DesktopWidth = 1280;
RdpClient.DesktopHeight = 1024;
break;
}

Куем RDP-клиент
Нерассмотренные методы RemoteDesctop
- IsConnected — возвращает true, если соединение с удаленным компьютером установлено;
- Disconnect — выполняет отключение от удаленной машины;
- SetScalingMode (bool scaled) — если в качестве единственного параметра передаем true, то будет установлен режим масштабирования;
- FullScreenUpdate() — полноэкранный режим;
- FillServerClipboard() — перенос содержимого буфера обмена с локальной машины на удаленную.
RdpClient.Connect();
Весь процесс установки связи с удаленным хостом сводится к заполнению свойств компонента RdpClient и вызову метода Connect(). В случае, когда вся введенная инфа корректна, подключение будет установлено, а если нет — вылетит экспешн.
Замечу, что в этом коде я не делал обработку ошибок. В реальном приложении она необходима. Попробуй реализовать их самостоятельно, а если не получится, не отчаивайся. После того, как мы разберем по косточкам работу с VNC, ты сможешь сделать обработку экспешнов по аналогии.
Итак, разбор практики RDP можно считать оконченным. В принципе, уже можно переходить к рассмотрению VNC, но сначала мне бы хотелось рассказать еще об одном около-rdp’шном нюансе.
Те, кто юзает стандартный RDP-клиент, наверняка, привыкли создавать ярлыки (правильнее сказать, файлы настроек) для быстрого подключения к нужному серверу. У меня таких ярлыков штук 13. Пользоваться ими удобно — кликнул и подключился к нужному серверу. Но вот при переходе на свой собственный клиент я испытал реальные трудности — либо вбивать в свою прогу параметры подключения для всех этих 13 машин самостоятельно, либо предусмотреть возможность импорта настроек из сохраненных файлов. Первый вариант не особо привлекал, поэтому я сразу решил заняться реализацией второго. Сначала я думал, что будет много сложностей, но, как оказалось, любители C# уже проделали работу вместо меня и запечатлели свои труды в классе RDPFileReader.
Этот класс позволяет как читать файлы настроек, так и создавать новые. Воспользовавшись им, ты сделаешь свою программу универсальной. Все старые файлы настроек можно импортировать, а если необходима передача параметров соединения кому-либо из пользователей — предусмотреть экспорт. Причем производить экспорт именно в стандартный файл настроек, который без проблем поймет встроенный в Windows rdp-клиент. Все, не буду тебя томить, взгляни лучше на пример чтения произвольного файла настроек:
OpenFileDialog myOpenFileDialog;
myOpenFileDialog = new OpenFileDialog();
myOpenFileDialog.Filter = "RDP File|*.rdp";
myOpenFileDialog.Title = "Выбор файла с настройками RDP";
myOpenFileDialog.ShowDialog();
if (myOpenFileDialog.FileNames.Count() > 0) {
RDPFile MyRdpFile = new RDPFile();
MyRdpFile.Read(myOpenFileDialog.FileName);
//Обработка свойств объекта MyRdpFile
}

Разработка в самом разгаре
В этом небольшом куске кода я вызываю стандартный диалог открытия файлов. Если пользователь выбрал файл, то мне ничего не остается, кроме как создать экземпляр объекта типа RDPFile и выполнить его метод Read(). В качестве параметра методу надо передать путь к файлу, который и нужно читать. Завершив чтение, поля объекта MyRdpFile будут заполнены данными из файла. Например:
- AudioMode — режим аудио
- Domain — домен
- FullAddress — адрес сервера
- Password — пароль
- RedirectComPorts — переопределять Com-порты
- Username — имя пользователя
- DesktopHeight — высота рабочего стола
- DesktopWidth — ширина рабочего стола
- и т.д.
Х-совет
На основе полученных знаний ты без проблем сможешь написать полезные в хакерском деле утилиты — брутфорсеры. Например, совсем недавно в нашем журнале был рассказ об утилите, которая подбирает пароль к RDP. В ее основе как раз лежала технология, которую мы сегодня разобрали. Так что, мотай на ус и не расслабляйся.
VNC
Сделать поддержку VNC несколько сложнее. Протокол создан не в недрах MS, а, следовательно, весь кодинг ложится сугубо на твои плечи. Не стоит раньше времени переживать и думать, что протокол придется описывать с нуля. Уже создано немало различных клиентов (в том числе и OpenSource), в которых можно подсмотреть код и перенести его на нужный нам язык программирования.
Народные умельцы уже проработали вопрос и оформили результат в виде компонента для моего любимого C#. Все, что от нас требуется — подключить его к Visual Studio и написать несколько нехитрых строчек кода.
Итак, отправляйся на http://cdot.senecac.on.ca/projects/vncsharp и сливай последнюю версию компонента. Он доступен как в исходных текстах, так и в виде готовой библиотеки dll. Мне больше нравится работать с исходниками, поскольку при необходимости у тебя всегда будет возможность внести изменения. Мой выбор — это архив с исходником и самостоятельная компиляция.
Если ты тоже отдал предпочтению архиву с исходными текстами, то открывай сорцы в Visual Studio и приступай к компиляции. У меня она прошла без ошибок. Искать дополнительные библиотеки не пришлось. Если у тебя также все ОК, то на выходе ты должен получить библиотеку с именем VncSharp.dll.
Закрывай в студии открытые ранее сорцы компонента VncSharp и создавай новый проект типа Windows Forms Application. Сейчас мы подключим скомпилированный ранее компонент. Для этого выполни несколько простых шагов:
- Открой панель элементов и кликни на ней правой кнопкой мыши.
- В контекстном меню выбери пункт «Выбрать элементы/choose elements».
- В появившемся окне клацни по кнопке «Обзор» и выбери получившуюся в результате компиляции библиотеку.
- Кликай Ok, а затем найди на панели элементов новый компонент RemoteDesktop и кинь его на форму.

Компонент RemoteDesktop готов к использованию
Считай, что после установки компонента на форму полдела уже сделано. Остается лишь заполнить пару полей и выполнить несколько методов. Для лучшей демонстрации работы с VNC я набросал небольшой проект. Его форму ты можешь увидеть на соответствующем рисунке.

Проект для демонстрации работы с VNC
Для установки соединения с серверной частью VNC тебе необходимо выполнить метод Connect компонента RemoteDesktop. Метод описан так:
Void Connect(string host, bool ViewOnly, bool scaled);
Принимает он целых три параметра:
- host — адрес удаленного хоста. Можешь указывать здесь либо ip-адрес, либо символьное имя.
- viewOnly — режим отображения удаленного рабочего стола. Если передать в этом параметре true, то удаленный десктоп будет доступен лишь для просмотра. Все клики мышкой или нажатия клавиш на клавиатуре будут игнорироваться.
- scaled — масштабирование. Передаем true — получаем масшабированное изображение.
Код установки соединения из моего примера ты можешь увидеть на врезке.
Установка соединения с сервером VNC
string remoteHost = TextBox1.Text; int remotePort = Convert.ToInt32(TextBox2.Text); try { remoteDesktop1.VncPort = remotePort; remoteDesktop1.Connect(remoteHost, false, true); } catch (VncProtocolException vex) { MessageBox.Show(string.Format("Невозможно установить соединение: {0}", vex.Message)); }
Код второй врезки прост до безобразия и вряд ли требует детальных пояснений. Все, что в нем происходит — заполнение необходимых полей и вызов метода Connect, а также обработка эксепшн типа VncProtocolException.
При удачном раскладе в компоненте RemoteDesktop отобразится содержимое удаленного рабочего стола. Внимательно посмотрев на листинг, ты, наверное, заметил, что в коде нигде не видно передачи пароля. А ведь известно, что на подключение к серверной части может требоваться пасс. Тогда почему мы нигде не обрабатываем эту ситуацию? Дело в том, что компонент берет эту обязанность на себя. В случае если требуется ввод пассворда, он автоматически сгенерирует окно запроса пароля. Не устраивает такой подход? Не проблема! Объяви функцию, которая будет возвращать пароль (можешь даже организовать хранилище паролей в базе данных) и передавать его в свойство GetPassword объекта RemoteDesktop (смотри исходник).
Так, подключаться мы научились. Теперь рассмотрим самый последний момент работы с VNC и двинемся дальше. При администрировании удаленных систем периодически нам требуется послать сочетание нескольких клавиш. Типичный пример — <Ctrl + Alt + Del>. Если попробовать их тупо нажать, хорошего ничего не произойдет. Все, что ты увидишь — запуск на локальном компьютере таск менеджера. Исправить положение дел поможет метод SendSpecialKeys(). Его описание выглядит так:
void SendSpecialKeys(SpecialKeys keys, bool release);
Метод принимает аж два параметра:
- keys — значение из перечисления типа SpecialKeys. В качестве значений могут быть:
- Alt
- AltF4
- Ctrl
- CtrlAltDel
- CtrlEsc
- realese — освобождать клавишу или нет.
Получается, что для отправки сочетания трех клавиш от всех бед тебе требуется выполнить всего одну строчку кода:
RemoteDesctop1.SendSpecialKeys(
SpecialKeys.CtrlAltDel);
На этом знакомство с компонентом для работы с VNC можно считать оконченным. Хочу обратить внимание на то, что я рассмотрел не все методы компонента RemoteDesktop. Те, что остались за кадром — доступны во врезке.
Делаем закладки
Работать с обоими протоколами мы научились как в теории, так и на практике. Остается посмотреть на процесс создания табов для каждого удаленного рабочего стола.
Как ты уже мог догадаться, все новые табы должны создаваться во время работы приложения (RunTime). Код создания очередной вкладки будет выглядеть примерно так:
TabPage NewPage = new TabPage();
NewPage.Text = "Новая вкладка";
RemoteDesktop NewRemoteDesctop = new RemoteDesktop();
NewPage.Controls.Add(NewRemoteDesctop);
tabControl1.TabPages.Add(NewPage);
Лучше сохранять ссылку на вновь созданную вкладку в каком-нибудь контейнере. Потом будет намного удобней работать с большим количеством табов (например, выполнять поиск, сортировку и т.д.).
Работа окончена!
Для сегодняшнего примера мы выбрали C#, хотя могли писать и на Java, или на приплюснутом Си, но в этом случае так быстро создать рабочее приложение нам бы не удалось. Удачи тебе в кодинге, возникнут вопросы — пиши на мыло, постараюсь помочь!