Программирование на C++ глазами хакера

Работа с сетью с помощью объектов Visual C++


При работе с сетью можно использовать возможности, которые предоставляет среда разработки Visual C++. Объекты упрощают программирование и скрывают некоторые особенности реализации протоколов и сети.

При использовании объектов проекты будут достаточно большими, потому что уже нельзя использовать приложения Win32 Project. Проекты надо создавать с помощью мастера MFC Application. Для начала этого будет достаточно, потому что основная цель сейчас — понять процесс программирования сетевых приложений. Чуть позже я познакомлю вас с сетевыми WinAPI-функциями, и тогда мы сможем написать те же приложения, но без использования объектов, и получить приложения маленького размера.

Для работы с сетью в MFC есть очень удобный класс — CSocket. В качестве предка у него выступает CAsyncSocket. Что это означает? Объект CAsyncSocket работает с сетью асинхронно. Отправив пакет в сеть, объект не ждет подтверждения, и программа может продолжать работать дальше. Об окончании действия мы можем узнать по событиям, которые для нас уже реализованы в объекте, и достаточно только написать их обработчики.

При синхронной работе каждая отправка пакета или соединение с сервером замораживает выполнение программы до окончания выполнения действия. Таким образом, процессорное время расходуется нерационально.

Объект CSocket является потомком объекта CAsyncSocket, а значит, получает все его возможности, свойства и методы. Его работа построена на основе технологии "клиент-сервер". Это значит, что один объект может быть сервером, который принимает соединения клиентов и работает с ними. Из этого следует, что в примерах для передачи данных понадобится создавать два объекта: CServerSocet (сервер) и CClientSocket (клиент для подключения к серверу).

Объект CServerSocet схож с CClientSocket. Сервер ожидает соединения на определенном порту, и когда клиент подключился, создается объект CClientSocket, с помощью которого можно отправлять и принимать данные на сервере.

Чтобы увидеть на практике работу с сетью, давайте напишем программу, которая будет сканировать указанный компьютер и искать на нем открытые порты (сканер портов). Как это работает? Для того чтобы узнать, какие порты открыты, достаточно только попробовать подсоединиться к порту. Если соединение пройдет успешно, то данный порт открыла какая-то программа.


Теперь откройте файл ресурсов и найдите диалоговое окно IDD_MFCSCAN_DIALOG. Дважды щелкните по нему, чтобы откорректировать в редакторе ресурсов. Удалите кнопки и Cancel, а поместите на окно диалога следующие компоненты:

Static Text — с надписью "Server address";

Edit Control — для ввода адреса сканируемого сервера (по умолчанию текст " Sample edit box ";

List Box — для сохранения открытых портов;

Button (кнопка) — с надписью "Scan" для запуска сканирования портов указанного компьютера.



У вас должно получиться нечто похожее на изображенное на 4.8.

Теперь необходимо создать переменную для списка, чтобы с ним потом работать. Для этого надо щелкнуть по компоненту List Box правой кнопкой мышки и в выпадающем меню выбрать пункт Add Variable. В появившемся окне ( 4.9) нужно ввести в поле Variable name имя переменной. Укажите имя PortsList.

Все подготовительные работы закончены. Можно приступать к написанию кода сканера портов. Необходимо создать обработчик события, который будет срабатывать при нажатии пользователем кнопки Scan, и написать в нем весь необходимый код. Для этого щелкните правой кнопкой мышки по компоненту Button и выберите в появившемся меню пункт Add Event Handler. Перед вами откроется окно мастера Event Handler Wizard ( 4.10). Согласитесь со всеми установками мастера и нажмите кнопку Add and Edit.

Мастер создаст заготовку в виде пустой функции для обработчика события. В ней нужно написать код из листинга 4.5.



4.8. Окно диалога для нашего будущего приложения



4.9. Окно создания переменной



4.10. Окно Мастера создания обработчика события

Листинг 4.5. Код сканера портов
void CMFCScanDlg::OnBnClickedScanButton() { // TODO: Add your control notification handler code here CClientSocket *pSocket; CString ip; CString messtr; int port;

pSocket=new CClientSocket(); pSocket-Create();

GetDlgItemText(IDC_EDIT1,ip); port=1; while (port100) { if(pSocket-Connect(ip, port)) { messtr.Format("Port=%d opened", port); PortsList.AddString(messtr); pSocket-Close(); pSocket-Create(); } port++; } }



Теперь разберемся с тем, что здесь происходит. В данном коде объявлена переменная pSocket типа CClientSocket. С ее помощью мы будем работать с объектом, который умеет общаться с сетью по протоколу TCP/IP. Но прежде чем начать работу, нужно выделить память и создать объект. Это делается в следующих двух строчках:

pSocket=new CClientSocket();

pSocket-Create();

После этого следует узнать, какой IP-адрес указал пользователь в поле ввода. Для этого используется функция GetDlgItemText, у которой два параметра: идентификатор компонента и переменная, в которой будет сохранен результат.

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

После этого в переменную port заносится начальное значение 1, с которого начинается сканирование. Затем запускается цикл, который будет выполняться, пока переменная port не станет больше 100.

Внутри цикла производится попытка соединения с сервером следующим образом:

pSocket-Connect(ip, port)

Здесь вызывается метод Connect объекта, на который указывает переменная pSocket. У этого метода два параметра: адрес компьютера, к которому надо подключиться, и порт. Если соединение прошло удачно, то результатом будет ненулевое значение. В этом случае надо добавить информационную строку в список PortsList. Очень важно закрыть соединение и проинициализиро-вать объект заново, иначе дальнейшие попытки соединения с сервером будут бесполезны, и вы увидите только первый открытый порт. Закрытие соединения и инициализация производятся методами Close и Create соответственно:

pSocket-Close();

pSocket-Create();

В конце цикла увеличивается переменная port, чтобы на следующем этапе цикла сканировать следующий порт.

Теперь вы готовы скомпилировать программу, но чтобы все прошло удачно, нужно перейти в начало модуля, где перечислены подключаемые заголовочные файлы, и добавить следующую строку:



#include "ClientSocket.h"

Был использован объект CClientSocket, который описан в файле ClientSocket.h, поэтому без подключения модуля код не скомпилируется.

Результат работы программы вы можете увидеть на 4.11. Запустите программу и, указав в качестве адреса 127.0.0.1, просканируйте порты своего компьютера, начиная с 0 до 99. Почему сканируем так мало портов? В Windows процесс сканирования 1000 портов происходит слишком медленно (может занять около 5 минут), поэтому сканировать лучше маленькими порциями.

Чуть позже я покажу более совершенный пример по сканированию портов, а данная программа является чисто познавательной и очень хорошо подходит для понимания алгоритма сканирования. Если у вас большой опыт программирования в среде Visual C++ и вы знакомы с потоками, то я все равно не советую вам создавать множество потоков, чтобы каждый из них сканировал свой порт. Таким способом вы ускорите программу, но во время сканирования система будет нагружена и, причем, бесполезно. Потерпите немного, и вы познакомитесь с реально быстрым сканером портов.

Примечание
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter4\Scan.

Содержание раздела